File indexing completed on 2025-01-19 05:20:57

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Cache
0017  * @subpackage Zend_Cache_Frontend
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 
0024 /**
0025  * @see Zend_Cache_Core
0026  */
0027 // require_once 'Zend/Cache/Core.php';
0028 
0029 
0030 /**
0031  * @package    Zend_Cache
0032  * @subpackage Zend_Cache_Frontend
0033  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0034  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0035  */
0036 class Zend_Cache_Frontend_Page extends Zend_Cache_Core
0037 {
0038     /**
0039      * This frontend specific options
0040      *
0041      * ====> (boolean) http_conditional :
0042      * - if true, http conditional mode is on
0043      * WARNING : http_conditional OPTION IS NOT IMPLEMENTED FOR THE MOMENT (TODO)
0044      *
0045      * ====> (boolean) debug_header :
0046      * - if true, a debug text is added before each cached pages
0047      *
0048      * ====> (boolean) content_type_memorization :
0049      * - deprecated => use memorize_headers instead
0050      * - if the Content-Type header is sent after the cache was started, the
0051      *   corresponding value can be memorized and replayed when the cache is hit
0052      *   (if false (default), the frontend doesn't take care of Content-Type header)
0053      *
0054      * ====> (array) memorize_headers :
0055      * - an array of strings corresponding to some HTTP headers name. Listed headers
0056      *   will be stored with cache datas and "replayed" when the cache is hit
0057      *
0058      * ====> (array) default_options :
0059      * - an associative array of default options :
0060      *     - (boolean) cache : cache is on by default if true
0061      *     - (boolean) cacheWithXXXVariables  (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') :
0062      *       if true,  cache is still on even if there are some variables in this superglobal array
0063      *       if false, cache is off if there are some variables in this superglobal array
0064      *     - (boolean) makeIdWithXXXVariables (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') :
0065      *       if true, we have to use the content of this superglobal array to make a cache id
0066      *       if false, the cache id won't be dependent of the content of this superglobal array
0067      *     - (int) specific_lifetime : cache specific lifetime
0068      *                                (false => global lifetime is used, null => infinite lifetime,
0069      *                                 integer => this lifetime is used), this "lifetime" is probably only
0070      *                                usefull when used with "regexps" array
0071      *     - (array) tags : array of tags (strings)
0072      *     - (int) priority : integer between 0 (very low priority) and 10 (maximum priority) used by
0073      *                        some particular backends
0074      *
0075      * ====> (array) regexps :
0076      * - an associative array to set options only for some REQUEST_URI
0077      * - keys are (pcre) regexps
0078      * - values are associative array with specific options to set if the regexp matchs on $_SERVER['REQUEST_URI']
0079      *   (see default_options for the list of available options)
0080      * - if several regexps match the $_SERVER['REQUEST_URI'], only the last one will be used
0081      *
0082      * @var array options
0083      */
0084     protected $_specificOptions = array(
0085         'http_conditional' => false,
0086         'debug_header' => false,
0087         'content_type_memorization' => false,
0088         'memorize_headers' => array(),
0089         'default_options' => array(
0090             'cache_with_get_variables' => false,
0091             'cache_with_post_variables' => false,
0092             'cache_with_session_variables' => false,
0093             'cache_with_files_variables' => false,
0094             'cache_with_cookie_variables' => false,
0095             'make_id_with_get_variables' => true,
0096             'make_id_with_post_variables' => true,
0097             'make_id_with_session_variables' => true,
0098             'make_id_with_files_variables' => true,
0099             'make_id_with_cookie_variables' => true,
0100             'cache' => true,
0101             'specific_lifetime' => false,
0102             'tags' => array(),
0103             'priority' => null
0104         ),
0105         'regexps' => array()
0106     );
0107 
0108     /**
0109      * Internal array to store some options
0110      *
0111      * @var array associative array of options
0112      */
0113     protected $_activeOptions = array();
0114 
0115     /**
0116      * If true, the page won't be cached
0117      *
0118      * @var boolean
0119      */
0120     protected $_cancel = false;
0121 
0122     /**
0123      * Constructor
0124      *
0125      * @param  array   $options                Associative array of options
0126      * @param  boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
0127      * @throws Zend_Cache_Exception
0128      * @return void
0129      */
0130     public function __construct(array $options = array())
0131     {
0132         foreach ($options as $name => $value) {
0133             $name = strtolower($name);
0134             switch ($name) {
0135                 case 'regexps':
0136                     $this->_setRegexps($value);
0137                     break;
0138                 case 'default_options':
0139                     $this->_setDefaultOptions($value);
0140                     break;
0141                 case 'content_type_memorization':
0142                     $this->_setContentTypeMemorization($value);
0143                     break;
0144                 default:
0145                     $this->setOption($name, $value);
0146             }
0147         }
0148         if (isset($this->_specificOptions['http_conditional'])) {
0149             if ($this->_specificOptions['http_conditional']) {
0150                 Zend_Cache::throwException('http_conditional is not implemented for the moment !');
0151             }
0152         }
0153         $this->setOption('automatic_serialization', true);
0154     }
0155 
0156     /**
0157      * Specific setter for the 'default_options' option (with some additional tests)
0158      *
0159      * @param  array $options Associative array
0160      * @throws Zend_Cache_Exception
0161      * @return void
0162      */
0163     protected function _setDefaultOptions($options)
0164     {
0165         if (!is_array($options)) {
0166             Zend_Cache::throwException('default_options must be an array !');
0167         }
0168         foreach ($options as $key=>$value) {
0169             if (!is_string($key)) {
0170                 Zend_Cache::throwException("invalid option [$key] !");
0171             }
0172             $key = strtolower($key);
0173             if (isset($this->_specificOptions['default_options'][$key])) {
0174                 $this->_specificOptions['default_options'][$key] = $value;
0175             }
0176         }
0177     }
0178 
0179     /**
0180      * Set the deprecated contentTypeMemorization option
0181      *
0182      * @param boolean $value value
0183      * @return void
0184      * @deprecated
0185      */
0186     protected function _setContentTypeMemorization($value)
0187     {
0188         $found = null;
0189         foreach ($this->_specificOptions['memorize_headers'] as $key => $value) {
0190             if (strtolower($value) == 'content-type') {
0191                 $found = $key;
0192             }
0193         }
0194         if ($value) {
0195             if (!$found) {
0196                 $this->_specificOptions['memorize_headers'][] = 'Content-Type';
0197             }
0198         } else {
0199             if ($found) {
0200                 unset($this->_specificOptions['memorize_headers'][$found]);
0201             }
0202         }
0203     }
0204 
0205     /**
0206      * Specific setter for the 'regexps' option (with some additional tests)
0207      *
0208      * @param  array $options Associative array
0209      * @throws Zend_Cache_Exception
0210      * @return void
0211      */
0212     protected function _setRegexps($regexps)
0213     {
0214         if (!is_array($regexps)) {
0215             Zend_Cache::throwException('regexps option must be an array !');
0216         }
0217         foreach ($regexps as $regexp=>$conf) {
0218             if (!is_array($conf)) {
0219                 Zend_Cache::throwException('regexps option must be an array of arrays !');
0220             }
0221             $validKeys = array_keys($this->_specificOptions['default_options']);
0222             foreach ($conf as $key=>$value) {
0223                 if (!is_string($key)) {
0224                     Zend_Cache::throwException("unknown option [$key] !");
0225                 }
0226                 $key = strtolower($key);
0227                 if (!in_array($key, $validKeys)) {
0228                     unset($regexps[$regexp][$key]);
0229                 }
0230             }
0231         }
0232         $this->setOption('regexps', $regexps);
0233     }
0234 
0235     /**
0236      * Start the cache
0237      *
0238      * @param  string  $id       (optional) A cache id (if you set a value here, maybe you have to use Output frontend instead)
0239      * @param  boolean $doNotDie For unit testing only !
0240      * @return boolean True if the cache is hit (false else)
0241      */
0242     public function start($id = false, $doNotDie = false)
0243     {
0244         $this->_cancel = false;
0245         $lastMatchingRegexp = null;
0246         if (isset($_SERVER['REQUEST_URI'])) {
0247             foreach ($this->_specificOptions['regexps'] as $regexp => $conf) {
0248                 if (preg_match("`$regexp`", $_SERVER['REQUEST_URI'])) {
0249                     $lastMatchingRegexp = $regexp;
0250                 }
0251             }
0252         }
0253         $this->_activeOptions = $this->_specificOptions['default_options'];
0254         if ($lastMatchingRegexp !== null) {
0255             $conf = $this->_specificOptions['regexps'][$lastMatchingRegexp];
0256             foreach ($conf as $key=>$value) {
0257                 $this->_activeOptions[$key] = $value;
0258             }
0259         }
0260         if (!($this->_activeOptions['cache'])) {
0261             return false;
0262         }
0263         if (!$id) {
0264             $id = $this->_makeId();
0265             if (!$id) {
0266                 return false;
0267             }
0268         }
0269         $array = $this->load($id);
0270         if ($array !== false) {
0271             $data = $array['data'];
0272             $headers = $array['headers'];
0273             if (!headers_sent()) {
0274                 foreach ($headers as $key=>$headerCouple) {
0275                     $name = $headerCouple[0];
0276                     $value = $headerCouple[1];
0277                     header("$name: $value");
0278                 }
0279             }
0280             if ($this->_specificOptions['debug_header']) {
0281                 echo 'DEBUG HEADER : This is a cached page !';
0282             }
0283             echo $data;
0284             if ($doNotDie) {
0285                 return true;
0286             }
0287             die();
0288         }
0289         ob_start(array($this, '_flush'));
0290         ob_implicit_flush(false);
0291         return false;
0292     }
0293 
0294     /**
0295      * Cancel the current caching process
0296      */
0297     public function cancel()
0298     {
0299         $this->_cancel = true;
0300     }
0301 
0302     /**
0303      * callback for output buffering
0304      * (shouldn't really be called manually)
0305      *
0306      * @param  string $data Buffered output
0307      * @return string Data to send to browser
0308      */
0309     public function _flush($data)
0310     {
0311         if ($this->_cancel) {
0312             return $data;
0313         }
0314         $contentType = null;
0315         $storedHeaders = array();
0316         $headersList = headers_list();
0317         foreach($this->_specificOptions['memorize_headers'] as $key=>$headerName) {
0318             foreach ($headersList as $headerSent) {
0319                 $tmp = explode(':', $headerSent);
0320                 $headerSentName = trim(array_shift($tmp));
0321                 if (strtolower($headerName) == strtolower($headerSentName)) {
0322                     $headerSentValue = trim(implode(':', $tmp));
0323                     $storedHeaders[] = array($headerSentName, $headerSentValue);
0324                 }
0325             }
0326         }
0327         $array = array(
0328             'data' => $data,
0329             'headers' => $storedHeaders
0330         );
0331         $this->save($array, null, $this->_activeOptions['tags'], $this->_activeOptions['specific_lifetime'], $this->_activeOptions['priority']);
0332         return $data;
0333     }
0334 
0335     /**
0336      * Make an id depending on REQUEST_URI and superglobal arrays (depending on options)
0337      *
0338      * @return mixed|false a cache id (string), false if the cache should have not to be used
0339      */
0340     protected function _makeId()
0341     {
0342         $tmp = $_SERVER['REQUEST_URI'];
0343         $array = explode('?', $tmp, 2);
0344           $tmp = $array[0];
0345         foreach (array('Get', 'Post', 'Session', 'Files', 'Cookie') as $arrayName) {
0346             $tmp2 = $this->_makePartialId($arrayName, $this->_activeOptions['cache_with_' . strtolower($arrayName) . '_variables'], $this->_activeOptions['make_id_with_' . strtolower($arrayName) . '_variables']);
0347             if ($tmp2===false) {
0348                 return false;
0349             }
0350             $tmp = $tmp . $tmp2;
0351         }
0352         return md5($tmp);
0353     }
0354 
0355     /**
0356      * Make a partial id depending on options
0357      *
0358      * @param  string $arrayName Superglobal array name
0359      * @param  bool   $bool1     If true, cache is still on even if there are some variables in the superglobal array
0360      * @param  bool   $bool2     If true, we have to use the content of the superglobal array to make a partial id
0361      * @return mixed|false Partial id (string) or false if the cache should have not to be used
0362      */
0363     protected function _makePartialId($arrayName, $bool1, $bool2)
0364     {
0365         switch ($arrayName) {
0366         case 'Get':
0367             $var = $_GET;
0368             break;
0369         case 'Post':
0370             $var = $_POST;
0371             break;
0372         case 'Session':
0373             if (isset($_SESSION)) {
0374                 $var = $_SESSION;
0375             } else {
0376                 $var = null;
0377             }
0378             break;
0379         case 'Cookie':
0380             if (isset($_COOKIE)) {
0381                 $var = $_COOKIE;
0382             } else {
0383                 $var = null;
0384             }
0385             break;
0386         case 'Files':
0387             $var = $_FILES;
0388             break;
0389         default:
0390             return false;
0391         }
0392         if ($bool1) {
0393             if ($bool2) {
0394                 return serialize($var);
0395             }
0396             return '';
0397         }
0398         if (count($var) > 0) {
0399             return false;
0400         }
0401         return '';
0402     }
0403 
0404 }