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_Backend
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  * @see Zend_Cache_Backend_Interface
0025  */
0026 // require_once 'Zend/Cache/Backend.php';
0027 
0028 /**
0029  * @see Zend_Cache_Backend_Interface
0030  */
0031 // require_once 'Zend/Cache/Backend/Interface.php';
0032 
0033 
0034 /**
0035  * Impementation of Zend Cache Backend using the Zend Platform (Output Content Caching)
0036  *
0037  * @package    Zend_Cache
0038  * @subpackage Zend_Cache_Backend
0039  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0040  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0041  */
0042 class Zend_Cache_Backend_ZendPlatform extends Zend_Cache_Backend implements Zend_Cache_Backend_Interface
0043 {
0044     /**
0045      * internal ZP prefix
0046      */
0047     const TAGS_PREFIX = "internal_ZPtag:";
0048 
0049     /**
0050      * Constructor
0051      * Validate that the Zend Platform is loaded and licensed
0052      *
0053      * @param  array $options Associative array of options
0054      * @throws Zend_Cache_Exception
0055      * @return void
0056      */
0057     public function __construct(array $options = array())
0058     {
0059         if (!function_exists('accelerator_license_info')) {
0060             Zend_Cache::throwException('The Zend Platform extension must be loaded for using this backend !');
0061         }
0062         if (!function_exists('accelerator_get_configuration')) {
0063             $licenseInfo = accelerator_license_info();
0064             Zend_Cache::throwException('The Zend Platform extension is not loaded correctly: '.$licenseInfo['failure_reason']);
0065         }
0066         $accConf = accelerator_get_configuration();
0067         if (@!$accConf['output_cache_licensed']) {
0068             Zend_Cache::throwException('The Zend Platform extension does not have the proper license to use content caching features');
0069         }
0070         if (@!$accConf['output_cache_enabled']) {
0071             Zend_Cache::throwException('The Zend Platform content caching feature must be enabled for using this backend, set the \'zend_accelerator.output_cache_enabled\' directive to On !');
0072         }
0073         if (!is_writable($accConf['output_cache_dir'])) {
0074             Zend_Cache::throwException('The cache copies directory \''. ini_get('zend_accelerator.output_cache_dir') .'\' must be writable !');
0075         }
0076         parent:: __construct($options);
0077     }
0078 
0079     /**
0080      * Test if a cache is available for the given id and (if yes) return it (false else)
0081      *
0082      * @param  string  $id                     Cache id
0083      * @param  boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
0084      * @return string Cached data (or false)
0085      */
0086     public function load($id, $doNotTestCacheValidity = false)
0087     {
0088         // doNotTestCacheValidity implemented by giving zero lifetime to the cache
0089         if ($doNotTestCacheValidity) {
0090             $lifetime = 0;
0091         } else {
0092             $lifetime = $this->_directives['lifetime'];
0093         }
0094         $res = output_cache_get($id, $lifetime);
0095         if($res) {
0096             return $res[0];
0097         } else {
0098             return false;
0099         }
0100     }
0101 
0102 
0103     /**
0104      * Test if a cache is available or not (for the given id)
0105      *
0106      * @param  string $id Cache id
0107      * @return mixed|false false (a cache is not available) or "last modified" timestamp (int) of the available cache record
0108      */
0109     public function test($id)
0110     {
0111         $result = output_cache_get($id, $this->_directives['lifetime']);
0112         if ($result) {
0113             return $result[1];
0114         }
0115         return false;
0116     }
0117 
0118     /**
0119      * Save some string datas into a cache record
0120      *
0121      * Note : $data is always "string" (serialization is done by the
0122      * core not by the backend)
0123      *
0124      * @param  string $data             Data to cache
0125      * @param  string $id               Cache id
0126      * @param  array  $tags             Array of strings, the cache record will be tagged by each string entry
0127      * @param  int    $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
0128      * @return boolean true if no problem
0129      */
0130     public function save($data, $id, $tags = array(), $specificLifetime = false)
0131     {
0132         if (!($specificLifetime === false)) {
0133             $this->_log("Zend_Cache_Backend_ZendPlatform::save() : non false specifc lifetime is unsuported for this backend");
0134         }
0135 
0136         $lifetime = $this->_directives['lifetime'];
0137         $result1  = output_cache_put($id, array($data, time()));
0138         $result2  = (count($tags) == 0);
0139 
0140         foreach ($tags as $tag) {
0141             $tagid = self::TAGS_PREFIX.$tag;
0142             $old_tags = output_cache_get($tagid, $lifetime);
0143             if ($old_tags === false) {
0144                 $old_tags = array();
0145             }
0146             $old_tags[$id] = $id;
0147             output_cache_remove_key($tagid);
0148             $result2 = output_cache_put($tagid, $old_tags);
0149         }
0150 
0151         return $result1 && $result2;
0152     }
0153 
0154 
0155     /**
0156      * Remove a cache record
0157      *
0158      * @param  string $id Cache id
0159      * @return boolean True if no problem
0160      */
0161     public function remove($id)
0162     {
0163         return output_cache_remove_key($id);
0164     }
0165 
0166 
0167     /**
0168      * Clean some cache records
0169      *
0170      * Available modes are :
0171      * Zend_Cache::CLEANING_MODE_ALL (default)    => remove all cache entries ($tags is not used)
0172      * Zend_Cache::CLEANING_MODE_OLD              => remove too old cache entries ($tags is not used)
0173      *                                               This mode is not supported in this backend
0174      * Zend_Cache::CLEANING_MODE_MATCHING_TAG     => remove cache entries matching all given tags
0175      *                                               ($tags can be an array of strings or a single string)
0176      * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => unsupported
0177      * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
0178      *                                               ($tags can be an array of strings or a single string)
0179      *
0180      * @param  string $mode Clean mode
0181      * @param  array  $tags Array of tags
0182      * @throws Zend_Cache_Exception
0183      * @return boolean True if no problem
0184      */
0185     public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
0186     {
0187         switch ($mode) {
0188             case Zend_Cache::CLEANING_MODE_ALL:
0189             case Zend_Cache::CLEANING_MODE_OLD:
0190                 $cache_dir = ini_get('zend_accelerator.output_cache_dir');
0191                 if (!$cache_dir) {
0192                     return false;
0193                 }
0194                 $cache_dir .= '/.php_cache_api/';
0195                 return $this->_clean($cache_dir, $mode);
0196                 break;
0197             case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
0198                 $idlist = null;
0199                 foreach ($tags as $tag) {
0200                     $next_idlist = output_cache_get(self::TAGS_PREFIX.$tag, $this->_directives['lifetime']);
0201                     if ($idlist) {
0202                         $idlist = array_intersect_assoc($idlist, $next_idlist);
0203                     } else {
0204                         $idlist = $next_idlist;
0205                     }
0206                     if (count($idlist) == 0) {
0207                         // if ID list is already empty - we may skip checking other IDs
0208                         $idlist = null;
0209                         break;
0210                     }
0211                 }
0212                 if ($idlist) {
0213                     foreach ($idlist as $id) {
0214                         output_cache_remove_key($id);
0215                     }
0216                 }
0217                 return true;
0218                 break;
0219             case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
0220                 $this->_log("Zend_Cache_Backend_ZendPlatform::clean() : CLEANING_MODE_NOT_MATCHING_TAG is not supported by the Zend Platform backend");
0221                 return false;
0222                 break;
0223             case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
0224                 $idlist = null;
0225                 foreach ($tags as $tag) {
0226                     $next_idlist = output_cache_get(self::TAGS_PREFIX.$tag, $this->_directives['lifetime']);
0227                     if ($idlist) {
0228                         $idlist = array_merge_recursive($idlist, $next_idlist);
0229                     } else {
0230                         $idlist = $next_idlist;
0231                     }
0232                     if (count($idlist) == 0) {
0233                         // if ID list is already empty - we may skip checking other IDs
0234                         $idlist = null;
0235                         break;
0236                     }
0237                 }
0238                 if ($idlist) {
0239                     foreach ($idlist as $id) {
0240                         output_cache_remove_key($id);
0241                     }
0242                 }
0243                 return true;
0244                 break;
0245             default:
0246                 Zend_Cache::throwException('Invalid mode for clean() method');
0247                 break;
0248         }
0249     }
0250 
0251     /**
0252      * Clean a directory and recursivly go over it's subdirectories
0253      *
0254      * Remove all the cached files that need to be cleaned (according to mode and files mtime)
0255      *
0256      * @param  string $dir  Path of directory ot clean
0257      * @param  string $mode The same parameter as in Zend_Cache_Backend_ZendPlatform::clean()
0258      * @return boolean True if ok
0259      */
0260     private function _clean($dir, $mode)
0261     {
0262         $d = @dir($dir);
0263         if (!$d) {
0264             return false;
0265         }
0266         $result = true;
0267         while (false !== ($file = $d->read())) {
0268             if ($file == '.' || $file == '..') {
0269                 continue;
0270             }
0271             $file = $d->path . $file;
0272             if (is_dir($file)) {
0273                 $result = ($this->_clean($file .'/', $mode)) && ($result);
0274             } else {
0275                 if ($mode == Zend_Cache::CLEANING_MODE_ALL) {
0276                     $result = ($this->_remove($file)) && ($result);
0277                 } else if ($mode == Zend_Cache::CLEANING_MODE_OLD) {
0278                     // Files older than lifetime get deleted from cache
0279                     if ($this->_directives['lifetime'] !== null) {
0280                         if ((time() - @filemtime($file)) > $this->_directives['lifetime']) {
0281                             $result = ($this->_remove($file)) && ($result);
0282                         }
0283                     }
0284                 }
0285             }
0286         }
0287         $d->close();
0288         return $result;
0289     }
0290 
0291     /**
0292      * Remove a file
0293      *
0294      * If we can't remove the file (because of locks or any problem), we will touch
0295      * the file to invalidate it
0296      *
0297      * @param  string $file Complete file path
0298      * @return boolean True if ok
0299      */
0300     private function _remove($file)
0301     {
0302         if (!@unlink($file)) {
0303             # If we can't remove the file (because of locks or any problem), we will touch
0304             # the file to invalidate it
0305             $this->_log("Zend_Cache_Backend_ZendPlatform::_remove() : we can't remove $file => we are going to try to invalidate it");
0306             if ($this->_directives['lifetime'] === null) {
0307                 return false;
0308             }
0309             if (!file_exists($file)) {
0310                 return false;
0311             }
0312             return @touch($file, time() - 2*abs($this->_directives['lifetime']));
0313         }
0314         return true;
0315     }
0316 
0317 }