PageRenderTime 85ms CodeModel.GetById 41ms app.highlight 13ms RepoModel.GetById 27ms app.codeStats 0ms

/Cache/Frontend/Page.php

https://bitbucket.org/goldie/zend-framework1
PHP | 404 lines | 244 code | 16 blank | 144 comment | 39 complexity | efcf79b494e8d5a75b327637b92f87c4 MD5 | raw file
  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_Cache
 17 * @subpackage Zend_Cache_Frontend
 18 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 19 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 20 * @version    $Id: Page.php 23775 2011-03-01 17:25:24Z ralph $
 21 */
 22
 23
 24/**
 25 * @see Zend_Cache_Core
 26 */
 27require_once 'Zend/Cache/Core.php';
 28
 29
 30/**
 31 * @package    Zend_Cache
 32 * @subpackage Zend_Cache_Frontend
 33 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 34 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 35 */
 36class Zend_Cache_Frontend_Page extends Zend_Cache_Core
 37{
 38    /**
 39     * This frontend specific options
 40     *
 41     * ====> (boolean) http_conditional :
 42     * - if true, http conditional mode is on
 43     * WARNING : http_conditional OPTION IS NOT IMPLEMENTED FOR THE MOMENT (TODO)
 44     *
 45     * ====> (boolean) debug_header :
 46     * - if true, a debug text is added before each cached pages
 47     *
 48     * ====> (boolean) content_type_memorization :
 49     * - deprecated => use memorize_headers instead
 50     * - if the Content-Type header is sent after the cache was started, the
 51     *   corresponding value can be memorized and replayed when the cache is hit
 52     *   (if false (default), the frontend doesn't take care of Content-Type header)
 53     *
 54     * ====> (array) memorize_headers :
 55     * - an array of strings corresponding to some HTTP headers name. Listed headers
 56     *   will be stored with cache datas and "replayed" when the cache is hit
 57     *
 58     * ====> (array) default_options :
 59     * - an associative array of default options :
 60     *     - (boolean) cache : cache is on by default if true
 61     *     - (boolean) cacheWithXXXVariables  (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') :
 62     *       if true,  cache is still on even if there are some variables in this superglobal array
 63     *       if false, cache is off if there are some variables in this superglobal array
 64     *     - (boolean) makeIdWithXXXVariables (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') :
 65     *       if true, we have to use the content of this superglobal array to make a cache id
 66     *       if false, the cache id won't be dependent of the content of this superglobal array
 67     *     - (int) specific_lifetime : cache specific lifetime
 68     *                                (false => global lifetime is used, null => infinite lifetime,
 69     *                                 integer => this lifetime is used), this "lifetime" is probably only
 70     *                                usefull when used with "regexps" array
 71     *     - (array) tags : array of tags (strings)
 72     *     - (int) priority : integer between 0 (very low priority) and 10 (maximum priority) used by
 73     *                        some particular backends
 74     *
 75     * ====> (array) regexps :
 76     * - an associative array to set options only for some REQUEST_URI
 77     * - keys are (pcre) regexps
 78     * - values are associative array with specific options to set if the regexp matchs on $_SERVER['REQUEST_URI']
 79     *   (see default_options for the list of available options)
 80     * - if several regexps match the $_SERVER['REQUEST_URI'], only the last one will be used
 81     *
 82     * @var array options
 83     */
 84    protected $_specificOptions = array(
 85        'http_conditional' => false,
 86        'debug_header' => false,
 87        'content_type_memorization' => false,
 88        'memorize_headers' => array(),
 89        'default_options' => array(
 90            'cache_with_get_variables' => false,
 91            'cache_with_post_variables' => false,
 92            'cache_with_session_variables' => false,
 93            'cache_with_files_variables' => false,
 94            'cache_with_cookie_variables' => false,
 95            'make_id_with_get_variables' => true,
 96            'make_id_with_post_variables' => true,
 97            'make_id_with_session_variables' => true,
 98            'make_id_with_files_variables' => true,
 99            'make_id_with_cookie_variables' => true,
100            'cache' => true,
101            'specific_lifetime' => false,
102            'tags' => array(),
103            'priority' => null
104        ),
105        'regexps' => array()
106    );
107
108    /**
109     * Internal array to store some options
110     *
111     * @var array associative array of options
112     */
113    protected $_activeOptions = array();
114
115    /**
116     * If true, the page won't be cached
117     *
118     * @var boolean
119     */
120    protected $_cancel = false;
121
122    /**
123     * Constructor
124     *
125     * @param  array   $options                Associative array of options
126     * @param  boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
127     * @throws Zend_Cache_Exception
128     * @return void
129     */
130    public function __construct(array $options = array())
131    {
132        while (list($name, $value) = each($options)) {
133            $name = strtolower($name);
134            switch ($name) {
135                case 'regexps':
136                    $this->_setRegexps($value);
137                    break;
138                case 'default_options':
139                    $this->_setDefaultOptions($value);
140                    break;
141                case 'content_type_memorization':
142                    $this->_setContentTypeMemorization($value);
143                    break;
144                default:
145                    $this->setOption($name, $value);
146            }
147        }
148        if (isset($this->_specificOptions['http_conditional'])) {
149            if ($this->_specificOptions['http_conditional']) {
150                Zend_Cache::throwException('http_conditional is not implemented for the moment !');
151            }
152        }
153        $this->setOption('automatic_serialization', true);
154    }
155
156    /**
157     * Specific setter for the 'default_options' option (with some additional tests)
158     *
159     * @param  array $options Associative array
160     * @throws Zend_Cache_Exception
161     * @return void
162     */
163    protected function _setDefaultOptions($options)
164    {
165        if (!is_array($options)) {
166            Zend_Cache::throwException('default_options must be an array !');
167        }
168        foreach ($options as $key=>$value) {
169            if (!is_string($key)) {
170                Zend_Cache::throwException("invalid option [$key] !");
171            }
172            $key = strtolower($key);
173            if (isset($this->_specificOptions['default_options'][$key])) {
174                $this->_specificOptions['default_options'][$key] = $value;
175            }
176        }
177    }
178
179    /**
180     * Set the deprecated contentTypeMemorization option
181     *
182     * @param boolean $value value
183     * @return void
184     * @deprecated
185     */
186    protected function _setContentTypeMemorization($value)
187    {
188        $found = null;
189        foreach ($this->_specificOptions['memorize_headers'] as $key => $value) {
190            if (strtolower($value) == 'content-type') {
191                $found = $key;
192            }
193        }
194        if ($value) {
195            if (!$found) {
196                $this->_specificOptions['memorize_headers'][] = 'Content-Type';
197            }
198        } else {
199            if ($found) {
200                unset($this->_specificOptions['memorize_headers'][$found]);
201            }
202        }
203    }
204
205    /**
206     * Specific setter for the 'regexps' option (with some additional tests)
207     *
208     * @param  array $options Associative array
209     * @throws Zend_Cache_Exception
210     * @return void
211     */
212    protected function _setRegexps($regexps)
213    {
214        if (!is_array($regexps)) {
215            Zend_Cache::throwException('regexps option must be an array !');
216        }
217        foreach ($regexps as $regexp=>$conf) {
218            if (!is_array($conf)) {
219                Zend_Cache::throwException('regexps option must be an array of arrays !');
220            }
221            $validKeys = array_keys($this->_specificOptions['default_options']);
222            foreach ($conf as $key=>$value) {
223                if (!is_string($key)) {
224                    Zend_Cache::throwException("unknown option [$key] !");
225                }
226                $key = strtolower($key);
227                if (!in_array($key, $validKeys)) {
228                    unset($regexps[$regexp][$key]);
229                }
230            }
231        }
232        $this->setOption('regexps', $regexps);
233    }
234
235    /**
236     * Start the cache
237     *
238     * @param  string  $id       (optional) A cache id (if you set a value here, maybe you have to use Output frontend instead)
239     * @param  boolean $doNotDie For unit testing only !
240     * @return boolean True if the cache is hit (false else)
241     */
242    public function start($id = false, $doNotDie = false)
243    {
244        $this->_cancel = false;
245        $lastMatchingRegexp = null;
246        if (isset($_SERVER['REQUEST_URI'])) {
247            foreach ($this->_specificOptions['regexps'] as $regexp => $conf) {
248                if (preg_match("`$regexp`", $_SERVER['REQUEST_URI'])) {
249                    $lastMatchingRegexp = $regexp;
250                }
251            }
252        }
253        $this->_activeOptions = $this->_specificOptions['default_options'];
254        if ($lastMatchingRegexp !== null) {
255            $conf = $this->_specificOptions['regexps'][$lastMatchingRegexp];
256            foreach ($conf as $key=>$value) {
257                $this->_activeOptions[$key] = $value;
258            }
259        }
260        if (!($this->_activeOptions['cache'])) {
261            return false;
262        }
263        if (!$id) {
264            $id = $this->_makeId();
265            if (!$id) {
266                return false;
267            }
268        }
269        $array = $this->load($id);
270        if ($array !== false) {
271            $data = $array['data'];
272            $headers = $array['headers'];
273            if (!headers_sent()) {
274                foreach ($headers as $key=>$headerCouple) {
275                    $name = $headerCouple[0];
276                    $value = $headerCouple[1];
277                    header("$name: $value");
278                }
279            }
280            if ($this->_specificOptions['debug_header']) {
281                echo 'DEBUG HEADER : This is a cached page !';
282            }
283            echo $data;
284            if ($doNotDie) {
285                return true;
286            }
287            die();
288        }
289        ob_start(array($this, '_flush'));
290        ob_implicit_flush(false);
291        return false;
292    }
293
294    /**
295     * Cancel the current caching process
296     */
297    public function cancel()
298    {
299        $this->_cancel = true;
300    }
301
302    /**
303     * callback for output buffering
304     * (shouldn't really be called manually)
305     *
306     * @param  string $data Buffered output
307     * @return string Data to send to browser
308     */
309    public function _flush($data)
310    {
311        if ($this->_cancel) {
312            return $data;
313        }
314        $contentType = null;
315        $storedHeaders = array();
316        $headersList = headers_list();
317        foreach($this->_specificOptions['memorize_headers'] as $key=>$headerName) {
318            foreach ($headersList as $headerSent) {
319                $tmp = explode(':', $headerSent);
320                $headerSentName = trim(array_shift($tmp));
321                if (strtolower($headerName) == strtolower($headerSentName)) {
322                    $headerSentValue = trim(implode(':', $tmp));
323                    $storedHeaders[] = array($headerSentName, $headerSentValue);
324                }
325            }
326        }
327        $array = array(
328            'data' => $data,
329            'headers' => $storedHeaders
330        );
331        $this->save($array, null, $this->_activeOptions['tags'], $this->_activeOptions['specific_lifetime'], $this->_activeOptions['priority']);
332        return $data;
333    }
334
335    /**
336     * Make an id depending on REQUEST_URI and superglobal arrays (depending on options)
337     *
338     * @return mixed|false a cache id (string), false if the cache should have not to be used
339     */
340    protected function _makeId()
341    {
342        $tmp = $_SERVER['REQUEST_URI'];
343        $array = explode('?', $tmp, 2);
344          $tmp = $array[0];
345        foreach (array('Get', 'Post', 'Session', 'Files', 'Cookie') as $arrayName) {
346            $tmp2 = $this->_makePartialId($arrayName, $this->_activeOptions['cache_with_' . strtolower($arrayName) . '_variables'], $this->_activeOptions['make_id_with_' . strtolower($arrayName) . '_variables']);
347            if ($tmp2===false) {
348                return false;
349            }
350            $tmp = $tmp . $tmp2;
351        }
352        return md5($tmp);
353    }
354
355    /**
356     * Make a partial id depending on options
357     *
358     * @param  string $arrayName Superglobal array name
359     * @param  bool   $bool1     If true, cache is still on even if there are some variables in the superglobal array
360     * @param  bool   $bool2     If true, we have to use the content of the superglobal array to make a partial id
361     * @return mixed|false Partial id (string) or false if the cache should have not to be used
362     */
363    protected function _makePartialId($arrayName, $bool1, $bool2)
364    {
365        switch ($arrayName) {
366        case 'Get':
367            $var = $_GET;
368            break;
369        case 'Post':
370            $var = $_POST;
371            break;
372        case 'Session':
373            if (isset($_SESSION)) {
374                $var = $_SESSION;
375            } else {
376                $var = null;
377            }
378            break;
379        case 'Cookie':
380            if (isset($_COOKIE)) {
381                $var = $_COOKIE;
382            } else {
383                $var = null;
384            }
385            break;
386        case 'Files':
387            $var = $_FILES;
388            break;
389        default:
390            return false;
391        }
392        if ($bool1) {
393            if ($bool2) {
394                return serialize($var);
395            }
396            return '';
397        }
398        if (count($var) > 0) {
399            return false;
400        }
401        return '';
402    }
403
404}