<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://framework.zend.com/license/new-bsd * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to [email protected] so we can send you a copy immediately. * * @category Zend * @package Zend_Wildfire * @subpackage Channel * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @version $Id$ */ /** Zend_Wildfire_Channel_Interface */ require_once 'Zend/Wildfire/Channel/Interface.php'; /** Zend_Controller_Request_Abstract */ require_once('Zend/Controller/Request/Abstract.php'); /** Zend_Controller_Response_Abstract */ require_once('Zend/Controller/Response/Abstract.php'); /** Zend_Controller_Plugin_Abstract */ require_once 'Zend/Controller/Plugin/Abstract.php'; /** Zend_Wildfire_Protocol_JsonStream */ require_once 'Zend/Wildfire/Protocol/JsonStream.php'; /** Zend_Controller_Front **/ require_once 'Zend/Controller/Front.php'; /** * Implements communication via HTTP request and response headers for Wildfire Protocols. * * @category Zend * @package Zend_Wildfire * @subpackage Channel * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Wildfire_Channel_HttpHeaders extends Zend_Controller_Plugin_Abstract implements Zend_Wildfire_Channel_Interface { /** * The string to be used to prefix the headers. * @var string */ protected static $_headerPrefix = 'X-WF-'; /** * Singleton instance * @var Zend_Wildfire_Channel_HttpHeaders */ protected static $_instance = null; /** * The index of the plugin in the controller dispatch loop plugin stack * @var integer */ protected static $_controllerPluginStackIndex = 999; /** * The protocol instances for this channel * @var array */ protected $_protocols = null; /** * Initialize singleton instance. * * @param string $class OPTIONAL Subclass of Zend_Wildfire_Channel_HttpHeaders * @return Zend_Wildfire_Channel_HttpHeaders Returns the singleton Zend_Wildfire_Channel_HttpHeaders instance * @throws Zend_Wildfire_Exception */ public static function init($class = null) { if (self::$_instance !== null) { require_once 'Zend/Wildfire/Exception.php'; throw new Zend_Wildfire_Exception('Singleton instance of Zend_Wildfire_Channel_HttpHeaders already exists!'); } if ($class !== null) { if (!is_string($class)) { require_once 'Zend/Wildfire/Exception.php'; throw new Zend_Wildfire_Exception('Third argument is not a class string'); } if (!class_exists($class)) { require_once 'Zend/Loader.php'; Zend_Loader::loadClass($class); } self::$_instance = new $class(); if (!self::$_instance instanceof Zend_Wildfire_Channel_HttpHeaders) { self::$_instance = null; require_once 'Zend/Wildfire/Exception.php'; throw new Zend_Wildfire_Exception('Invalid class to third argument. Must be subclass of Zend_Wildfire_Channel_HttpHeaders.'); } } else { self::$_instance = new self(); } return self::$_instance; } /** * Get or create singleton instance * * @param bool $skipCreate True if an instance should not be created * @return Zend_Wildfire_Channel_HttpHeaders */ public static function getInstance($skipCreate=false) { if (self::$_instance===null && $skipCreate!==true) { return self::init(); } return self::$_instance; } /** * Destroys the singleton instance * * Primarily used for testing. * * @return void */ public static function destroyInstance() { self::$_instance = null; } /** * Get the instance of a give protocol for this channel * * @param string $uri The URI for the protocol * @return object Returns the protocol instance for the diven URI */ public function getProtocol($uri) { if (!isset($this->_protocols[$uri])) { $this->_protocols[$uri] = $this->_initProtocol($uri); } $this->_registerControllerPlugin(); return $this->_protocols[$uri]; } /** * Initialize a new protocol * * @param string $uri The URI for the protocol to be initialized * @return object Returns the new initialized protocol instance * @throws Zend_Wildfire_Exception */ protected function _initProtocol($uri) { switch ($uri) { case Zend_Wildfire_Protocol_JsonStream::PROTOCOL_URI; return new Zend_Wildfire_Protocol_JsonStream(); } require_once 'Zend/Wildfire/Exception.php'; throw new Zend_Wildfire_Exception('Tyring to initialize unknown protocol for URI "'.$uri.'".'); } /** * Flush all data from all protocols and send all data to response headers. * * @return boolean Returns TRUE if data was flushed */ public function flush() { if (!$this->_protocols || !$this->isReady()) { return false; } foreach ( $this->_protocols as $protocol ) { $payload = $protocol->getPayload($this); if ($payload) { foreach( $payload as $message ) { $this->getResponse()->setHeader(self::$_headerPrefix.$message[0], $message[1], true); } } } return true; } /** * Set the index of the plugin in the controller dispatch loop plugin stack * * @param integer $index The index of the plugin in the stack * @return integer The previous index. */ public static function setControllerPluginStackIndex($index) { $previous = self::$_controllerPluginStackIndex; self::$_controllerPluginStackIndex = $index; return $previous; } /** * Register this object as a controller plugin. * * @return void */ protected function _registerControllerPlugin() { $controller = Zend_Controller_Front::getInstance(); if (!$controller->hasPlugin(get_class($this))) { $controller->registerPlugin($this, self::$_controllerPluginStackIndex); } } /* * Zend_Wildfire_Channel_Interface */ /** * Determine if channel is ready. * * The channel is ready as long as the request and response objects are initialized, * can send headers and the FirePHP header exists in the User-Agent. * * If the header does not exist in the User-Agent, no appropriate client * is making this request and the messages should not be sent. * * A timing issue arises when messages are logged before the request/response * objects are initialized. In this case we do not yet know if the client * will be able to accept the messages. If we consequently indicate that * the channel is not ready, these messages will be dropped which is in * most cases not the intended behaviour. The intent is to send them at the * end of the request when the request/response objects will be available * for sure. * * If the request/response objects are not yet initialized we assume if messages are * logged, the client will be able to receive them. As soon as the request/response * objects are availoable and a message is logged this assumption is challenged. * If the client cannot accept the messages any further messages are dropped * and messages sent prior are kept but discarded when the channel is finally * flushed at the end of the request. * * When the channel is flushed the $forceCheckRequest option is used to force * a check of the request/response objects. This is the last verification to ensure * messages are only sent when the client can accept them. * * @param boolean $forceCheckRequest OPTIONAL Set to TRUE if the request must be checked * @return boolean Returns TRUE if channel is ready. */ public function isReady($forceCheckRequest=false) { if (!$forceCheckRequest && !$this->_request && !$this->_response ) { return true; } if (!($this->getRequest() instanceof Zend_Controller_Request_Http)) { return false; } return ($this->getResponse()->canSendHeaders() && (preg_match_all( '/\s?FirePHP\/([\.\d]*)\s?/si', $this->getRequest()->getHeader('User-Agent'), $m ) || (($header = $this->getRequest()->getHeader('X-FirePHP-Version')) && preg_match_all('/^([\.\d]*)$/si', $header, $m) )) ); } /* * Zend_Controller_Plugin_Abstract */ /** * Flush messages to headers as late as possible but before headers have been sent. * * @return void */ public function dispatchLoopShutdown() { $this->flush(); } /** * Get the request object * * @return Zend_Controller_Request_Abstract * @throws Zend_Wildfire_Exception */ public function getRequest() { if (!$this->_request) { $controller = Zend_Controller_Front::getInstance(); $this->setRequest($controller->getRequest()); } if (!$this->_request) { require_once 'Zend/Wildfire/Exception.php'; throw new Zend_Wildfire_Exception('Request objects not initialized.'); } return $this->_request; } /** * Get the response object * * @return Zend_Controller_Response_Abstract * @throws Zend_Wildfire_Exception */ public function getResponse() { if (!$this->_response) { $response = Zend_Controller_Front::getInstance()->getResponse(); if ($response) { $this->setResponse($response); } } if (!$this->_response) { require_once 'Zend/Wildfire/Exception.php'; throw new Zend_Wildfire_Exception('Response objects not initialized.'); } return $this->_response; } }