web/lib/Zend/Wildfire/Channel/HttpHeaders.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     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 }