PageRenderTime 546ms CodeModel.GetById 111ms app.highlight 266ms RepoModel.GetById 107ms app.codeStats 1ms

/concreteOLD/libraries/3rdparty/Zend/Cache/Backend/Static.php

https://bitbucket.org/selfeky/xclusivescardwebsite
PHP | 564 lines | 452 code | 21 blank | 91 comment | 40 complexity | aaf545fa09291d0a3c89e95dac666e76 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_Backend
 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: Static.php 23775 2011-03-01 17:25:24Z ralph $
 21 */
 22
 23/**
 24 * @see Zend_Cache_Backend_Interface
 25 */
 26require_once 'Zend/Cache/Backend/Interface.php';
 27
 28/**
 29 * @see Zend_Cache_Backend
 30 */
 31require_once 'Zend/Cache/Backend.php';
 32
 33/**
 34 * @package    Zend_Cache
 35 * @subpackage Zend_Cache_Backend
 36 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 37 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 38 */
 39class Zend_Cache_Backend_Static
 40    extends Zend_Cache_Backend
 41    implements Zend_Cache_Backend_Interface
 42{
 43    const INNER_CACHE_NAME = 'zend_cache_backend_static_tagcache';
 44
 45    /**
 46     * Static backend options
 47     * @var array
 48     */
 49    protected $_options = array(
 50        'public_dir'            => null,
 51        'sub_dir'               => 'html',
 52        'file_extension'        => '.html',
 53        'index_filename'        => 'index',
 54        'file_locking'          => true,
 55        'cache_file_umask'      => 0600,
 56        'cache_directory_umask' => 0700,
 57        'debug_header'          => false,
 58        'tag_cache'             => null,
 59        'disable_caching'       => false
 60    );
 61
 62    /**
 63     * Cache for handling tags
 64     * @var Zend_Cache_Core
 65     */
 66    protected $_tagCache = null;
 67
 68    /**
 69     * Tagged items
 70     * @var array
 71     */
 72    protected $_tagged = null;
 73
 74    /**
 75     * Interceptor child method to handle the case where an Inner
 76     * Cache object is being set since it's not supported by the
 77     * standard backend interface
 78     *
 79     * @param  string $name
 80     * @param  mixed $value
 81     * @return Zend_Cache_Backend_Static
 82     */
 83    public function setOption($name, $value)
 84    {
 85        if ($name == 'tag_cache') {
 86            $this->setInnerCache($value);
 87        } else {
 88            parent::setOption($name, $value);
 89        }
 90        return $this;
 91    }
 92
 93    /**
 94     * Retrieve any option via interception of the parent's statically held
 95     * options including the local option for a tag cache.
 96     *
 97     * @param  string $name
 98     * @return mixed
 99     */
100    public function getOption($name)
101    {
102        if ($name == 'tag_cache') {
103            return $this->getInnerCache();
104        } else {
105            if (in_array($name, $this->_options)) {
106                return $this->_options[$name];
107            }
108            if ($name == 'lifetime') {
109                return parent::getLifetime();
110            }
111            return null;
112        }
113    }
114
115    /**
116     * Test if a cache is available for the given id and (if yes) return it (false else)
117     *
118     * Note : return value is always "string" (unserialization is done by the core not by the backend)
119     *
120     * @param  string  $id                     Cache id
121     * @param  boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
122     * @return string|false cached datas
123     */
124    public function load($id, $doNotTestCacheValidity = false)
125    {
126        if (($id = (string)$id) === '') {
127            $id = $this->_detectId();
128        } else {
129            $id = $this->_decodeId($id);
130        }
131        if (!$this->_verifyPath($id)) {
132            Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
133        }
134        if ($doNotTestCacheValidity) {
135            $this->_log("Zend_Cache_Backend_Static::load() : \$doNotTestCacheValidity=true is unsupported by the Static backend");
136        }
137
138        $fileName = basename($id);
139        if ($fileName === '') {
140            $fileName = $this->_options['index_filename'];
141        }
142        $pathName = $this->_options['public_dir'] . dirname($id);
143        $file     = rtrim($pathName, '/') . '/' . $fileName . $this->_options['file_extension'];
144        if (file_exists($file)) {
145            $content = file_get_contents($file);
146            return $content;
147        }
148
149        return false;
150    }
151
152    /**
153     * Test if a cache is available or not (for the given id)
154     *
155     * @param  string $id cache id
156     * @return bool
157     */
158    public function test($id)
159    {
160        $id = $this->_decodeId($id);
161        if (!$this->_verifyPath($id)) {
162            Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
163        }
164
165        $fileName = basename($id);
166        if ($fileName === '') {
167            $fileName = $this->_options['index_filename'];
168        }
169        if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
170            $this->_tagged = $tagged;
171        } elseif (!$this->_tagged) {
172            return false;
173        }
174        $pathName = $this->_options['public_dir'] . dirname($id);
175
176        // Switch extension if needed
177        if (isset($this->_tagged[$id])) {
178            $extension = $this->_tagged[$id]['extension'];
179        } else {
180            $extension = $this->_options['file_extension'];
181        }
182        $file     = $pathName . '/' . $fileName . $extension;
183        if (file_exists($file)) {
184            return true;
185        }
186        return false;
187    }
188
189    /**
190     * Save some string datas into a cache record
191     *
192     * Note : $data is always "string" (serialization is done by the
193     * core not by the backend)
194     *
195     * @param  string $data            Datas to cache
196     * @param  string $id              Cache id
197     * @param  array $tags             Array of strings, the cache record will be tagged by each string entry
198     * @param  int   $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
199     * @return boolean true if no problem
200     */
201    public function save($data, $id, $tags = array(), $specificLifetime = false)
202    {
203        if ($this->_options['disable_caching']) {
204            return true;
205        }
206        $extension = null;
207        if ($this->_isSerialized($data)) {
208            $data = unserialize($data);
209            $extension = '.' . ltrim($data[1], '.');
210            $data = $data[0];
211        }
212
213        clearstatcache();
214        if (($id = (string)$id) === '') {
215            $id = $this->_detectId();
216        } else {
217            $id = $this->_decodeId($id);
218        }
219
220        $fileName = basename($id);
221        if ($fileName === '') {
222            $fileName = $this->_options['index_filename'];
223        }
224
225        $pathName = realpath($this->_options['public_dir']) . dirname($id);
226        $this->_createDirectoriesFor($pathName);
227
228        if ($id === null || strlen($id) == 0) {
229            $dataUnserialized = unserialize($data);
230            $data = $dataUnserialized['data'];
231        }
232        $ext = $this->_options['file_extension'];
233        if ($extension) $ext = $extension;
234        $file = rtrim($pathName, '/') . '/' . $fileName . $ext;
235        if ($this->_options['file_locking']) {
236            $result = file_put_contents($file, $data, LOCK_EX);
237        } else {
238            $result = file_put_contents($file, $data);
239        }
240        @chmod($file, $this->_octdec($this->_options['cache_file_umask']));
241
242        if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
243            $this->_tagged = $tagged;
244        } elseif ($this->_tagged === null) {
245            $this->_tagged = array();
246        }
247        if (!isset($this->_tagged[$id])) {
248            $this->_tagged[$id] = array();
249        }
250        if (!isset($this->_tagged[$id]['tags'])) {
251            $this->_tagged[$id]['tags'] = array();
252        }
253        $this->_tagged[$id]['tags'] = array_unique(array_merge($this->_tagged[$id]['tags'], $tags));
254        $this->_tagged[$id]['extension'] = $ext;
255        $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
256        return (bool) $result;
257    }
258
259    /**
260     * Recursively create the directories needed to write the static file
261     */
262    protected function _createDirectoriesFor($path)
263    {
264        if (!is_dir($path)) {
265            $oldUmask = umask(0);
266            if ( !@mkdir($path, $this->_octdec($this->_options['cache_directory_umask']), true)) {
267                $lastErr = error_get_last();
268                umask($oldUmask);
269                Zend_Cache::throwException("Can't create directory: {$lastErr['message']}");
270            }
271            umask($oldUmask);
272        }
273    }
274
275    /**
276     * Detect serialization of data (cannot predict since this is the only way
277     * to obey the interface yet pass in another parameter).
278     *
279     * In future, ZF 2.0, check if we can just avoid the interface restraints.
280     *
281     * This format is the only valid one possible for the class, so it's simple
282     * to just run a regular expression for the starting serialized format.
283     */
284    protected function _isSerialized($data)
285    {
286        return preg_match("/a:2:\{i:0;s:\d+:\"/", $data);
287    }
288
289    /**
290     * Remove a cache record
291     *
292     * @param  string $id Cache id
293     * @return boolean True if no problem
294     */
295    public function remove($id)
296    {
297        if (!$this->_verifyPath($id)) {
298            Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
299        }
300        $fileName = basename($id);
301        if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
302            $this->_tagged = $tagged;
303        } elseif (!$this->_tagged) {
304            return false;
305        }
306        if (isset($this->_tagged[$id])) {
307            $extension = $this->_tagged[$id]['extension'];
308        } else {
309            $extension = $this->_options['file_extension'];
310        }
311        if ($fileName === '') {
312            $fileName = $this->_options['index_filename'];
313        }
314        $pathName = $this->_options['public_dir'] . dirname($id);
315        $file     = realpath($pathName) . '/' . $fileName . $extension;
316        if (!file_exists($file)) {
317            return false;
318        }
319        return unlink($file);
320    }
321
322    /**
323     * Remove a cache record recursively for the given directory matching a
324     * REQUEST_URI based relative path (deletes the actual file matching this
325     * in addition to the matching directory)
326     *
327     * @param  string $id Cache id
328     * @return boolean True if no problem
329     */
330    public function removeRecursively($id)
331    {
332        if (!$this->_verifyPath($id)) {
333            Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
334        }
335        $fileName = basename($id);
336        if ($fileName === '') {
337            $fileName = $this->_options['index_filename'];
338        }
339        $pathName  = $this->_options['public_dir'] . dirname($id);
340        $file      = $pathName . '/' . $fileName . $this->_options['file_extension'];
341        $directory = $pathName . '/' . $fileName;
342        if (file_exists($directory)) {
343            if (!is_writable($directory)) {
344                return false;
345            }
346            if (is_dir($directory)) {
347                foreach (new DirectoryIterator($directory) as $file) {
348                    if (true === $file->isFile()) {
349                        if (false === unlink($file->getPathName())) {
350                            return false;
351                        }
352                    }
353                }
354            }
355            rmdir($directory);
356        }
357        if (file_exists($file)) {
358            if (!is_writable($file)) {
359                return false;
360            }
361            return unlink($file);
362        }
363        return true;
364    }
365
366    /**
367     * Clean some cache records
368     *
369     * Available modes are :
370     * Zend_Cache::CLEANING_MODE_ALL (default)    => remove all cache entries ($tags is not used)
371     * Zend_Cache::CLEANING_MODE_OLD              => remove too old cache entries ($tags is not used)
372     * Zend_Cache::CLEANING_MODE_MATCHING_TAG     => remove cache entries matching all given tags
373     *                                               ($tags can be an array of strings or a single string)
374     * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
375     *                                               ($tags can be an array of strings or a single string)
376     * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
377     *                                               ($tags can be an array of strings or a single string)
378     *
379     * @param  string $mode Clean mode
380     * @param  array  $tags Array of tags
381     * @return boolean true if no problem
382     */
383    public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
384    {
385        $result = false;
386        switch ($mode) {
387            case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
388            case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
389                if (empty($tags)) {
390                    throw new Zend_Exception('Cannot use tag matching modes as no tags were defined');
391                }
392                if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
393                    $this->_tagged = $tagged;
394                } elseif (!$this->_tagged) {
395                    return true;
396                }
397                foreach ($tags as $tag) {
398                    $urls = array_keys($this->_tagged);
399                    foreach ($urls as $url) {
400                        if (isset($this->_tagged[$url]['tags']) && in_array($tag, $this->_tagged[$url]['tags'])) {
401                            $this->remove($url);
402                            unset($this->_tagged[$url]);
403                        }
404                    }
405                }
406                $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
407                $result = true;
408                break;
409            case Zend_Cache::CLEANING_MODE_ALL:
410                if ($this->_tagged === null) {
411                    $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME);
412                    $this->_tagged = $tagged;
413                }
414                if ($this->_tagged === null || empty($this->_tagged)) {
415                    return true;
416                }
417                $urls = array_keys($this->_tagged);
418                foreach ($urls as $url) {
419                    $this->remove($url);
420                    unset($this->_tagged[$url]);
421                }
422                $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
423                $result = true;
424                break;
425            case Zend_Cache::CLEANING_MODE_OLD:
426                $this->_log("Zend_Cache_Backend_Static : Selected Cleaning Mode Currently Unsupported By This Backend");
427                break;
428            case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
429                if (empty($tags)) {
430                    throw new Zend_Exception('Cannot use tag matching modes as no tags were defined');
431                }
432                if ($this->_tagged === null) {
433                    $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME);
434                    $this->_tagged = $tagged;
435                }
436                if ($this->_tagged === null || empty($this->_tagged)) {
437                    return true;
438                }
439                $urls = array_keys($this->_tagged);
440                foreach ($urls as $url) {
441                    $difference = array_diff($tags, $this->_tagged[$url]['tags']);
442                    if (count($tags) == count($difference)) {
443                        $this->remove($url);
444                        unset($this->_tagged[$url]);
445                    }
446                }
447                $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
448                $result = true;
449                break;
450            default:
451                Zend_Cache::throwException('Invalid mode for clean() method');
452                break;
453        }
454        return $result;
455    }
456
457    /**
458     * Set an Inner Cache, used here primarily to store Tags associated
459     * with caches created by this backend. Note: If Tags are lost, the cache
460     * should be completely cleaned as the mapping of tags to caches will
461     * have been irrevocably lost.
462     *
463     * @param  Zend_Cache_Core
464     * @return void
465     */
466    public function setInnerCache(Zend_Cache_Core $cache)
467    {
468        $this->_tagCache = $cache;
469        $this->_options['tag_cache'] = $cache;
470    }
471
472    /**
473     * Get the Inner Cache if set
474     *
475     * @return Zend_Cache_Core
476     */
477    public function getInnerCache()
478    {
479        if ($this->_tagCache === null) {
480            Zend_Cache::throwException('An Inner Cache has not been set; use setInnerCache()');
481        }
482        return $this->_tagCache;
483    }
484
485    /**
486     * Verify path exists and is non-empty
487     *
488     * @param  string $path
489     * @return bool
490     */
491    protected function _verifyPath($path)
492    {
493        $path = realpath($path);
494        $base = realpath($this->_options['public_dir']);
495        return strncmp($path, $base, strlen($base)) !== 0;
496    }
497
498    /**
499     * Determine the page to save from the request
500     *
501     * @return string
502     */
503    protected function _detectId()
504    {
505        return $_SERVER['REQUEST_URI'];
506    }
507
508    /**
509     * Validate a cache id or a tag (security, reliable filenames, reserved prefixes...)
510     *
511     * Throw an exception if a problem is found
512     *
513     * @param  string $string Cache id or tag
514     * @throws Zend_Cache_Exception
515     * @return void
516     * @deprecated Not usable until perhaps ZF 2.0
517     */
518    protected static function _validateIdOrTag($string)
519    {
520        if (!is_string($string)) {
521            Zend_Cache::throwException('Invalid id or tag : must be a string');
522        }
523
524        // Internal only checked in Frontend - not here!
525        if (substr($string, 0, 9) == 'internal-') {
526            return;
527        }
528
529        // Validation assumes no query string, fragments or scheme included - only the path
530        if (!preg_match(
531                '/^(?:\/(?:(?:%[[:xdigit:]]{2}|[A-Za-z0-9-_.!~*\'()\[\]:@&=+$,;])*)?)+$/',
532                $string
533            )
534        ) {
535            Zend_Cache::throwException("Invalid id or tag '$string' : must be a valid URL path");
536        }
537    }
538
539    /**
540     * Detect an octal string and return its octal value for file permission ops
541     * otherwise return the non-string (assumed octal or decimal int already)
542     *
543     * @param string $val The potential octal in need of conversion
544     * @return int
545     */
546    protected function _octdec($val)
547    {
548        if (is_string($val) && decoct(octdec($val)) == $val) {
549            return octdec($val);
550        }
551        return $val;
552    }
553
554    /**
555     * Decode a request URI from the provided ID
556     *
557     * @param string $id
558     * @return string
559     */
560    protected function _decodeId($id)
561    {
562        return pack('H*', $id);
563    }
564}