HttpHeaders.php 11 KB

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