v7‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT Manager.php000064400000023612152101626120006627 0ustar00 array( 'frontend' => array( 'name' => 'Core', 'options' => array( 'automatic_serialization' => true, ), ), 'backend' => array( 'name' => 'File', 'options' => array( // use system temp dir by default of file backend // 'cache_dir' => '../cache', ), ), ), // Static Page HTML Cache 'page' => array( 'frontend' => array( 'name' => 'Capture', 'options' => array( 'ignore_user_abort' => true, ), ), 'backend' => array( 'name' => 'Static', 'options' => array( 'public_dir' => '../public', ), ), ), // Tag Cache 'pagetag' => array( 'frontend' => array( 'name' => 'Core', 'options' => array( 'automatic_serialization' => true, 'lifetime' => null ), ), 'backend' => array( 'name' => 'File', 'options' => array( // use system temp dir by default of file backend // 'cache_dir' => '../cache', // use default umask of file backend // 'cache_file_umask' => 0644 ), ), ), ); /** * Set a new cache for the Cache Manager to contain * * @param string $name * @param Zend_Cache_Core $cache * @return Zend_Cache_Manager */ public function setCache($name, Zend_Cache_Core $cache) { $this->_caches[$name] = $cache; return $this; } /** * Check if the Cache Manager contains the named cache object, or a named * configuration template to lazy load the cache object * * @param string $name * @return bool */ public function hasCache($name) { if (isset($this->_caches[$name]) || $this->hasCacheTemplate($name) ) { return true; } return false; } /** * Fetch the named cache object, or instantiate and return a cache object * using a named configuration template * * @param string $name * @return Zend_Cache_Core */ public function getCache($name) { if (isset($this->_caches[$name])) { return $this->_caches[$name]; } if (isset($this->_optionTemplates[$name])) { if ($name == self::PAGECACHE && (!isset($this->_optionTemplates[$name]['backend']['options']['tag_cache']) || !$this->_optionTemplates[$name]['backend']['options']['tag_cache'] instanceof Zend_Cache_Core) ) { $this->_optionTemplates[$name]['backend']['options']['tag_cache'] = $this->getCache(self::PAGETAGCACHE); } $this->_caches[$name] = Zend_Cache::factory( $this->_optionTemplates[$name]['frontend']['name'], $this->_optionTemplates[$name]['backend']['name'], isset($this->_optionTemplates[$name]['frontend']['options']) ? $this->_optionTemplates[$name]['frontend']['options'] : array(), isset($this->_optionTemplates[$name]['backend']['options']) ? $this->_optionTemplates[$name]['backend']['options'] : array(), isset($this->_optionTemplates[$name]['frontend']['customFrontendNaming']) ? $this->_optionTemplates[$name]['frontend']['customFrontendNaming'] : false, isset($this->_optionTemplates[$name]['backend']['customBackendNaming']) ? $this->_optionTemplates[$name]['backend']['customBackendNaming'] : false, isset($this->_optionTemplates[$name]['frontendBackendAutoload']) ? $this->_optionTemplates[$name]['frontendBackendAutoload'] : false ); return $this->_caches[$name]; } } /** * Fetch all available caches * * @return array An array of all available caches with it's names as key */ public function getCaches() { $caches = $this->_caches; foreach ($this->_optionTemplates as $name => $tmp) { if (!isset($caches[$name])) { $caches[$name] = $this->getCache($name); } } return $caches; } /** * Set a named configuration template from which a cache object can later * be lazy loaded * * @param string $name * @param array $options * @return Zend_Cache_Manager * @throws Zend_Cache_Exception */ public function setCacheTemplate($name, $options) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } elseif (!is_array($options)) { //--//require_once 'Zend/Cache/Exception.php'; throw new Zend_Cache_Exception('Options passed must be in' . ' an associative array or instance of Zend_Config'); } $this->_optionTemplates[$name] = $options; return $this; } /** * Check if the named configuration template * * @param string $name * @return bool */ public function hasCacheTemplate($name) { if (isset($this->_optionTemplates[$name])) { return true; } return false; } /** * Get the named configuration template * * @param string $name * @return array */ public function getCacheTemplate($name) { if (isset($this->_optionTemplates[$name])) { return $this->_optionTemplates[$name]; } } /** * Pass an array containing changes to be applied to a named * configuration * template * * @param string $name * @param array $options * @return Zend_Cache_Manager * @throws Zend_Cache_Exception for invalid options format or if option templates do not have $name */ public function setTemplateOptions($name, $options) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } elseif (!is_array($options)) { //--//require_once 'Zend/Cache/Exception.php'; throw new Zend_Cache_Exception('Options passed must be in' . ' an associative array or instance of Zend_Config'); } if (!isset($this->_optionTemplates[$name])) { throw new Zend_Cache_Exception('A cache configuration template' . 'does not exist with the name "' . $name . '"'); } $this->_optionTemplates[$name] = $this->_mergeOptions($this->_optionTemplates[$name], $options); return $this; } /** * Simple method to merge two configuration arrays * * @param array $current * @param array $options * @return array */ protected function _mergeOptions(array $current, array $options) { if (isset($options['frontend']['name'])) { $current['frontend']['name'] = $options['frontend']['name']; } if (isset($options['backend']['name'])) { $current['backend']['name'] = $options['backend']['name']; } if (isset($options['frontend']['options'])) { foreach ($options['frontend']['options'] as $key => $value) { $current['frontend']['options'][$key] = $value; } } if (isset($options['backend']['options'])) { foreach ($options['backend']['options'] as $key => $value) { $current['backend']['options'][$key] = $value; } } if (isset($options['frontend']['customFrontendNaming'])) { $current['frontend']['customFrontendNaming'] = $options['frontend']['customFrontendNaming']; } if (isset($options['backend']['customBackendNaming'])) { $current['backend']['customBackendNaming'] = $options['backend']['customBackendNaming']; } if (isset($options['frontendBackendAutoload'])) { $current['frontendBackendAutoload'] = $options['frontendBackendAutoload']; } return $current; } } Backend.php000064400000020375152101626120006607 0ustar00 (int) lifetime : * - Cache lifetime (in seconds) * - If null, the cache is valid forever * * =====> (int) logging : * - if set to true, a logging is activated throw Zend_Log * * @var array directives */ protected $_directives = array( 'lifetime' => 3600, 'logging' => false, 'logger' => null ); /** * Available options * * @var array available options */ protected $_options = array(); /** * Constructor * * @param array $options Associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $this->setOption($name, $value); } } /** * Set the frontend directives * * @param array $directives Assoc of directives * @throws Zend_Cache_Exception * @return void */ public function setDirectives($directives) { if (!is_array($directives)) Zend_Cache::throwException('Directives parameter must be an array'); while (list($name, $value) = each($directives)) { if (!is_string($name)) { Zend_Cache::throwException("Incorrect option name : $name"); } $name = strtolower($name); if (array_key_exists($name, $this->_directives)) { $this->_directives[$name] = $value; } } $this->_loggerSanity(); } /** * Set an option * * @param string $name * @param mixed $value * @throws Zend_Cache_Exception * @return void */ public function setOption($name, $value) { if (!is_string($name)) { Zend_Cache::throwException("Incorrect option name : $name"); } $name = strtolower($name); if (array_key_exists($name, $this->_options)) { $this->_options[$name] = $value; } } /** * Returns an option * * @param string $name Optional, the options name to return * @throws Zend_Cache_Exceptions * @return mixed */ public function getOption($name) { $name = strtolower($name); if (array_key_exists($name, $this->_options)) { return $this->_options[$name]; } if (array_key_exists($name, $this->_directives)) { return $this->_directives[$name]; } Zend_Cache::throwException("Incorrect option name : {$name}"); } /** * Get the life time * * if $specificLifetime is not false, the given specific life time is used * else, the global lifetime is used * * @param int $specificLifetime * @return int Cache life time */ public function getLifetime($specificLifetime) { if ($specificLifetime === false) { return $this->_directives['lifetime']; } return $specificLifetime; } /** * Return true if the automatic cleaning is available for the backend * * DEPRECATED : use getCapabilities() instead * * @deprecated * @return boolean */ public function isAutomaticCleaningAvailable() { return true; } /** * Determine system TMP directory and detect if we have read access * * inspired from Zend_File_Transfer_Adapter_Abstract * * @return string * @throws Zend_Cache_Exception if unable to determine directory */ public function getTmpDir() { $tmpdir = array(); foreach (array($_ENV, $_SERVER) as $tab) { foreach (array('TMPDIR', 'TEMP', 'TMP', 'windir', 'SystemRoot') as $key) { if (isset($tab[$key]) && is_string($tab[$key])) { if (($key == 'windir') or ($key == 'SystemRoot')) { $dir = realpath($tab[$key] . '\\temp'); } else { $dir = realpath($tab[$key]); } if ($this->_isGoodTmpDir($dir)) { return $dir; } } } } $upload = ini_get('upload_tmp_dir'); if ($upload) { $dir = realpath($upload); if ($this->_isGoodTmpDir($dir)) { return $dir; } } if (function_exists('sys_get_temp_dir')) { $dir = sys_get_temp_dir(); if ($this->_isGoodTmpDir($dir)) { return $dir; } } // Attemp to detect by creating a temporary file $tempFile = tempnam(md5(uniqid(rand(), TRUE)), ''); if ($tempFile) { $dir = realpath(dirname($tempFile)); unlink($tempFile); if ($this->_isGoodTmpDir($dir)) { return $dir; } } if ($this->_isGoodTmpDir('/tmp')) { return '/tmp'; } if ($this->_isGoodTmpDir('\\temp')) { return '\\temp'; } Zend_Cache::throwException('Could not determine temp directory, please specify a cache_dir manually'); } /** * Verify if the given temporary directory is readable and writable * * @param string $dir temporary directory * @return boolean true if the directory is ok */ protected function _isGoodTmpDir($dir) { if (is_readable($dir)) { if (is_writable($dir)) { return true; } } return false; } /** * Make sure if we enable logging that the Zend_Log class * is available. * Create a default log object if none is set. * * @throws Zend_Cache_Exception * @return void */ protected function _loggerSanity() { if (!isset($this->_directives['logging']) || !$this->_directives['logging']) { return; } if (isset($this->_directives['logger'])) { if ($this->_directives['logger'] instanceof Zend_Log) { return; } Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.'); } // Create a default logger to the standard output stream //--//require_once 'Zend/Log.php'; //--//require_once 'Zend/Log/Writer/Stream.php'; //--//require_once 'Zend/Log/Filter/Priority.php'; $logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output')); $logger->addFilter(new Zend_Log_Filter_Priority(Zend_Log::WARN, '<=')); $this->_directives['logger'] = $logger; } /** * Log a message at the WARN (4) priority. * * @param string $message * @throws Zend_Cache_Exception * @return void */ protected function _log($message, $priority = 4) { if (!$this->_directives['logging']) { return; } if (!isset($this->_directives['logger'])) { Zend_Cache::throwException('Logging is enabled but logger is not set.'); } $logger = $this->_directives['logger']; if (!$logger instanceof Zend_Log) { Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.'); } $logger->log($message, $priority); } } Backend/Apc.php000064400000025451152101626120007312 0ustar00 infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $lifetime = $this->getLifetime($specificLifetime); $result = apc_store($id, array($data, time(), $lifetime), $lifetime); if (count($tags) > 0) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); } return $result; } /** * Remove a cache record * * @param string $id cache id * @return boolean true if no problem */ public function remove($id) { return apc_delete($id); } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => unsupported * 'matchingTag' => unsupported * 'notMatchingTag' => unsupported * 'matchingAnyTag' => unsupported * * @param string $mode clean mode * @param array $tags array of tags * @throws Zend_Cache_Exception * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: return apc_clear_cache('user'); break; case Zend_Cache::CLEANING_MODE_OLD: $this->_log("Zend_Cache_Backend_Apc::clean() : CLEANING_MODE_OLD is unsupported by the Apc backend"); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_APC_BACKEND); break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Return true if the automatic cleaning is available for the backend * * DEPRECATED : use getCapabilities() instead * * @deprecated * @return boolean */ public function isAutomaticCleaningAvailable() { return false; } /** * Return the filling percentage of the backend storage * * @throws Zend_Cache_Exception * @return int integer between 0 and 100 */ public function getFillingPercentage() { $mem = apc_sma_info(true); $memSize = $mem['num_seg'] * $mem['seg_size']; $memAvailable= $mem['avail_mem']; $memUsed = $memSize - $memAvailable; if ($memSize == 0) { Zend_Cache::throwException('can\'t get apc memory size'); } if ($memUsed > $memSize) { return 100; } return ((int) (100. * ($memUsed / $memSize))); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); return array(); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); return array(); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); return array(); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); return array(); } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { $ids = array(); $iterator = new APCIterator('user', null, APC_ITER_KEY); foreach ($iterator as $item) { $ids[] = $item['key']; } return $ids; } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { $tmp = apc_fetch($id); if (is_array($tmp)) { $data = $tmp[0]; $mtime = $tmp[1]; if (!isset($tmp[2])) { // because this record is only with 1.7 release // if old cache records are still there... return false; } $lifetime = $tmp[2]; return array( 'expire' => $mtime + $lifetime, 'tags' => array(), 'mtime' => $mtime ); } return false; } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { $tmp = apc_fetch($id); if (is_array($tmp)) { $data = $tmp[0]; $mtime = $tmp[1]; if (!isset($tmp[2])) { // because this record is only with 1.7 release // if old cache records are still there... return false; } $lifetime = $tmp[2]; $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime; if ($newLifetime <=0) { return false; } apc_store($id, array($data, time(), $newLifetime), $newLifetime); return true; } return false; } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => false, 'tags' => false, 'expired_read' => false, 'priority' => false, 'infinite_lifetime' => false, 'get_list' => true ); } } Backend/Xcache.php000064400000016215152101626120010000 0ustar00 (string) user : * xcache.admin.user (necessary for the clean() method) * * =====> (string) password : * xcache.admin.pass (clear, not MD5) (necessary for the clean() method) * * @var array available options */ protected $_options = array( 'user' => null, 'password' => null ); /** * Constructor * * @param array $options associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { if (!extension_loaded('xcache')) { Zend_Cache::throwException('The xcache extension must be loaded for using this backend !'); } parent::__construct($options); } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * WARNING $doNotTestCacheValidity=true is unsupported by the Xcache backend * * @param string $id cache id * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested * @return string cached datas (or false) */ public function load($id, $doNotTestCacheValidity = false) { if ($doNotTestCacheValidity) { $this->_log("Zend_Cache_Backend_Xcache::load() : \$doNotTestCacheValidity=true is unsupported by the Xcache backend"); } $tmp = xcache_get($id); if (is_array($tmp)) { return $tmp[0]; } return false; } /** * Test if a cache is available or not (for the given id) * * @param string $id cache id * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { if (xcache_isset($id)) { $tmp = xcache_get($id); if (is_array($tmp)) { return $tmp[1]; } } return false; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data datas to cache * @param string $id cache id * @param array $tags array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime if != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $lifetime = $this->getLifetime($specificLifetime); $result = xcache_set($id, array($data, time()), $lifetime); if (count($tags) > 0) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_XCACHE_BACKEND); } return $result; } /** * Remove a cache record * * @param string $id cache id * @return boolean true if no problem */ public function remove($id) { return xcache_unset($id); } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => unsupported * 'matchingTag' => unsupported * 'notMatchingTag' => unsupported * 'matchingAnyTag' => unsupported * * @param string $mode clean mode * @param array $tags array of tags * @throws Zend_Cache_Exception * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: // Necessary because xcache_clear_cache() need basic authentification $backup = array(); if (isset($_SERVER['PHP_AUTH_USER'])) { $backup['PHP_AUTH_USER'] = $_SERVER['PHP_AUTH_USER']; } if (isset($_SERVER['PHP_AUTH_PW'])) { $backup['PHP_AUTH_PW'] = $_SERVER['PHP_AUTH_PW']; } if ($this->_options['user']) { $_SERVER['PHP_AUTH_USER'] = $this->_options['user']; } if ($this->_options['password']) { $_SERVER['PHP_AUTH_PW'] = $this->_options['password']; } $cnt = xcache_count(XC_TYPE_VAR); for ($i=0; $i < $cnt; $i++) { xcache_clear_cache(XC_TYPE_VAR, $i); } if (isset($backup['PHP_AUTH_USER'])) { $_SERVER['PHP_AUTH_USER'] = $backup['PHP_AUTH_USER']; $_SERVER['PHP_AUTH_PW'] = $backup['PHP_AUTH_PW']; } return true; break; case Zend_Cache::CLEANING_MODE_OLD: $this->_log("Zend_Cache_Backend_Xcache::clean() : CLEANING_MODE_OLD is unsupported by the Xcache backend"); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_XCACHE_BACKEND); break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Return true if the automatic cleaning is available for the backend * * @return boolean */ public function isAutomaticCleaningAvailable() { return false; } } Backend/Interface.php000064400000007417152101626120010511 0ustar00 infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false); /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id); /** * Clean some cache records * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode Clean mode * @param array $tags Array of tags * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()); } Backend/File.php000064400000105647152101626120007474 0ustar00 (string) cache_dir : * - Directory where to put the cache files * * =====> (boolean) file_locking : * - Enable / disable file_locking * - Can avoid cache corruption under bad circumstances but it doesn't work on multithread * webservers and on NFS filesystems for example * * =====> (boolean) read_control : * - Enable / disable read control * - If enabled, a control key is embeded in cache file and this key is compared with the one * calculated after the reading. * * =====> (string) read_control_type : * - Type of read control (only if read control is enabled). Available values are : * 'md5' for a md5 hash control (best but slowest) * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice) * 'adler32' for an adler32 hash control (excellent choice too, faster than crc32) * 'strlen' for a length only test (fastest) * * =====> (int) hashed_directory_level : * - Hashed directory level * - Set the hashed directory structure level. 0 means "no hashed directory * structure", 1 means "one level of directory", 2 means "two levels"... * This option can speed up the cache only when you have many thousands of * cache file. Only specific benchs can help you to choose the perfect value * for you. Maybe, 1 or 2 is a good start. * * =====> (int) hashed_directory_umask : * - deprecated * - Permissions for hashed directory structure * * =====> (int) hashed_directory_perm : * - Permissions for hashed directory structure * * =====> (string) file_name_prefix : * - prefix for cache files * - be really carefull with this option because a too generic value in a system cache dir * (like /tmp) can cause disasters when cleaning the cache * * =====> (int) cache_file_umask : * - deprecated * - Permissions for cache files * * =====> (int) cache_file_perm : * - Permissions for cache files * * =====> (int) metatadatas_array_max_size : * - max size for the metadatas array (don't change this value unless you * know what you are doing) * * @var array available options */ protected $_options = array( 'cache_dir' => null, 'file_locking' => true, 'read_control' => true, 'read_control_type' => 'crc32', 'hashed_directory_level' => 0, 'hashed_directory_perm' => 0700, 'file_name_prefix' => 'zend_cache', 'cache_file_perm' => 0600, 'metadatas_array_max_size' => 100 ); /** * Array of metadatas (each item is an associative array) * * @var array */ protected $_metadatasArray = array(); /** * Constructor * * @param array $options associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { parent::__construct($options); if ($this->_options['cache_dir'] !== null) { // particular case for this option $this->setCacheDir($this->_options['cache_dir']); } else { $this->setCacheDir(self::getTmpDir() . DIRECTORY_SEPARATOR, false); } if (isset($this->_options['file_name_prefix'])) { // particular case for this option if (!preg_match('~^[a-zA-Z0-9_]+$~D', $this->_options['file_name_prefix'])) { Zend_Cache::throwException('Invalid file_name_prefix : must use only [a-zA-Z0-9_]'); } } if ($this->_options['metadatas_array_max_size'] < 10) { Zend_Cache::throwException('Invalid metadatas_array_max_size, must be > 10'); } if (isset($options['hashed_directory_umask'])) { // See #ZF-12047 trigger_error("'hashed_directory_umask' is deprecated -> please use 'hashed_directory_perm' instead", E_USER_NOTICE); if (!isset($options['hashed_directory_perm'])) { $options['hashed_directory_perm'] = $options['hashed_directory_umask']; } } if (isset($options['hashed_directory_perm']) && is_string($options['hashed_directory_perm'])) { // See #ZF-4422 $this->_options['hashed_directory_perm'] = octdec($this->_options['hashed_directory_perm']); } if (isset($options['cache_file_umask'])) { // See #ZF-12047 trigger_error("'cache_file_umask' is deprecated -> please use 'cache_file_perm' instead", E_USER_NOTICE); if (!isset($options['cache_file_perm'])) { $options['cache_file_perm'] = $options['cache_file_umask']; } } if (isset($options['cache_file_perm']) && is_string($options['cache_file_perm'])) { // See #ZF-4422 $this->_options['cache_file_perm'] = octdec($this->_options['cache_file_perm']); } } /** * Set the cache_dir (particular case of setOption() method) * * @param string $value * @param boolean $trailingSeparator If true, add a trailing separator is necessary * @throws Zend_Cache_Exception * @return void */ public function setCacheDir($value, $trailingSeparator = true) { if (!is_dir($value)) { Zend_Cache::throwException(sprintf('cache_dir "%s" must be a directory', $value)); } if (!is_writable($value)) { Zend_Cache::throwException(sprintf('cache_dir "%s" is not writable', $value)); } if ($trailingSeparator) { // add a trailing DIRECTORY_SEPARATOR if necessary $value = rtrim(realpath($value), '\\/') . DIRECTORY_SEPARATOR; } $this->_options['cache_dir'] = $value; } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id cache id * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested * @return string|false cached datas */ public function load($id, $doNotTestCacheValidity = false) { if (!($this->_test($id, $doNotTestCacheValidity))) { // The cache is not hit ! return false; } $metadatas = $this->_getMetadatas($id); $file = $this->_file($id); $data = $this->_fileGetContents($file); if ($this->_options['read_control']) { $hashData = $this->_hash($data, $this->_options['read_control_type']); $hashControl = $metadatas['hash']; if ($hashData != $hashControl) { // Problem detected by the read control ! $this->_log('Zend_Cache_Backend_File::load() / read_control : stored hash and computed hash do not match'); $this->remove($id); return false; } } return $data; } /** * Test if a cache is available or not (for the given id) * * @param string $id cache id * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { clearstatcache(); return $this->_test($id, false); } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { clearstatcache(); $file = $this->_file($id); $path = $this->_path($id); if ($this->_options['hashed_directory_level'] > 0) { if (!is_writable($path)) { // maybe, we just have to build the directory structure $this->_recursiveMkdirAndChmod($id); } if (!is_writable($path)) { return false; } } if ($this->_options['read_control']) { $hash = $this->_hash($data, $this->_options['read_control_type']); } else { $hash = ''; } $metadatas = array( 'hash' => $hash, 'mtime' => time(), 'expire' => $this->_expireTime($this->getLifetime($specificLifetime)), 'tags' => $tags ); $res = $this->_setMetadatas($id, $metadatas); if (!$res) { $this->_log('Zend_Cache_Backend_File::save() / error on saving metadata'); return false; } $res = $this->_filePutContents($file, $data); return $res; } /** * Remove a cache record * * @param string $id cache id * @return boolean true if no problem */ public function remove($id) { $file = $this->_file($id); $boolRemove = $this->_remove($file); $boolMetadata = $this->_delMetadatas($id); return $boolMetadata && $boolRemove; } /** * Clean some cache records * * Available modes are : * * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode clean mode * @param tags array $tags array of tags * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { // We use this protected method to hide the recursive stuff clearstatcache(); return $this->_clean($this->_options['cache_dir'], $mode, $tags); } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { return $this->_get($this->_options['cache_dir'], 'ids', array()); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { return $this->_get($this->_options['cache_dir'], 'tags', array()); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { return $this->_get($this->_options['cache_dir'], 'matching', $tags); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { return $this->_get($this->_options['cache_dir'], 'notMatching', $tags); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { return $this->_get($this->_options['cache_dir'], 'matchingAny', $tags); } /** * Return the filling percentage of the backend storage * * @throws Zend_Cache_Exception * @return int integer between 0 and 100 */ public function getFillingPercentage() { $free = disk_free_space($this->_options['cache_dir']); $total = disk_total_space($this->_options['cache_dir']); if ($total == 0) { Zend_Cache::throwException('can\'t get disk_total_space'); } else { if ($free >= $total) { return 100; } return ((int) (100. * ($total - $free) / $total)); } } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { $metadatas = $this->_getMetadatas($id); if (!$metadatas) { return false; } if (time() > $metadatas['expire']) { return false; } return array( 'expire' => $metadatas['expire'], 'tags' => $metadatas['tags'], 'mtime' => $metadatas['mtime'] ); } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { $metadatas = $this->_getMetadatas($id); if (!$metadatas) { return false; } if (time() > $metadatas['expire']) { return false; } $newMetadatas = array( 'hash' => $metadatas['hash'], 'mtime' => time(), 'expire' => $metadatas['expire'] + $extraLifetime, 'tags' => $metadatas['tags'] ); $res = $this->_setMetadatas($id, $newMetadatas); if (!$res) { return false; } return true; } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => true, 'tags' => true, 'expired_read' => true, 'priority' => false, 'infinite_lifetime' => true, 'get_list' => true ); } /** * PUBLIC METHOD FOR UNIT TESTING ONLY ! * * Force a cache record to expire * * @param string $id cache id */ public function ___expire($id) { $metadatas = $this->_getMetadatas($id); if ($metadatas) { $metadatas['expire'] = 1; $this->_setMetadatas($id, $metadatas); } } /** * Get a metadatas record * * @param string $id Cache id * @return array|false Associative array of metadatas */ protected function _getMetadatas($id) { if (isset($this->_metadatasArray[$id])) { return $this->_metadatasArray[$id]; } else { $metadatas = $this->_loadMetadatas($id); if (!$metadatas) { return false; } $this->_setMetadatas($id, $metadatas, false); return $metadatas; } } /** * Set a metadatas record * * @param string $id Cache id * @param array $metadatas Associative array of metadatas * @param boolean $save optional pass false to disable saving to file * @return boolean True if no problem */ protected function _setMetadatas($id, $metadatas, $save = true) { if (count($this->_metadatasArray) >= $this->_options['metadatas_array_max_size']) { $n = (int) ($this->_options['metadatas_array_max_size'] / 10); $this->_metadatasArray = array_slice($this->_metadatasArray, $n); } if ($save) { $result = $this->_saveMetadatas($id, $metadatas); if (!$result) { return false; } } $this->_metadatasArray[$id] = $metadatas; return true; } /** * Drop a metadata record * * @param string $id Cache id * @return boolean True if no problem */ protected function _delMetadatas($id) { if (isset($this->_metadatasArray[$id])) { unset($this->_metadatasArray[$id]); } $file = $this->_metadatasFile($id); return $this->_remove($file); } /** * Clear the metadatas array * * @return void */ protected function _cleanMetadatas() { $this->_metadatasArray = array(); } /** * Load metadatas from disk * * @param string $id Cache id * @return array|false Metadatas associative array */ protected function _loadMetadatas($id) { $file = $this->_metadatasFile($id); $result = $this->_fileGetContents($file); if (!$result) { return false; } $tmp = @unserialize($result); return $tmp; } /** * Save metadatas to disk * * @param string $id Cache id * @param array $metadatas Associative array * @return boolean True if no problem */ protected function _saveMetadatas($id, $metadatas) { $file = $this->_metadatasFile($id); $result = $this->_filePutContents($file, serialize($metadatas)); if (!$result) { return false; } return true; } /** * Make and return a file name (with path) for metadatas * * @param string $id Cache id * @return string Metadatas file name (with path) */ protected function _metadatasFile($id) { $path = $this->_path($id); $fileName = $this->_idToFileName('internal-metadatas---' . $id); return $path . $fileName; } /** * Check if the given filename is a metadatas one * * @param string $fileName File name * @return boolean True if it's a metadatas one */ protected function _isMetadatasFile($fileName) { $id = $this->_fileNameToId($fileName); if (substr($id, 0, 21) == 'internal-metadatas---') { return true; } else { return false; } } /** * Remove a file * * If we can't remove the file (because of locks or any problem), we will touch * the file to invalidate it * * @param string $file Complete file path * @return boolean True if ok */ protected function _remove($file) { if (!is_file($file)) { return false; } if (!@unlink($file)) { # we can't remove the file (because of locks or any problem) $this->_log("Zend_Cache_Backend_File::_remove() : we can't remove $file"); return false; } return true; } /** * Clean some cache records (protected method used for recursive stuff) * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $dir Directory to clean * @param string $mode Clean mode * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return boolean True if no problem */ protected function _clean($dir, $mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { if (!is_dir($dir)) { return false; } $result = true; $prefix = $this->_options['file_name_prefix']; $glob = @glob($dir . $prefix . '--*'); if ($glob === false) { // On some systems it is impossible to distinguish between empty match and an error. return true; } foreach ($glob as $file) { if (is_file($file)) { $fileName = basename($file); if ($this->_isMetadatasFile($fileName)) { // in CLEANING_MODE_ALL, we drop anything, even remainings old metadatas files if ($mode != Zend_Cache::CLEANING_MODE_ALL) { continue; } } $id = $this->_fileNameToId($fileName); $metadatas = $this->_getMetadatas($id); if ($metadatas === FALSE) { $metadatas = array('expire' => 1, 'tags' => array()); } switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: $res = $this->remove($id); if (!$res) { // in this case only, we accept a problem with the metadatas file drop $res = $this->_remove($file); } $result = $result && $res; break; case Zend_Cache::CLEANING_MODE_OLD: if (time() > $metadatas['expire']) { $result = $this->remove($id) && $result; } break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: $matching = true; foreach ($tags as $tag) { if (!in_array($tag, $metadatas['tags'])) { $matching = false; break; } } if ($matching) { $result = $this->remove($id) && $result; } break; case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: $matching = false; foreach ($tags as $tag) { if (in_array($tag, $metadatas['tags'])) { $matching = true; break; } } if (!$matching) { $result = $this->remove($id) && $result; } break; case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $matching = false; foreach ($tags as $tag) { if (in_array($tag, $metadatas['tags'])) { $matching = true; break; } } if ($matching) { $result = $this->remove($id) && $result; } break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) { // Recursive call $result = $this->_clean($file . DIRECTORY_SEPARATOR, $mode, $tags) && $result; if ($mode == Zend_Cache::CLEANING_MODE_ALL) { // we try to drop the structure too @rmdir($file); } } } return $result; } protected function _get($dir, $mode, $tags = array()) { if (!is_dir($dir)) { return false; } $result = array(); $prefix = $this->_options['file_name_prefix']; $glob = @glob($dir . $prefix . '--*'); if ($glob === false) { // On some systems it is impossible to distinguish between empty match and an error. return array(); } foreach ($glob as $file) { if (is_file($file)) { $fileName = basename($file); $id = $this->_fileNameToId($fileName); $metadatas = $this->_getMetadatas($id); if ($metadatas === FALSE) { continue; } if (time() > $metadatas['expire']) { continue; } switch ($mode) { case 'ids': $result[] = $id; break; case 'tags': $result = array_unique(array_merge($result, $metadatas['tags'])); break; case 'matching': $matching = true; foreach ($tags as $tag) { if (!in_array($tag, $metadatas['tags'])) { $matching = false; break; } } if ($matching) { $result[] = $id; } break; case 'notMatching': $matching = false; foreach ($tags as $tag) { if (in_array($tag, $metadatas['tags'])) { $matching = true; break; } } if (!$matching) { $result[] = $id; } break; case 'matchingAny': $matching = false; foreach ($tags as $tag) { if (in_array($tag, $metadatas['tags'])) { $matching = true; break; } } if ($matching) { $result[] = $id; } break; default: Zend_Cache::throwException('Invalid mode for _get() method'); break; } } if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) { // Recursive call $recursiveRs = $this->_get($file . DIRECTORY_SEPARATOR, $mode, $tags); if ($recursiveRs === false) { $this->_log('Zend_Cache_Backend_File::_get() / recursive call : can\'t list entries of "'.$file.'"'); } else { $result = array_unique(array_merge($result, $recursiveRs)); } } } return array_unique($result); } /** * Compute & return the expire time * * @return int expire time (unix timestamp) */ protected function _expireTime($lifetime) { if ($lifetime === null) { return 9999999999; } return time() + $lifetime; } /** * Make a control key with the string containing datas * * @param string $data Data * @param string $controlType Type of control 'md5', 'crc32' or 'strlen' * @throws Zend_Cache_Exception * @return string Control key */ protected function _hash($data, $controlType) { switch ($controlType) { case 'md5': return md5($data); case 'crc32': return crc32($data); case 'strlen': return strlen($data); case 'adler32': return hash('adler32', $data); default: Zend_Cache::throwException("Incorrect hash function : $controlType"); } } /** * Transform a cache id into a file name and return it * * @param string $id Cache id * @return string File name */ protected function _idToFileName($id) { $prefix = $this->_options['file_name_prefix']; $result = $prefix . '---' . $id; return $result; } /** * Make and return a file name (with path) * * @param string $id Cache id * @return string File name (with path) */ protected function _file($id) { $path = $this->_path($id); $fileName = $this->_idToFileName($id); return $path . $fileName; } /** * Return the complete directory path of a filename (including hashedDirectoryStructure) * * @param string $id Cache id * @param boolean $parts if true, returns array of directory parts instead of single string * @return string Complete directory path */ protected function _path($id, $parts = false) { $partsArray = array(); $root = $this->_options['cache_dir']; $prefix = $this->_options['file_name_prefix']; if ($this->_options['hashed_directory_level']>0) { $hash = hash('adler32', $id); for ($i=0 ; $i < $this->_options['hashed_directory_level'] ; $i++) { $root = $root . $prefix . '--' . substr($hash, 0, $i + 1) . DIRECTORY_SEPARATOR; $partsArray[] = $root; } } if ($parts) { return $partsArray; } else { return $root; } } /** * Make the directory strucuture for the given id * * @param string $id cache id * @return boolean true */ protected function _recursiveMkdirAndChmod($id) { if ($this->_options['hashed_directory_level'] <=0) { return true; } $partsArray = $this->_path($id, true); foreach ($partsArray as $part) { if (!is_dir($part)) { @mkdir($part, $this->_options['hashed_directory_perm']); @chmod($part, $this->_options['hashed_directory_perm']); // see #ZF-320 (this line is required in some configurations) } } return true; } /** * Test if the given cache id is available (and still valid as a cache record) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return boolean|mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ protected function _test($id, $doNotTestCacheValidity) { $metadatas = $this->_getMetadatas($id); if (!$metadatas) { return false; } if ($doNotTestCacheValidity || (time() <= $metadatas['expire'])) { return $metadatas['mtime']; } return false; } /** * Return the file content of the given file * * @param string $file File complete path * @return string File content (or false if problem) */ protected function _fileGetContents($file) { $result = false; if (!is_file($file)) { return false; } $f = @fopen($file, 'rb'); if ($f) { if ($this->_options['file_locking']) @flock($f, LOCK_SH); $result = stream_get_contents($f); if ($this->_options['file_locking']) @flock($f, LOCK_UN); @fclose($f); } return $result; } /** * Put the given string into the given file * * @param string $file File complete path * @param string $string String to put in file * @return boolean true if no problem */ protected function _filePutContents($file, $string) { $result = false; $f = @fopen($file, 'ab+'); if ($f) { if ($this->_options['file_locking']) @flock($f, LOCK_EX); fseek($f, 0); ftruncate($f, 0); $tmp = @fwrite($f, $string); if (!($tmp === FALSE)) { $result = true; } @fclose($f); } @chmod($file, $this->_options['cache_file_perm']); return $result; } /** * Transform a file name into cache id and return it * * @param string $fileName File name * @return string Cache id */ protected function _fileNameToId($fileName) { $prefix = $this->_options['file_name_prefix']; return preg_replace('~^' . $prefix . '---(.*)$~', '$1', $fileName); } } Backend/Memcached.php000064400000042673152101626120010462 0ustar00 (array) servers : * an array of memcached server ; each memcached server is described by an associative array : * 'host' => (string) : the name of the memcached server * 'port' => (int) : the port of the memcached server * 'persistent' => (bool) : use or not persistent connections to this memcached server * 'weight' => (int) : number of buckets to create for this server which in turn control its * probability of it being selected. The probability is relative to the total * weight of all servers. * 'timeout' => (int) : value in seconds which will be used for connecting to the daemon. Think twice * before changing the default value of 1 second - you can lose all the * advantages of caching if your connection is too slow. * 'retry_interval' => (int) : controls how often a failed server will be retried, the default value * is 15 seconds. Setting this parameter to -1 disables automatic retry. * 'status' => (bool) : controls if the server should be flagged as online. * 'failure_callback' => (callback) : Allows the user to specify a callback function to run upon * encountering an error. The callback is run before failover * is attempted. The function takes two parameters, the hostname * and port of the failed server. * * =====> (boolean) compression : * true if you want to use on-the-fly compression * * =====> (boolean) compatibility : * true if you use old memcache server or extension * * @var array available options */ protected $_options = array( 'servers' => array(array( 'host' => self::DEFAULT_HOST, 'port' => self::DEFAULT_PORT, 'persistent' => self::DEFAULT_PERSISTENT, 'weight' => self::DEFAULT_WEIGHT, 'timeout' => self::DEFAULT_TIMEOUT, 'retry_interval' => self::DEFAULT_RETRY_INTERVAL, 'status' => self::DEFAULT_STATUS, 'failure_callback' => self::DEFAULT_FAILURE_CALLBACK )), 'compression' => false, 'compatibility' => false, ); /** * Memcache object * * @var mixed memcache object */ protected $_memcache = null; /** * Constructor * * @param array $options associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { if (!extension_loaded('memcache')) { Zend_Cache::throwException('The memcache extension must be loaded for using this backend !'); } parent::__construct($options); if (isset($this->_options['servers'])) { $value= $this->_options['servers']; if (isset($value['host'])) { // in this case, $value seems to be a simple associative array (one server only) $value = array(0 => $value); // let's transform it into a classical array of associative arrays } $this->setOption('servers', $value); } $this->_memcache = new Memcache; foreach ($this->_options['servers'] as $server) { if (!array_key_exists('port', $server)) { $server['port'] = self::DEFAULT_PORT; } if (!array_key_exists('persistent', $server)) { $server['persistent'] = self::DEFAULT_PERSISTENT; } if (!array_key_exists('weight', $server)) { $server['weight'] = self::DEFAULT_WEIGHT; } if (!array_key_exists('timeout', $server)) { $server['timeout'] = self::DEFAULT_TIMEOUT; } if (!array_key_exists('retry_interval', $server)) { $server['retry_interval'] = self::DEFAULT_RETRY_INTERVAL; } if (!array_key_exists('status', $server)) { $server['status'] = self::DEFAULT_STATUS; } if (!array_key_exists('failure_callback', $server)) { $server['failure_callback'] = self::DEFAULT_FAILURE_CALLBACK; } if ($this->_options['compatibility']) { // No status for compatibility mode (#ZF-5887) $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'], $server['weight'], $server['timeout'], $server['retry_interval']); } else { $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'], $server['weight'], $server['timeout'], $server['retry_interval'], $server['status'], $server['failure_callback']); } } } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string|false cached datas */ public function load($id, $doNotTestCacheValidity = false) { $tmp = $this->_memcache->get($id); if (is_array($tmp) && isset($tmp[0])) { return $tmp[0]; } return false; } /** * Test if a cache is available or not (for the given id) * * @param string $id Cache id * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { $tmp = $this->_memcache->get($id); if (is_array($tmp)) { return $tmp[1]; } return false; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean True if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $lifetime = $this->getLifetime($specificLifetime); if ($this->_options['compression']) { $flag = MEMCACHE_COMPRESSED; } else { $flag = 0; } // ZF-8856: using set because add needs a second request if item already exists $result = @$this->_memcache->set($id, array($data, time(), $lifetime), $flag, $lifetime); if (count($tags) > 0) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); } return $result; } /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id) { return $this->_memcache->delete($id, 0); } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => unsupported * 'matchingTag' => unsupported * 'notMatchingTag' => unsupported * 'matchingAnyTag' => unsupported * * @param string $mode Clean mode * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return boolean True if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: return $this->_memcache->flush(); break; case Zend_Cache::CLEANING_MODE_OLD: $this->_log("Zend_Cache_Backend_Memcached::clean() : CLEANING_MODE_OLD is unsupported by the Memcached backend"); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_MEMCACHED_BACKEND); break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Return true if the automatic cleaning is available for the backend * * @return boolean */ public function isAutomaticCleaningAvailable() { return false; } /** * Set the frontend directives * * @param array $directives Assoc of directives * @throws Zend_Cache_Exception * @return void */ public function setDirectives($directives) { parent::setDirectives($directives); $lifetime = $this->getLifetime(false); if ($lifetime > 2592000) { // #ZF-3490 : For the memcached backend, there is a lifetime limit of 30 days (2592000 seconds) $this->_log('memcached backend has a limit of 30 days (2592000 seconds) for the lifetime'); } if ($lifetime === null) { // #ZF-4614 : we tranform null to zero to get the maximal lifetime parent::setDirectives(array('lifetime' => 0)); } } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { $this->_log("Zend_Cache_Backend_Memcached::save() : getting the list of cache ids is unsupported by the Memcache backend"); return array(); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); return array(); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); return array(); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); return array(); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); return array(); } /** * Return the filling percentage of the backend storage * * @throws Zend_Cache_Exception * @return int integer between 0 and 100 */ public function getFillingPercentage() { $mems = $this->_memcache->getExtendedStats(); $memSize = null; $memUsed = null; foreach ($mems as $key => $mem) { if ($mem === false) { $this->_log('can\'t get stat from ' . $key); continue; } $eachSize = $mem['limit_maxbytes']; $eachUsed = $mem['bytes']; if ($eachUsed > $eachSize) { $eachUsed = $eachSize; } $memSize += $eachSize; $memUsed += $eachUsed; } if ($memSize === null || $memUsed === null) { Zend_Cache::throwException('Can\'t get filling percentage'); } return ((int) (100. * ($memUsed / $memSize))); } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { $tmp = $this->_memcache->get($id); if (is_array($tmp)) { $data = $tmp[0]; $mtime = $tmp[1]; if (!isset($tmp[2])) { // because this record is only with 1.7 release // if old cache records are still there... return false; } $lifetime = $tmp[2]; return array( 'expire' => $mtime + $lifetime, 'tags' => array(), 'mtime' => $mtime ); } return false; } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { if ($this->_options['compression']) { $flag = MEMCACHE_COMPRESSED; } else { $flag = 0; } $tmp = $this->_memcache->get($id); if (is_array($tmp)) { $data = $tmp[0]; $mtime = $tmp[1]; if (!isset($tmp[2])) { // because this record is only with 1.7 release // if old cache records are still there... return false; } $lifetime = $tmp[2]; $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime; if ($newLifetime <=0) { return false; } // #ZF-5702 : we try replace() first becase set() seems to be slower if (!($result = $this->_memcache->replace($id, array($data, time(), $newLifetime), $flag, $newLifetime))) { $result = $this->_memcache->set($id, array($data, time(), $newLifetime), $flag, $newLifetime); } return $result; } return false; } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => false, 'tags' => false, 'expired_read' => false, 'priority' => false, 'infinite_lifetime' => false, 'get_list' => false ); } } Backend/ExtendedInterface.php000064400000007615152101626120012172 0ustar00 (string) slow_backend : * - Slow backend name * - Must implement the Zend_Cache_Backend_ExtendedInterface * - Should provide a big storage * * =====> (string) fast_backend : * - Flow backend name * - Must implement the Zend_Cache_Backend_ExtendedInterface * - Must be much faster than slow_backend * * =====> (array) slow_backend_options : * - Slow backend options (see corresponding backend) * * =====> (array) fast_backend_options : * - Fast backend options (see corresponding backend) * * =====> (int) stats_update_factor : * - Disable / Tune the computation of the fast backend filling percentage * - When saving a record into cache : * 1 => systematic computation of the fast backend filling percentage * x (integer) > 1 => computation of the fast backend filling percentage randomly 1 times on x cache write * * =====> (boolean) slow_backend_custom_naming : * =====> (boolean) fast_backend_custom_naming : * =====> (boolean) slow_backend_autoload : * =====> (boolean) fast_backend_autoload : * - See Zend_Cache::factory() method * * =====> (boolean) auto_refresh_fast_cache * - If true, auto refresh the fast cache when a cache record is hit * * @var array available options */ protected $_options = array( 'slow_backend' => 'File', 'fast_backend' => 'Apc', 'slow_backend_options' => array(), 'fast_backend_options' => array(), 'stats_update_factor' => 10, 'slow_backend_custom_naming' => false, 'fast_backend_custom_naming' => false, 'slow_backend_autoload' => false, 'fast_backend_autoload' => false, 'auto_refresh_fast_cache' => true ); /** * Slow Backend * * @var Zend_Cache_Backend_ExtendedInterface */ protected $_slowBackend; /** * Fast Backend * * @var Zend_Cache_Backend_ExtendedInterface */ protected $_fastBackend; /** * Cache for the fast backend filling percentage * * @var int */ protected $_fastBackendFillingPercentage = null; /** * Constructor * * @param array $options Associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { parent::__construct($options); if ($this->_options['slow_backend'] === null) { Zend_Cache::throwException('slow_backend option has to set'); } elseif ($this->_options['slow_backend'] instanceof Zend_Cache_Backend_ExtendedInterface) { $this->_slowBackend = $this->_options['slow_backend']; } else { $this->_slowBackend = Zend_Cache::_makeBackend( $this->_options['slow_backend'], $this->_options['slow_backend_options'], $this->_options['slow_backend_custom_naming'], $this->_options['slow_backend_autoload'] ); if (!in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_slowBackend))) { Zend_Cache::throwException('slow_backend must implement the Zend_Cache_Backend_ExtendedInterface interface'); } } if ($this->_options['fast_backend'] === null) { Zend_Cache::throwException('fast_backend option has to set'); } elseif ($this->_options['fast_backend'] instanceof Zend_Cache_Backend_ExtendedInterface) { $this->_fastBackend = $this->_options['fast_backend']; } else { $this->_fastBackend = Zend_Cache::_makeBackend( $this->_options['fast_backend'], $this->_options['fast_backend_options'], $this->_options['fast_backend_custom_naming'], $this->_options['fast_backend_autoload'] ); if (!in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_fastBackend))) { Zend_Cache::throwException('fast_backend must implement the Zend_Cache_Backend_ExtendedInterface interface'); } } $this->_slowBackend->setDirectives($this->_directives); $this->_fastBackend->setDirectives($this->_directives); } /** * Test if a cache is available or not (for the given id) * * @param string $id cache id * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { $fastTest = $this->_fastBackend->test($id); if ($fastTest) { return $fastTest; } else { return $this->_slowBackend->test($id); } } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false, $priority = 8) { $usage = $this->_getFastFillingPercentage('saving'); $boolFast = true; $lifetime = $this->getLifetime($specificLifetime); $preparedData = $this->_prepareData($data, $lifetime, $priority); if (($priority > 0) && (10 * $priority >= $usage)) { $fastLifetime = $this->_getFastLifetime($lifetime, $priority); $boolFast = $this->_fastBackend->save($preparedData, $id, array(), $fastLifetime); $boolSlow = $this->_slowBackend->save($preparedData, $id, $tags, $lifetime); } else { $boolSlow = $this->_slowBackend->save($preparedData, $id, $tags, $lifetime); if ($boolSlow === true) { $boolFast = $this->_fastBackend->remove($id); if (!$boolFast && !$this->_fastBackend->test($id)) { // some backends return false on remove() even if the key never existed. (and it won't if fast is full) // all we care about is that the key doesn't exist now $boolFast = true; } } } return ($boolFast && $boolSlow); } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * Note : return value is always "string" (unserialization is done by the core not by the backend) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string|false cached datas */ public function load($id, $doNotTestCacheValidity = false) { $res = $this->_fastBackend->load($id, $doNotTestCacheValidity); if ($res === false) { $res = $this->_slowBackend->load($id, $doNotTestCacheValidity); if ($res === false) { // there is no cache at all for this id return false; } } $array = unserialize($res); // maybe, we have to refresh the fast cache ? if ($this->_options['auto_refresh_fast_cache']) { if ($array['priority'] == 10) { // no need to refresh the fast cache with priority = 10 return $array['data']; } $newFastLifetime = $this->_getFastLifetime($array['lifetime'], $array['priority'], time() - $array['expire']); // we have the time to refresh the fast cache $usage = $this->_getFastFillingPercentage('loading'); if (($array['priority'] > 0) && (10 * $array['priority'] >= $usage)) { // we can refresh the fast cache $preparedData = $this->_prepareData($array['data'], $array['lifetime'], $array['priority']); $this->_fastBackend->save($preparedData, $id, array(), $newFastLifetime); } } return $array['data']; } /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id) { $boolFast = $this->_fastBackend->remove($id); $boolSlow = $this->_slowBackend->remove($id); return $boolFast && $boolSlow; } /** * Clean some cache records * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode Clean mode * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch($mode) { case Zend_Cache::CLEANING_MODE_ALL: $boolFast = $this->_fastBackend->clean(Zend_Cache::CLEANING_MODE_ALL); $boolSlow = $this->_slowBackend->clean(Zend_Cache::CLEANING_MODE_ALL); return $boolFast && $boolSlow; break; case Zend_Cache::CLEANING_MODE_OLD: return $this->_slowBackend->clean(Zend_Cache::CLEANING_MODE_OLD); case Zend_Cache::CLEANING_MODE_MATCHING_TAG: $ids = $this->_slowBackend->getIdsMatchingTags($tags); $res = true; foreach ($ids as $id) { $bool = $this->remove($id); $res = $res && $bool; } return $res; break; case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: $ids = $this->_slowBackend->getIdsNotMatchingTags($tags); $res = true; foreach ($ids as $id) { $bool = $this->remove($id); $res = $res && $bool; } return $res; break; case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $ids = $this->_slowBackend->getIdsMatchingAnyTags($tags); $res = true; foreach ($ids as $id) { $bool = $this->remove($id); $res = $res && $bool; } return $res; break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { return $this->_slowBackend->getIds(); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { return $this->_slowBackend->getTags(); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { return $this->_slowBackend->getIdsMatchingTags($tags); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { return $this->_slowBackend->getIdsNotMatchingTags($tags); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { return $this->_slowBackend->getIdsMatchingAnyTags($tags); } /** * Return the filling percentage of the backend storage * * @return int integer between 0 and 100 */ public function getFillingPercentage() { return $this->_slowBackend->getFillingPercentage(); } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { return $this->_slowBackend->getMetadatas($id); } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { return $this->_slowBackend->touch($id, $extraLifetime); } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { $slowBackendCapabilities = $this->_slowBackend->getCapabilities(); return array( 'automatic_cleaning' => $slowBackendCapabilities['automatic_cleaning'], 'tags' => $slowBackendCapabilities['tags'], 'expired_read' => $slowBackendCapabilities['expired_read'], 'priority' => $slowBackendCapabilities['priority'], 'infinite_lifetime' => $slowBackendCapabilities['infinite_lifetime'], 'get_list' => $slowBackendCapabilities['get_list'] ); } /** * Prepare a serialized array to store datas and metadatas informations * * @param string $data data to store * @param int $lifetime original lifetime * @param int $priority priority * @return string serialize array to store into cache */ private function _prepareData($data, $lifetime, $priority) { $lt = $lifetime; if ($lt === null) { $lt = 9999999999; } return serialize(array( 'data' => $data, 'lifetime' => $lifetime, 'expire' => time() + $lt, 'priority' => $priority )); } /** * Compute and return the lifetime for the fast backend * * @param int $lifetime original lifetime * @param int $priority priority * @param int $maxLifetime maximum lifetime * @return int lifetime for the fast backend */ private function _getFastLifetime($lifetime, $priority, $maxLifetime = null) { if ($lifetime <= 0) { // if no lifetime, we have an infinite lifetime // we need to use arbitrary lifetimes $fastLifetime = (int) (2592000 / (11 - $priority)); } else { // prevent computed infinite lifetime (0) by ceil $fastLifetime = (int) ceil($lifetime / (11 - $priority)); } if ($maxLifetime >= 0 && $fastLifetime > $maxLifetime) { return $maxLifetime; } return $fastLifetime; } /** * PUBLIC METHOD FOR UNIT TESTING ONLY ! * * Force a cache record to expire * * @param string $id cache id */ public function ___expire($id) { $this->_fastBackend->remove($id); $this->_slowBackend->___expire($id); } private function _getFastFillingPercentage($mode) { if ($mode == 'saving') { // mode saving if ($this->_fastBackendFillingPercentage === null) { $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage(); } else { $rand = rand(1, $this->_options['stats_update_factor']); if ($rand == 1) { // we force a refresh $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage(); } } } else { // mode loading // we compute the percentage only if it's not available in cache if ($this->_fastBackendFillingPercentage === null) { $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage(); } } return $this->_fastBackendFillingPercentage; } } Frontend/Page.php000064400000033765152101626120007722 0ustar00 (boolean) http_conditional : * - if true, http conditional mode is on * WARNING : http_conditional OPTION IS NOT IMPLEMENTED FOR THE MOMENT (TODO) * * ====> (boolean) debug_header : * - if true, a debug text is added before each cached pages * * ====> (boolean) content_type_memorization : * - deprecated => use memorize_headers instead * - if the Content-Type header is sent after the cache was started, the * corresponding value can be memorized and replayed when the cache is hit * (if false (default), the frontend doesn't take care of Content-Type header) * * ====> (array) memorize_headers : * - an array of strings corresponding to some HTTP headers name. Listed headers * will be stored with cache datas and "replayed" when the cache is hit * * ====> (array) default_options : * - an associative array of default options : * - (boolean) cache : cache is on by default if true * - (boolean) cacheWithXXXVariables (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') : * if true, cache is still on even if there are some variables in this superglobal array * if false, cache is off if there are some variables in this superglobal array * - (boolean) makeIdWithXXXVariables (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') : * if true, we have to use the content of this superglobal array to make a cache id * if false, the cache id won't be dependent of the content of this superglobal array * - (int) specific_lifetime : cache specific lifetime * (false => global lifetime is used, null => infinite lifetime, * integer => this lifetime is used), this "lifetime" is probably only * usefull when used with "regexps" array * - (array) tags : array of tags (strings) * - (int) priority : integer between 0 (very low priority) and 10 (maximum priority) used by * some particular backends * * ====> (array) regexps : * - an associative array to set options only for some REQUEST_URI * - keys are (pcre) regexps * - values are associative array with specific options to set if the regexp matchs on $_SERVER['REQUEST_URI'] * (see default_options for the list of available options) * - if several regexps match the $_SERVER['REQUEST_URI'], only the last one will be used * * @var array options */ protected $_specificOptions = array( 'http_conditional' => false, 'debug_header' => false, 'content_type_memorization' => false, 'memorize_headers' => array(), 'default_options' => array( 'cache_with_get_variables' => false, 'cache_with_post_variables' => false, 'cache_with_session_variables' => false, 'cache_with_files_variables' => false, 'cache_with_cookie_variables' => false, 'make_id_with_get_variables' => true, 'make_id_with_post_variables' => true, 'make_id_with_session_variables' => true, 'make_id_with_files_variables' => true, 'make_id_with_cookie_variables' => true, 'cache' => true, 'specific_lifetime' => false, 'tags' => array(), 'priority' => null ), 'regexps' => array() ); /** * Internal array to store some options * * @var array associative array of options */ protected $_activeOptions = array(); /** * If true, the page won't be cached * * @var boolean */ protected $_cancel = false; /** * Constructor * * @param array $options Associative array of options * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $name = strtolower($name); switch ($name) { case 'regexps': $this->_setRegexps($value); break; case 'default_options': $this->_setDefaultOptions($value); break; case 'content_type_memorization': $this->_setContentTypeMemorization($value); break; default: $this->setOption($name, $value); } } if (isset($this->_specificOptions['http_conditional'])) { if ($this->_specificOptions['http_conditional']) { Zend_Cache::throwException('http_conditional is not implemented for the moment !'); } } $this->setOption('automatic_serialization', true); } /** * Specific setter for the 'default_options' option (with some additional tests) * * @param array $options Associative array * @throws Zend_Cache_Exception * @return void */ protected function _setDefaultOptions($options) { if (!is_array($options)) { Zend_Cache::throwException('default_options must be an array !'); } foreach ($options as $key=>$value) { if (!is_string($key)) { Zend_Cache::throwException("invalid option [$key] !"); } $key = strtolower($key); if (isset($this->_specificOptions['default_options'][$key])) { $this->_specificOptions['default_options'][$key] = $value; } } } /** * Set the deprecated contentTypeMemorization option * * @param boolean $value value * @return void * @deprecated */ protected function _setContentTypeMemorization($value) { $found = null; foreach ($this->_specificOptions['memorize_headers'] as $key => $value) { if (strtolower($value) == 'content-type') { $found = $key; } } if ($value) { if (!$found) { $this->_specificOptions['memorize_headers'][] = 'Content-Type'; } } else { if ($found) { unset($this->_specificOptions['memorize_headers'][$found]); } } } /** * Specific setter for the 'regexps' option (with some additional tests) * * @param array $options Associative array * @throws Zend_Cache_Exception * @return void */ protected function _setRegexps($regexps) { if (!is_array($regexps)) { Zend_Cache::throwException('regexps option must be an array !'); } foreach ($regexps as $regexp=>$conf) { if (!is_array($conf)) { Zend_Cache::throwException('regexps option must be an array of arrays !'); } $validKeys = array_keys($this->_specificOptions['default_options']); foreach ($conf as $key=>$value) { if (!is_string($key)) { Zend_Cache::throwException("unknown option [$key] !"); } $key = strtolower($key); if (!in_array($key, $validKeys)) { unset($regexps[$regexp][$key]); } } } $this->setOption('regexps', $regexps); } /** * Start the cache * * @param string $id (optional) A cache id (if you set a value here, maybe you have to use Output frontend instead) * @param boolean $doNotDie For unit testing only ! * @return boolean True if the cache is hit (false else) */ public function start($id = false, $doNotDie = false) { $this->_cancel = false; $lastMatchingRegexp = null; if (isset($_SERVER['REQUEST_URI'])) { foreach ($this->_specificOptions['regexps'] as $regexp => $conf) { if (preg_match("`$regexp`", $_SERVER['REQUEST_URI'])) { $lastMatchingRegexp = $regexp; } } } $this->_activeOptions = $this->_specificOptions['default_options']; if ($lastMatchingRegexp !== null) { $conf = $this->_specificOptions['regexps'][$lastMatchingRegexp]; foreach ($conf as $key=>$value) { $this->_activeOptions[$key] = $value; } } if (!($this->_activeOptions['cache'])) { return false; } if (!$id) { $id = $this->_makeId(); if (!$id) { return false; } } $array = $this->load($id); if ($array !== false) { $data = $array['data']; $headers = $array['headers']; if (!headers_sent()) { foreach ($headers as $key=>$headerCouple) { $name = $headerCouple[0]; $value = $headerCouple[1]; header("$name: $value"); } } if ($this->_specificOptions['debug_header']) { echo 'DEBUG HEADER : This is a cached page !'; } echo $data; if ($doNotDie) { return true; } die(); } ob_start(array($this, '_flush')); ob_implicit_flush(false); return false; } /** * Cancel the current caching process */ public function cancel() { $this->_cancel = true; } /** * callback for output buffering * (shouldn't really be called manually) * * @param string $data Buffered output * @return string Data to send to browser */ public function _flush($data) { if ($this->_cancel) { return $data; } $contentType = null; $storedHeaders = array(); $headersList = headers_list(); foreach($this->_specificOptions['memorize_headers'] as $key=>$headerName) { foreach ($headersList as $headerSent) { $tmp = explode(':', $headerSent); $headerSentName = trim(array_shift($tmp)); if (strtolower($headerName) == strtolower($headerSentName)) { $headerSentValue = trim(implode(':', $tmp)); $storedHeaders[] = array($headerSentName, $headerSentValue); } } } $array = array( 'data' => $data, 'headers' => $storedHeaders ); $this->save($array, null, $this->_activeOptions['tags'], $this->_activeOptions['specific_lifetime'], $this->_activeOptions['priority']); return $data; } /** * Make an id depending on REQUEST_URI and superglobal arrays (depending on options) * * @return mixed|false a cache id (string), false if the cache should have not to be used */ protected function _makeId() { $tmp = $_SERVER['REQUEST_URI']; $array = explode('?', $tmp, 2); $tmp = $array[0]; foreach (array('Get', 'Post', 'Session', 'Files', 'Cookie') as $arrayName) { $tmp2 = $this->_makePartialId($arrayName, $this->_activeOptions['cache_with_' . strtolower($arrayName) . '_variables'], $this->_activeOptions['make_id_with_' . strtolower($arrayName) . '_variables']); if ($tmp2===false) { return false; } $tmp = $tmp . $tmp2; } return md5($tmp); } /** * Make a partial id depending on options * * @param string $arrayName Superglobal array name * @param bool $bool1 If true, cache is still on even if there are some variables in the superglobal array * @param bool $bool2 If true, we have to use the content of the superglobal array to make a partial id * @return mixed|false Partial id (string) or false if the cache should have not to be used */ protected function _makePartialId($arrayName, $bool1, $bool2) { switch ($arrayName) { case 'Get': $var = $_GET; break; case 'Post': $var = $_POST; break; case 'Session': if (isset($_SESSION)) { $var = $_SESSION; } else { $var = null; } break; case 'Cookie': if (isset($_COOKIE)) { $var = $_COOKIE; } else { $var = null; } break; case 'Files': $var = $_FILES; break; default: return false; } if ($bool1) { if ($bool2) { return serialize($var); } return ''; } if (count($var) > 0) { return false; } return ''; } } Frontend/Output.php000064400000006534152101626120010340 0ustar00_idStack = array(); } /** * Start the cache * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @param boolean $echoData If set to true, datas are sent to the browser if the cache is hit (simply returned else) * @return mixed True if the cache is hit (false else) with $echoData=true (default) ; string else (datas) */ public function start($id, $doNotTestCacheValidity = false, $echoData = true) { $data = $this->load($id, $doNotTestCacheValidity); if ($data !== false) { if ( $echoData ) { echo($data); return true; } else { return $data; } } ob_start(); ob_implicit_flush(false); $this->_idStack[] = $id; return false; } /** * Stop the cache * * @param array $tags Tags array * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @param string $forcedDatas If not null, force written datas with this * @param boolean $echoData If set to true, datas are sent to the browser * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends * @return void */ public function end($tags = array(), $specificLifetime = false, $forcedDatas = null, $echoData = true, $priority = 8) { if ($forcedDatas === null) { $data = ob_get_clean(); } else { $data =& $forcedDatas; } $id = array_pop($this->_idStack); if ($id === null) { Zend_Cache::throwException('use of end() without a start()'); } $this->save($data, $id, $tags, $specificLifetime, $priority); if ($echoData) { echo($data); } } } Frontend/Capture.php000064400000004544152101626120010442 0ustar00_tags = $tags; $this->_extension = $extension; ob_start(array($this, '_flush')); ob_implicit_flush(false); $this->_idStack[] = $id; return false; } /** * callback for output buffering * (shouldn't really be called manually) * * @param string $data Buffered output * @return string Data to send to browser */ public function _flush($data) { $id = array_pop($this->_idStack); if ($id === null) { Zend_Cache::throwException('use of _flush() without a start()'); } if ($this->_extension) { $this->save(serialize(array($data, $this->_extension)), $id, $this->_tags); } else { $this->save($data, $id, $this->_tags); } return $data; } } Frontend/Class.php000064400000017244152101626120010105 0ustar00 (mixed) cached_entity : * - if set to a class name, we will cache an abstract class and will use only static calls * - if set to an object, we will cache this object methods * * ====> (boolean) cache_by_default : * - if true, method calls will be cached by default * * ====> (array) cached_methods : * - an array of method names which will be cached (even if cache_by_default = false) * * ====> (array) non_cached_methods : * - an array of method names which won't be cached (even if cache_by_default = true) * * @var array available options */ protected $_specificOptions = array( 'cached_entity' => null, 'cache_by_default' => true, 'cached_methods' => array(), 'non_cached_methods' => array() ); /** * Tags array * * @var array */ protected $_tags = array(); /** * SpecificLifetime value * * false => no specific life time * * @var bool|int */ protected $_specificLifetime = false; /** * The cached object or the name of the cached abstract class * * @var mixed */ protected $_cachedEntity = null; /** * The class name of the cached object or cached abstract class * * Used to differentiate between different classes with the same method calls. * * @var string */ protected $_cachedEntityLabel = ''; /** * Priority (used by some particular backends) * * @var int */ protected $_priority = 8; /** * Constructor * * @param array $options Associative array of options * @throws Zend_Cache_Exception */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $this->setOption($name, $value); } if ($this->_specificOptions['cached_entity'] === null) { Zend_Cache::throwException('cached_entity must be set !'); } $this->setCachedEntity($this->_specificOptions['cached_entity']); $this->setOption('automatic_serialization', true); } /** * Set a specific life time * * @param bool|int $specificLifetime * @return void */ public function setSpecificLifetime($specificLifetime = false) { $this->_specificLifetime = $specificLifetime; } /** * Set the priority (used by some particular backends) * * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) */ public function setPriority($priority) { $this->_priority = $priority; } /** * Public frontend to set an option * * Just a wrapper to get a specific behaviour for cached_entity * * @param string $name Name of the option * @param mixed $value Value of the option * @throws Zend_Cache_Exception * @return void */ public function setOption($name, $value) { if ($name == 'cached_entity') { $this->setCachedEntity($value); } else { parent::setOption($name, $value); } } /** * Specific method to set the cachedEntity * * if set to a class name, we will cache an abstract class and will use only static calls * if set to an object, we will cache this object methods * * @param mixed $cachedEntity */ public function setCachedEntity($cachedEntity) { if (!is_string($cachedEntity) && !is_object($cachedEntity)) { Zend_Cache::throwException( 'cached_entity must be an object or a class name' ); } $this->_cachedEntity = $cachedEntity; $this->_specificOptions['cached_entity'] = $cachedEntity; if (is_string($this->_cachedEntity)) { $this->_cachedEntityLabel = $this->_cachedEntity; } else { $ro = new ReflectionObject($this->_cachedEntity); $this->_cachedEntityLabel = $ro->getName(); } } /** * Set the cache array * * @param array $tags * @return void */ public function setTagsArray($tags = array()) { $this->_tags = $tags; } /** * Main method : call the specified method or get the result from cache * * @param string $name Method name * @param array $parameters Method parameters * @return mixed Result * @throws Exception */ public function __call($name, $parameters) { $callback = array($this->_cachedEntity, $name); if (!is_callable($callback, false)) { Zend_Cache::throwException('Invalid callback'); } $cacheBool1 = $this->_specificOptions['cache_by_default']; $cacheBool2 = in_array($name, $this->_specificOptions['cached_methods']); $cacheBool3 = in_array($name, $this->_specificOptions['non_cached_methods']); $cache = (($cacheBool1 || $cacheBool2) && (!$cacheBool3)); if (!$cache) { // We do not have not cache return call_user_func_array($callback, $parameters); } $id = $this->makeId($name, $parameters); if (($rs = $this->load($id)) && (array_key_exists(0, $rs)) && (array_key_exists(1, $rs)) ) { // A cache is available $output = $rs[0]; $return = $rs[1]; } else { // A cache is not available (or not valid for this frontend) ob_start(); ob_implicit_flush(false); try { $return = call_user_func_array($callback, $parameters); $output = ob_get_clean(); $data = array($output, $return); $this->save( $data, $id, $this->_tags, $this->_specificLifetime, $this->_priority ); } catch (Exception $e) { ob_end_clean(); throw $e; } } echo $output; return $return; } /** * ZF-9970 * * @deprecated */ private function _makeId($name, $args) { return $this->makeId($name, $args); } /** * Make a cache id from the method name and parameters * * @param string $name Method name * @param array $args Method parameters * @return string Cache id */ public function makeId($name, array $args = array()) { return md5($this->_cachedEntityLabel . '__' . $name . '__' . serialize($args)); } } Frontend/File.php000064400000015366152101626120007722 0ustar00 (string) master_file : * - a complete path of the master file * - deprecated (see master_files) * * ====> (array) master_files : * - an array of complete path of master files * - this option has to be set ! * * ====> (string) master_files_mode : * - Zend_Cache_Frontend_File::MODE_AND or Zend_Cache_Frontend_File::MODE_OR * - if MODE_AND, then all master files have to be touched to get a cache invalidation * - if MODE_OR (default), then a single touched master file is enough to get a cache invalidation * * ====> (boolean) ignore_missing_master_files * - if set to true, missing master files are ignored silently * - if set to false (default), an exception is thrown if there is a missing master file * @var array available options */ protected $_specificOptions = array( 'master_file' => null, 'master_files' => null, 'master_files_mode' => 'OR', 'ignore_missing_master_files' => false ); /** * Master file mtimes * * Array of int * * @var array */ private $_masterFile_mtimes = null; /** * Constructor * * @param array $options Associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $this->setOption($name, $value); } if (!isset($this->_specificOptions['master_files'])) { Zend_Cache::throwException('master_files option must be set'); } } /** * Change the master_files option * * @param array $masterFiles the complete paths and name of the master files */ public function setMasterFiles(array $masterFiles) { $this->_specificOptions['master_file'] = null; // to keep a compatibility $this->_specificOptions['master_files'] = null; $this->_masterFile_mtimes = array(); clearstatcache(); $i = 0; foreach ($masterFiles as $masterFile) { if (file_exists($masterFile)) { $mtime = filemtime($masterFile); } else { $mtime = false; } if (!$this->_specificOptions['ignore_missing_master_files'] && !$mtime) { Zend_Cache::throwException('Unable to read master_file : ' . $masterFile); } $this->_masterFile_mtimes[$i] = $mtime; $this->_specificOptions['master_files'][$i] = $masterFile; if ($i === 0) { // to keep a compatibility $this->_specificOptions['master_file'] = $masterFile; } $i++; } } /** * Change the master_file option * * To keep the compatibility * * @deprecated * @param string $masterFile the complete path and name of the master file */ public function setMasterFile($masterFile) { $this->setMasterFiles(array($masterFile)); } /** * Public frontend to set an option * * Just a wrapper to get a specific behaviour for master_file * * @param string $name Name of the option * @param mixed $value Value of the option * @throws Zend_Cache_Exception * @return void */ public function setOption($name, $value) { if ($name == 'master_file') { $this->setMasterFile($value); } else if ($name == 'master_files') { $this->setMasterFiles($value); } else { parent::setOption($name, $value); } } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @param boolean $doNotUnserialize Do not serialize (even if automatic_serialization is true) => for internal use * @return mixed|false Cached datas */ public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false) { if (!$doNotTestCacheValidity) { if ($this->test($id)) { return parent::load($id, true, $doNotUnserialize); } return false; } return parent::load($id, true, $doNotUnserialize); } /** * Test if a cache is available for the given id * * @param string $id Cache id * @return int|false Last modified time of cache entry if it is available, false otherwise */ public function test($id) { $lastModified = parent::test($id); if ($lastModified) { if ($this->_specificOptions['master_files_mode'] == self::MODE_AND) { // MODE_AND foreach($this->_masterFile_mtimes as $masterFileMTime) { if ($masterFileMTime) { if ($lastModified > $masterFileMTime) { return $lastModified; } } } } else { // MODE_OR $res = true; foreach($this->_masterFile_mtimes as $masterFileMTime) { if ($masterFileMTime) { if ($lastModified <= $masterFileMTime) { return false; } } } return $lastModified; } } return false; } } Frontend/Function.php000064400000013607152101626120010624 0ustar00 (boolean) cache_by_default : * - if true, function calls will be cached by default * * ====> (array) cached_functions : * - an array of function names which will be cached (even if cache_by_default = false) * * ====> (array) non_cached_functions : * - an array of function names which won't be cached (even if cache_by_default = true) * * @var array options */ protected $_specificOptions = array( 'cache_by_default' => true, 'cached_functions' => array(), 'non_cached_functions' => array() ); /** * Constructor * * @param array $options Associative array of options * @return void */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $this->setOption($name, $value); } $this->setOption('automatic_serialization', true); } /** * Main method : call the specified function or get the result from cache * * @param callback $callback A valid callback * @param array $parameters Function parameters * @param array $tags Cache tags * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends * @return mixed Result */ public function call($callback, array $parameters = array(), $tags = array(), $specificLifetime = false, $priority = 8) { if (!is_callable($callback, true, $name)) { Zend_Cache::throwException('Invalid callback'); } $cacheBool1 = $this->_specificOptions['cache_by_default']; $cacheBool2 = in_array($name, $this->_specificOptions['cached_functions']); $cacheBool3 = in_array($name, $this->_specificOptions['non_cached_functions']); $cache = (($cacheBool1 || $cacheBool2) && (!$cacheBool3)); if (!$cache) { // Caching of this callback is disabled return call_user_func_array($callback, $parameters); } $id = $this->_makeId($callback, $parameters); if ( ($rs = $this->load($id)) && isset($rs[0], $rs[1])) { // A cache is available $output = $rs[0]; $return = $rs[1]; } else { // A cache is not available (or not valid for this frontend) ob_start(); ob_implicit_flush(false); $return = call_user_func_array($callback, $parameters); $output = ob_get_clean(); $data = array($output, $return); $this->save($data, $id, $tags, $specificLifetime, $priority); } echo $output; return $return; } /** * ZF-9970 * * @deprecated */ private function _makeId($callback, array $args) { return $this->makeId($callback, $args); } /** * Make a cache id from the function name and parameters * * @param callback $callback A valid callback * @param array $args Function parameters * @throws Zend_Cache_Exception * @return string Cache id */ public function makeId($callback, array $args = array()) { if (!is_callable($callback, true, $name)) { Zend_Cache::throwException('Invalid callback'); } // functions, methods and classnames are case-insensitive $name = strtolower($name); // generate a unique id for object callbacks if (is_object($callback)) { // Closures & __invoke $object = $callback; } elseif (isset($callback[0])) { // array($object, 'method') $object = $callback[0]; } if (isset($object)) { try { $tmp = @serialize($callback); } catch (Exception $e) { Zend_Cache::throwException($e->getMessage()); } if (!$tmp) { $lastErr = error_get_last(); Zend_Cache::throwException("Can't serialize callback object to generate id: {$lastErr['message']}"); } $name.= '__' . $tmp; } // generate a unique id for arguments $argsStr = ''; if ($args) { try { $argsStr = @serialize(array_values($args)); } catch (Exception $e) { Zend_Cache::throwException($e->getMessage()); } if (!$argsStr) { $lastErr = error_get_last(); throw Zend_Cache::throwException("Can't serialize arguments to generate id: {$lastErr['message']}"); } } return md5($name . $argsStr); } } Core.php000064400000062331152101626120006146 0ustar00 (boolean) write_control : * - Enable / disable write control (the cache is read just after writing to detect corrupt entries) * - Enable write control will lightly slow the cache writing but not the cache reading * Write control can detect some corrupt cache files but maybe it's not a perfect control * * ====> (boolean) caching : * - Enable / disable caching * (can be very useful for the debug of cached scripts) * * =====> (string) cache_id_prefix : * - prefix for cache ids (namespace) * * ====> (boolean) automatic_serialization : * - Enable / disable automatic serialization * - It can be used to save directly datas which aren't strings (but it's slower) * * ====> (int) automatic_cleaning_factor : * - Disable / Tune the automatic cleaning process * - The automatic cleaning process destroy too old (for the given life time) * cache files when a new cache file is written : * 0 => no automatic cache cleaning * 1 => systematic cache cleaning * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write * * ====> (int) lifetime : * - Cache lifetime (in seconds) * - If null, the cache is valid forever. * * ====> (boolean) logging : * - If set to true, logging is activated (but the system is slower) * * ====> (boolean) ignore_user_abort * - If set to true, the core will set the ignore_user_abort PHP flag inside the * save() method to avoid cache corruptions in some cases (default false) * * @var array $_options available options */ protected $_options = array( 'write_control' => true, 'caching' => true, 'cache_id_prefix' => null, 'automatic_serialization' => false, 'automatic_cleaning_factor' => 10, 'lifetime' => 3600, 'logging' => false, 'logger' => null, 'ignore_user_abort' => false ); /** * Array of options which have to be transfered to backend * * @var array $_directivesList */ protected static $_directivesList = array('lifetime', 'logging', 'logger'); /** * Not used for the core, just a sort a hint to get a common setOption() method (for the core and for frontends) * * @var array $_specificOptions */ protected $_specificOptions = array(); /** * Last used cache id * * @var string $_lastId */ private $_lastId = null; /** * True if the backend implements Zend_Cache_Backend_ExtendedInterface * * @var boolean $_extendedBackend */ protected $_extendedBackend = false; /** * Array of capabilities of the backend (only if it implements Zend_Cache_Backend_ExtendedInterface) * * @var array */ protected $_backendCapabilities = array(); /** * Constructor * * @param array|Zend_Config $options Associative array of options or Zend_Config instance * @throws Zend_Cache_Exception * @return void */ public function __construct($options = array()) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } if (!is_array($options)) { Zend_Cache::throwException("Options passed were not an array" . " or Zend_Config instance."); } while (list($name, $value) = each($options)) { $this->setOption($name, $value); } $this->_loggerSanity(); } /** * Set options using an instance of type Zend_Config * * @param Zend_Config $config * @return Zend_Cache_Core */ public function setConfig(Zend_Config $config) { $options = $config->toArray(); while (list($name, $value) = each($options)) { $this->setOption($name, $value); } return $this; } /** * Set the backend * * @param Zend_Cache_Backend $backendObject * @throws Zend_Cache_Exception * @return void */ public function setBackend(Zend_Cache_Backend $backendObject) { $this->_backend= $backendObject; // some options (listed in $_directivesList) have to be given // to the backend too (even if they are not "backend specific") $directives = array(); foreach (Zend_Cache_Core::$_directivesList as $directive) { $directives[$directive] = $this->_options[$directive]; } $this->_backend->setDirectives($directives); if (in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_backend))) { $this->_extendedBackend = true; $this->_backendCapabilities = $this->_backend->getCapabilities(); } } /** * Returns the backend * * @return Zend_Cache_Backend backend object */ public function getBackend() { return $this->_backend; } /** * Public frontend to set an option * * There is an additional validation (relatively to the protected _setOption method) * * @param string $name Name of the option * @param mixed $value Value of the option * @throws Zend_Cache_Exception * @return void */ public function setOption($name, $value) { if (!is_string($name)) { Zend_Cache::throwException("Incorrect option name!"); } $name = strtolower($name); if (array_key_exists($name, $this->_options)) { // This is a Core option $this->_setOption($name, $value); return; } if (array_key_exists($name, $this->_specificOptions)) { // This a specic option of this frontend $this->_specificOptions[$name] = $value; return; } } /** * Public frontend to get an option value * * @param string $name Name of the option * @throws Zend_Cache_Exception * @return mixed option value */ public function getOption($name) { $name = strtolower($name); if (array_key_exists($name, $this->_options)) { // This is a Core option return $this->_options[$name]; } if (array_key_exists($name, $this->_specificOptions)) { // This a specic option of this frontend return $this->_specificOptions[$name]; } Zend_Cache::throwException("Incorrect option name : $name"); } /** * Set an option * * @param string $name Name of the option * @param mixed $value Value of the option * @throws Zend_Cache_Exception * @return void */ private function _setOption($name, $value) { if (!is_string($name) || !array_key_exists($name, $this->_options)) { Zend_Cache::throwException("Incorrect option name : $name"); } if ($name == 'lifetime' && empty($value)) { $value = null; } $this->_options[$name] = $value; } /** * Force a new lifetime * * The new value is set for the core/frontend but for the backend too (directive) * * @param int $newLifetime New lifetime (in seconds) * @return void */ public function setLifetime($newLifetime) { $this->_options['lifetime'] = $newLifetime; $this->_backend->setDirectives(array( 'lifetime' => $newLifetime )); } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @param boolean $doNotUnserialize Do not serialize (even if automatic_serialization is true) => for internal use * @return mixed|false Cached datas */ public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false) { if (!$this->_options['caching']) { return false; } $id = $this->_id($id); // cache id may need prefix $this->_lastId = $id; self::_validateIdOrTag($id); $this->_log("Zend_Cache_Core: load item '{$id}'", 7); $data = $this->_backend->load($id, $doNotTestCacheValidity); if ($data===false) { // no cache available return false; } if ((!$doNotUnserialize) && $this->_options['automatic_serialization']) { // we need to unserialize before sending the result return unserialize($data); } return $data; } /** * Test if a cache is available for the given id * * @param string $id Cache id * @return int|false Last modified time of cache entry if it is available, false otherwise */ public function test($id) { if (!$this->_options['caching']) { return false; } $id = $this->_id($id); // cache id may need prefix self::_validateIdOrTag($id); $this->_lastId = $id; $this->_log("Zend_Cache_Core: test item '{$id}'", 7); return $this->_backend->test($id); } /** * Save some data in a cache * * @param mixed $data Data to put in cache (can be another type than string if automatic_serialization is on) * @param string $id Cache id (if not set, the last cache id will be used) * @param array $tags Cache tags * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends * @throws Zend_Cache_Exception * @return boolean True if no problem */ public function save($data, $id = null, $tags = array(), $specificLifetime = false, $priority = 8) { if (!$this->_options['caching']) { return true; } if ($id === null) { $id = $this->_lastId; } else { $id = $this->_id($id); } self::_validateIdOrTag($id); self::_validateTagsArray($tags); if ($this->_options['automatic_serialization']) { // we need to serialize datas before storing them $data = serialize($data); } else { if (!is_string($data)) { Zend_Cache::throwException("Datas must be string or set automatic_serialization = true"); } } // automatic cleaning if ($this->_options['automatic_cleaning_factor'] > 0) { $rand = rand(1, $this->_options['automatic_cleaning_factor']); if ($rand==1) { // new way || deprecated way if ($this->_extendedBackend || method_exists($this->_backend, 'isAutomaticCleaningAvailable')) { $this->_log("Zend_Cache_Core::save(): automatic cleaning running", 7); $this->clean(Zend_Cache::CLEANING_MODE_OLD); } else { $this->_log("Zend_Cache_Core::save(): automatic cleaning is not available/necessary with current backend", 4); } } } $this->_log("Zend_Cache_Core: save item '{$id}'", 7); if ($this->_options['ignore_user_abort']) { $abort = ignore_user_abort(true); } if (($this->_extendedBackend) && ($this->_backendCapabilities['priority'])) { $result = $this->_backend->save($data, $id, $tags, $specificLifetime, $priority); } else { $result = $this->_backend->save($data, $id, $tags, $specificLifetime); } if ($this->_options['ignore_user_abort']) { ignore_user_abort($abort); } if (!$result) { // maybe the cache is corrupted, so we remove it ! $this->_log("Zend_Cache_Core::save(): failed to save item '{$id}' -> removing it", 4); $this->_backend->remove($id); return false; } if ($this->_options['write_control']) { $data2 = $this->_backend->load($id, true); if ($data!=$data2) { $this->_log("Zend_Cache_Core::save(): write control of item '{$id}' failed -> removing it", 4); $this->_backend->remove($id); return false; } } return true; } /** * Remove a cache * * @param string $id Cache id to remove * @return boolean True if ok */ public function remove($id) { if (!$this->_options['caching']) { return true; } $id = $this->_id($id); // cache id may need prefix self::_validateIdOrTag($id); $this->_log("Zend_Cache_Core: remove item '{$id}'", 7); return $this->_backend->remove($id); } /** * Clean cache entries * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => remove too old cache entries ($tags is not used) * 'matchingTag' => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * 'notMatchingTag' => remove cache entries not matching one of the given tags * ($tags can be an array of strings or a single string) * 'matchingAnyTag' => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode * @param array|string $tags * @throws Zend_Cache_Exception * @return boolean True if ok */ public function clean($mode = 'all', $tags = array()) { if (!$this->_options['caching']) { return true; } if (!in_array($mode, array(Zend_Cache::CLEANING_MODE_ALL, Zend_Cache::CLEANING_MODE_OLD, Zend_Cache::CLEANING_MODE_MATCHING_TAG, Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG, Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG))) { Zend_Cache::throwException('Invalid cleaning mode'); } self::_validateTagsArray($tags); return $this->_backend->clean($mode, $tags); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } if (!($this->_backendCapabilities['tags'])) { Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG); } $ids = $this->_backend->getIdsMatchingTags($tags); // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600) if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') { $prefix = & $this->_options['cache_id_prefix']; $prefixLen = strlen($prefix); foreach ($ids as &$id) { if (strpos($id, $prefix) === 0) { $id = substr($id, $prefixLen); } } } return $ids; } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } if (!($this->_backendCapabilities['tags'])) { Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG); } $ids = $this->_backend->getIdsNotMatchingTags($tags); // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600) if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') { $prefix = & $this->_options['cache_id_prefix']; $prefixLen = strlen($prefix); foreach ($ids as &$id) { if (strpos($id, $prefix) === 0) { $id = substr($id, $prefixLen); } } } return $ids; } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of matching any cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } if (!($this->_backendCapabilities['tags'])) { Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG); } $ids = $this->_backend->getIdsMatchingAnyTags($tags); // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600) if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') { $prefix = & $this->_options['cache_id_prefix']; $prefixLen = strlen($prefix); foreach ($ids as &$id) { if (strpos($id, $prefix) === 0) { $id = substr($id, $prefixLen); } } } return $ids; } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } $ids = $this->_backend->getIds(); // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600) if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') { $prefix = & $this->_options['cache_id_prefix']; $prefixLen = strlen($prefix); foreach ($ids as &$id) { if (strpos($id, $prefix) === 0) { $id = substr($id, $prefixLen); } } } return $ids; } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } if (!($this->_backendCapabilities['tags'])) { Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG); } return $this->_backend->getTags(); } /** * Return the filling percentage of the backend storage * * @return int integer between 0 and 100 */ public function getFillingPercentage() { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } return $this->_backend->getFillingPercentage(); } /** * Return an array of metadatas for the given cache id * * The array will include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } $id = $this->_id($id); // cache id may need prefix return $this->_backend->getMetadatas($id); } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } $id = $this->_id($id); // cache id may need prefix $this->_log("Zend_Cache_Core: touch item '{$id}'", 7); return $this->_backend->touch($id, $extraLifetime); } /** * Validate a cache id or a tag (security, reliable filenames, reserved prefixes...) * * Throw an exception if a problem is found * * @param string $string Cache id or tag * @throws Zend_Cache_Exception * @return void */ protected static function _validateIdOrTag($string) { if (!is_string($string)) { Zend_Cache::throwException('Invalid id or tag : must be a string'); } if (substr($string, 0, 9) == 'internal-') { Zend_Cache::throwException('"internal-*" ids or tags are reserved'); } if (!preg_match('~^[a-zA-Z0-9_]+$~D', $string)) { Zend_Cache::throwException("Invalid id or tag '$string' : must use only [a-zA-Z0-9_]"); } } /** * Validate a tags array (security, reliable filenames, reserved prefixes...) * * Throw an exception if a problem is found * * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return void */ protected static function _validateTagsArray($tags) { if (!is_array($tags)) { Zend_Cache::throwException('Invalid tags array : must be an array'); } foreach($tags as $tag) { self::_validateIdOrTag($tag); } reset($tags); } /** * Make sure if we enable logging that the Zend_Log class * is available. * Create a default log object if none is set. * * @throws Zend_Cache_Exception * @return void */ protected function _loggerSanity() { if (!isset($this->_options['logging']) || !$this->_options['logging']) { return; } if (isset($this->_options['logger']) && $this->_options['logger'] instanceof Zend_Log) { return; } // Create a default logger to the standard output stream //--//require_once 'Zend/Log.php'; //--//require_once 'Zend/Log/Writer/Stream.php'; //--//require_once 'Zend/Log/Filter/Priority.php'; $logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output')); $logger->addFilter(new Zend_Log_Filter_Priority(Zend_Log::WARN, '<=')); $this->_options['logger'] = $logger; } /** * Log a message at the WARN (4) priority. * * @param string $message * @throws Zend_Cache_Exception * @return void */ protected function _log($message, $priority = 4) { if (!$this->_options['logging']) { return; } if (!(isset($this->_options['logger']) || $this->_options['logger'] instanceof Zend_Log)) { Zend_Cache::throwException('Logging is enabled but logger is not set'); } $logger = $this->_options['logger']; $logger->log($message, $priority); } /** * Make and return a cache id * * Checks 'cache_id_prefix' and returns new id with prefix or simply the id if null * * @param string $id Cache id * @return string Cache id (with or without prefix) */ protected function _id($id) { if (($id !== null) && isset($this->_options['cache_id_prefix'])) { return $this->_options['cache_id_prefix'] . $id; // return with prefix } return $id; // no prefix, just return the $id passed } } Exception.php000064400000002000152101626120007177 0ustar00