PageRenderTime 27ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Wildfire/Channel/HttpHeaders.php

https://github.com/sidealice/zf2
PHP | 332 lines | 140 code | 34 blank | 158 comment | 24 complexity | 81a2aba4a283bab0fef83ab2435219c5 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Wildfire
  17. * @subpackage Channel
  18. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. /**
  22. * @namespace
  23. */
  24. namespace Zend\Wildfire\Channel;
  25. use Zend\Wildfire,
  26. Zend\Wildfire\Protocol,
  27. Zend\Controller,
  28. Zend\Controller\Request\Http as HttpRequest;
  29. /**
  30. * Implements communication via HTTP request and response headers for Wildfire Protocols.
  31. *
  32. * @uses \Zend\Controller\Front
  33. * @uses \Zend\Controller\Plugin\AbstractPlugin
  34. * @uses \Zend\Controller\Request\AbstractRequest
  35. * @uses \Zend\Controller\Response\AbstractResponse
  36. * @uses \Zend\Loader
  37. * @uses \Zend\Wildfire\Channel
  38. * @uses \Zend\Wildfire\Exception
  39. * @uses \Zend\Wildfire\Protocol\JsonStream
  40. * @category Zend
  41. * @package Zend_Wildfire
  42. * @subpackage Channel
  43. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  44. * @license http://framework.zend.com/license/new-bsd New BSD License
  45. */
  46. class HttpHeaders
  47. extends Controller\Plugin\AbstractPlugin
  48. implements Wildfire\Channel
  49. {
  50. /**
  51. * The string to be used to prefix the headers.
  52. * @var string
  53. */
  54. protected static $_headerPrefix = 'X-WF-';
  55. /**
  56. * Singleton instance
  57. * @var \Zend\Wildfire\Channel\HttpHeaders
  58. */
  59. protected static $_instance = null;
  60. /**
  61. * The index of the plugin in the controller dispatch loop plugin stack
  62. * @var integer
  63. */
  64. protected static $_controllerPluginStackIndex = 999;
  65. /**
  66. * The protocol instances for this channel
  67. * @var array
  68. */
  69. protected $_protocols = null;
  70. /**
  71. * Initialize singleton instance.
  72. *
  73. * @param string $class OPTIONAL Subclass of \Zend\Wildfire\Channel\HttpHeaders
  74. * @return \Zend\Wildfire\Channel\HttpHeaders Returns the singleton \Zend\Wildfire\Channel\HttpHeaders instance
  75. * @throws \Zend\Wildfire\Exception
  76. */
  77. public static function init($class = null)
  78. {
  79. if (self::$_instance !== null) {
  80. throw new Exception\RuntimeException('Singleton instance of Zend_Wildfire_Channel_HttpHeaders already exists!');
  81. }
  82. if ($class !== null) {
  83. if (!is_string($class)) {
  84. throw new Exception\InvalidArgumentException('Third argument is not a class string');
  85. }
  86. self::$_instance = new $class();
  87. if (!self::$_instance instanceof HttpHeaders) {
  88. self::$_instance = null;
  89. throw new Exception\InvalidArgumentException('Invalid class to third argument. Must be subclass of Zend_Wildfire_Channel_HttpHeaders.');
  90. }
  91. } else {
  92. self::$_instance = new self();
  93. }
  94. return self::$_instance;
  95. }
  96. /**
  97. * Get or create singleton instance
  98. *
  99. * @param $skipCreate boolean True if an instance should not be created
  100. * @return \Zend\Wildfire\Channel\HttpHeaders
  101. */
  102. public static function getInstance($skipCreate=false)
  103. {
  104. if (self::$_instance===null && $skipCreate!==true) {
  105. return self::init();
  106. }
  107. return self::$_instance;
  108. }
  109. /**
  110. * Destroys the singleton instance
  111. *
  112. * Primarily used for testing.
  113. *
  114. * @return void
  115. */
  116. public static function destroyInstance()
  117. {
  118. self::$_instance = null;
  119. }
  120. /**
  121. * Get the instance of a give protocol for this channel
  122. *
  123. * @param string $uri The URI for the protocol
  124. * @return object Returns the protocol instance for the diven URI
  125. */
  126. public function getProtocol($uri)
  127. {
  128. if (!isset($this->_protocols[$uri])) {
  129. $this->_protocols[$uri] = $this->_initProtocol($uri);
  130. }
  131. $this->_registerControllerPlugin();
  132. return $this->_protocols[$uri];
  133. }
  134. /**
  135. * Initialize a new protocol
  136. *
  137. * @param string $uri The URI for the protocol to be initialized
  138. * @return object Returns the new initialized protocol instance
  139. * @throws \Zend\Wildfire\Exception
  140. */
  141. protected function _initProtocol($uri)
  142. {
  143. switch ($uri) {
  144. case Protocol\JsonStream::PROTOCOL_URI;
  145. return new Protocol\JsonStream();
  146. }
  147. throw new Channel\InvalidArgumentException('Tyring to initialize unknown protocol for URI "'.$uri.'".');
  148. }
  149. /**
  150. * Flush all data from all protocols and send all data to response headers.
  151. *
  152. * @return boolean Returns TRUE if data was flushed
  153. */
  154. public function flush()
  155. {
  156. if (!$this->_protocols || !$this->isReady()) {
  157. return false;
  158. }
  159. foreach ( $this->_protocols as $protocol ) {
  160. $payload = $protocol->getPayload($this);
  161. if ($payload) {
  162. foreach( $payload as $message ) {
  163. $this->getResponse()->setHeader(self::$_headerPrefix.$message[0],
  164. $message[1], true);
  165. }
  166. }
  167. }
  168. return true;
  169. }
  170. /**
  171. * Set the index of the plugin in the controller dispatch loop plugin stack
  172. *
  173. * @param integer $index The index of the plugin in the stack
  174. * @return integer The previous index.
  175. */
  176. public static function setControllerPluginStackIndex($index)
  177. {
  178. $previous = self::$_controllerPluginStackIndex;
  179. self::$_controllerPluginStackIndex = $index;
  180. return $previous;
  181. }
  182. /**
  183. * Register this object as a controller plugin.
  184. *
  185. * @return void
  186. */
  187. protected function _registerControllerPlugin()
  188. {
  189. $controller = Controller\Front::getInstance();
  190. if (!$controller->hasPlugin(get_class($this))) {
  191. $controller->registerPlugin($this, self::$_controllerPluginStackIndex);
  192. }
  193. }
  194. /*
  195. * Zend_Wildfire_Channel_Interface
  196. */
  197. /**
  198. * Determine if channel is ready.
  199. *
  200. * The channel is ready as long as the request and response objects are initialized,
  201. * can send headers and the FirePHP header exists in the User-Agent.
  202. *
  203. * If the header does not exist in the User-Agent, no appropriate client
  204. * is making this request and the messages should not be sent.
  205. *
  206. * A timing issue arises when messages are logged before the request/response
  207. * objects are initialized. In this case we do not yet know if the client
  208. * will be able to accept the messages. If we consequently indicate that
  209. * the channel is not ready, these messages will be dropped which is in
  210. * most cases not the intended behaviour. The intent is to send them at the
  211. * end of the request when the request/response objects will be available
  212. * for sure.
  213. *
  214. * If the request/response objects are not yet initialized we assume if messages are
  215. * logged, the client will be able to receive them. As soon as the request/response
  216. * objects are availoable and a message is logged this assumption is challenged.
  217. * If the client cannot accept the messages any further messages are dropped
  218. * and messages sent prior are kept but discarded when the channel is finally
  219. * flushed at the end of the request.
  220. *
  221. * When the channel is flushed the $forceCheckRequest option is used to force
  222. * a check of the request/response objects. This is the last verification to ensure
  223. * messages are only sent when the client can accept them.
  224. *
  225. * @param boolean $forceCheckRequest OPTIONAL Set to TRUE if the request must be checked
  226. * @return boolean Returns TRUE if channel is ready.
  227. */
  228. public function isReady($forceCheckRequest=false)
  229. {
  230. if (!$forceCheckRequest
  231. && !$this->_request
  232. && !$this->_response
  233. ) {
  234. return true;
  235. }
  236. if (!($this->getRequest() instanceof HttpRequest)) {
  237. return false;
  238. }
  239. return ($this->getResponse()->canSendHeaders()
  240. && (preg_match_all(
  241. '/\s?FirePHP\/([\.\d]*)\s?/si',
  242. $this->getRequest()->getHeader('User-Agent'),
  243. $m
  244. ) ||
  245. (($header = $this->getRequest()->getHeader('X-FirePHP-Version'))
  246. && preg_match_all('/^([\.\d]*)$/si', $header, $m)
  247. ))
  248. );
  249. }
  250. /*
  251. * Zend_Controller_Plugin_Abstract
  252. */
  253. /**
  254. * Flush messages to headers as late as possible but before headers have been sent.
  255. *
  256. * @return void
  257. */
  258. public function dispatchLoopShutdown()
  259. {
  260. $this->flush();
  261. }
  262. /**
  263. * Get the request object
  264. *
  265. * @return \Zend\Controller\Request\AbstractRequest
  266. * @throws \Zend\Wildfire\Exception
  267. */
  268. public function getRequest()
  269. {
  270. if (!$this->_request) {
  271. $controller = Controller\Front::getInstance();
  272. $this->setRequest($controller->getRequest());
  273. }
  274. if (!$this->_request) {
  275. throw new Channel\RuntimeException('Request objects not initialized.');
  276. }
  277. return $this->_request;
  278. }
  279. /**
  280. * Get the response object
  281. *
  282. * @return \Zend\Controller\Response\AbstractResponse
  283. * @throws \Zend\Wildfire\Exception
  284. */
  285. public function getResponse()
  286. {
  287. if (!$this->_response) {
  288. $response = Controller\Front::getInstance()->getResponse();
  289. if ($response) {
  290. $this->setResponse($response);
  291. }
  292. }
  293. if (!$this->_response) {
  294. throw new Channel\RuntimeException('Response objects not initialized.');
  295. }
  296. return $this->_response;
  297. }
  298. }