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