2
0

HttpHeaders.php 10 KB

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