From 8ca6cc32b2c789a3149861159ad258f2cb9491e3 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 14:39:39 +0200 Subject: Adding upstream version 2.11.4. Signed-off-by: Daniel Baumann --- library/vendor/Zend/Cache/Backend/Static.php | 577 +++++++++++++++++++++++++++ 1 file changed, 577 insertions(+) create mode 100644 library/vendor/Zend/Cache/Backend/Static.php (limited to 'library/vendor/Zend/Cache/Backend/Static.php') diff --git a/library/vendor/Zend/Cache/Backend/Static.php b/library/vendor/Zend/Cache/Backend/Static.php new file mode 100644 index 0000000..9203020 --- /dev/null +++ b/library/vendor/Zend/Cache/Backend/Static.php @@ -0,0 +1,577 @@ + null, + 'sub_dir' => 'html', + 'file_extension' => '.html', + 'index_filename' => 'index', + 'file_locking' => true, + 'cache_file_perm' => 0600, + 'cache_directory_perm' => 0700, + 'debug_header' => false, + 'tag_cache' => null, + 'disable_caching' => false + ); + + /** + * Cache for handling tags + * @var Zend_Cache_Core + */ + protected $_tagCache = null; + + /** + * Tagged items + * @var array + */ + protected $_tagged = null; + + /** + * Interceptor child method to handle the case where an Inner + * Cache object is being set since it's not supported by the + * standard backend interface + * + * @param string $name + * @param mixed $value + * @return Zend_Cache_Backend_Static + */ + public function setOption($name, $value) + { + if ($name == 'tag_cache') { + $this->setInnerCache($value); + } else { + // See #ZF-12047 and #GH-91 + if ($name == 'cache_file_umask') { + trigger_error( + "'cache_file_umask' is deprecated -> please use 'cache_file_perm' instead", + E_USER_NOTICE + ); + + $name = 'cache_file_perm'; + } + if ($name == 'cache_directory_umask') { + trigger_error( + "'cache_directory_umask' is deprecated -> please use 'cache_directory_perm' instead", + E_USER_NOTICE + ); + + $name = 'cache_directory_perm'; + } + + parent::setOption($name, $value); + } + return $this; + } + + /** + * Retrieve any option via interception of the parent's statically held + * options including the local option for a tag cache. + * + * @param string $name + * @return mixed + */ + public function getOption($name) + { + $name = strtolower($name); + + if ($name == 'tag_cache') { + return $this->getInnerCache(); + } + + return parent::getOption($name); + } + + /** + * 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) + { + if (($id = (string)$id) === '') { + $id = $this->_detectId(); + } else { + $id = $this->_decodeId($id); + } + if (!$this->_verifyPath($id)) { + Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path'); + } + if ($doNotTestCacheValidity) { + $this->_log("Zend_Cache_Backend_Static::load() : \$doNotTestCacheValidity=true is unsupported by the Static backend"); + } + + $fileName = basename($id); + if ($fileName === '') { + $fileName = $this->_options['index_filename']; + } + $pathName = $this->_options['public_dir'] . dirname($id); + $file = rtrim($pathName, '/') . '/' . $fileName . $this->_options['file_extension']; + if (file_exists($file)) { + $content = file_get_contents($file); + return $content; + } + + return false; + } + + /** + * Test if a cache is available or not (for the given id) + * + * @param string $id cache id + * @return bool + */ + public function test($id) + { + $id = $this->_decodeId($id); + if (!$this->_verifyPath($id)) { + Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path'); + } + + $fileName = basename($id); + if ($fileName === '') { + $fileName = $this->_options['index_filename']; + } + if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) { + $this->_tagged = $tagged; + } elseif (!$this->_tagged) { + return false; + } + $pathName = $this->_options['public_dir'] . dirname($id); + + // Switch extension if needed + if (isset($this->_tagged[$id])) { + $extension = $this->_tagged[$id]['extension']; + } else { + $extension = $this->_options['file_extension']; + } + $file = $pathName . '/' . $fileName . $extension; + if (file_exists($file)) { + return true; + } + 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) + { + if ($this->_options['disable_caching']) { + return true; + } + $extension = null; + if ($this->_isSerialized($data)) { + $data = unserialize($data); + $extension = '.' . ltrim($data[1], '.'); + $data = $data[0]; + } + + clearstatcache(); + if (($id = (string)$id) === '') { + $id = $this->_detectId(); + } else { + $id = $this->_decodeId($id); + } + + $fileName = basename($id); + if ($fileName === '') { + $fileName = $this->_options['index_filename']; + } + + $pathName = realpath($this->_options['public_dir']) . dirname($id); + $this->_createDirectoriesFor($pathName); + + if ($id === null || strlen($id) == 0) { + $dataUnserialized = unserialize($data); + $data = $dataUnserialized['data']; + } + $ext = $this->_options['file_extension']; + if ($extension) $ext = $extension; + $file = rtrim($pathName, '/') . '/' . $fileName . $ext; + if ($this->_options['file_locking']) { + $result = file_put_contents($file, $data, LOCK_EX); + } else { + $result = file_put_contents($file, $data); + } + @chmod($file, $this->_octdec($this->_options['cache_file_perm'])); + + if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) { + $this->_tagged = $tagged; + } elseif ($this->_tagged === null) { + $this->_tagged = array(); + } + if (!isset($this->_tagged[$id])) { + $this->_tagged[$id] = array(); + } + if (!isset($this->_tagged[$id]['tags'])) { + $this->_tagged[$id]['tags'] = array(); + } + $this->_tagged[$id]['tags'] = array_unique(array_merge($this->_tagged[$id]['tags'], $tags)); + $this->_tagged[$id]['extension'] = $ext; + $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME); + return (bool) $result; + } + + /** + * Recursively create the directories needed to write the static file + */ + protected function _createDirectoriesFor($path) + { + if (!is_dir($path)) { + $oldUmask = umask(0); + if ( !@mkdir($path, $this->_octdec($this->_options['cache_directory_perm']), true)) { + $lastErr = error_get_last(); + umask($oldUmask); + Zend_Cache::throwException("Can't create directory: {$lastErr['message']}"); + } + umask($oldUmask); + } + } + + /** + * Detect serialization of data (cannot predict since this is the only way + * to obey the interface yet pass in another parameter). + * + * In future, ZF 2.0, check if we can just avoid the interface restraints. + * + * This format is the only valid one possible for the class, so it's simple + * to just run a regular expression for the starting serialized format. + */ + protected function _isSerialized($data) + { + return preg_match("/a:2:\{i:0;s:\d+:\"/", $data); + } + + /** + * Remove a cache record + * + * @param string $id Cache id + * @return boolean True if no problem + */ + public function remove($id) + { + if (!$this->_verifyPath($id)) { + Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path'); + } + $fileName = basename($id); + if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) { + $this->_tagged = $tagged; + } elseif (!$this->_tagged) { + return false; + } + if (isset($this->_tagged[$id])) { + $extension = $this->_tagged[$id]['extension']; + } else { + $extension = $this->_options['file_extension']; + } + if ($fileName === '') { + $fileName = $this->_options['index_filename']; + } + $pathName = $this->_options['public_dir'] . dirname($id); + $file = realpath($pathName) . '/' . $fileName . $extension; + if (!file_exists($file)) { + return false; + } + return unlink($file); + } + + /** + * Remove a cache record recursively for the given directory matching a + * REQUEST_URI based relative path (deletes the actual file matching this + * in addition to the matching directory) + * + * @param string $id Cache id + * @return boolean True if no problem + */ + public function removeRecursively($id) + { + if (!$this->_verifyPath($id)) { + Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path'); + } + $fileName = basename($id); + if ($fileName === '') { + $fileName = $this->_options['index_filename']; + } + $pathName = $this->_options['public_dir'] . dirname($id); + $file = $pathName . '/' . $fileName . $this->_options['file_extension']; + $directory = $pathName . '/' . $fileName; + if (file_exists($directory)) { + if (!is_writable($directory)) { + return false; + } + if (is_dir($directory)) { + foreach (new DirectoryIterator($directory) as $file) { + if (true === $file->isFile()) { + if (false === unlink($file->getPathName())) { + return false; + } + } + } + } + rmdir($directory); + } + if (file_exists($file)) { + if (!is_writable($file)) { + return false; + } + return unlink($file); + } + return true; + } + + /** + * 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 + * @throws Zend_Exception + */ + public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) + { + $result = false; + switch ($mode) { + case Zend_Cache::CLEANING_MODE_MATCHING_TAG: + case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: + if (empty($tags)) { + throw new Zend_Exception('Cannot use tag matching modes as no tags were defined'); + } + if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) { + $this->_tagged = $tagged; + } elseif (!$this->_tagged) { + return true; + } + foreach ($tags as $tag) { + $urls = array_keys($this->_tagged); + foreach ($urls as $url) { + if (isset($this->_tagged[$url]['tags']) && in_array($tag, $this->_tagged[$url]['tags'])) { + $this->remove($url); + unset($this->_tagged[$url]); + } + } + } + $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME); + $result = true; + break; + case Zend_Cache::CLEANING_MODE_ALL: + if ($this->_tagged === null) { + $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME); + $this->_tagged = $tagged; + } + if ($this->_tagged === null || empty($this->_tagged)) { + return true; + } + $urls = array_keys($this->_tagged); + foreach ($urls as $url) { + $this->remove($url); + unset($this->_tagged[$url]); + } + $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME); + $result = true; + break; + case Zend_Cache::CLEANING_MODE_OLD: + $this->_log("Zend_Cache_Backend_Static : Selected Cleaning Mode Currently Unsupported By This Backend"); + break; + case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: + if (empty($tags)) { + throw new Zend_Exception('Cannot use tag matching modes as no tags were defined'); + } + if ($this->_tagged === null) { + $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME); + $this->_tagged = $tagged; + } + if ($this->_tagged === null || empty($this->_tagged)) { + return true; + } + $urls = array_keys($this->_tagged); + foreach ($urls as $url) { + $difference = array_diff($tags, $this->_tagged[$url]['tags']); + if (count($tags) == count($difference)) { + $this->remove($url); + unset($this->_tagged[$url]); + } + } + $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME); + $result = true; + break; + default: + Zend_Cache::throwException('Invalid mode for clean() method'); + break; + } + return $result; + } + + /** + * Set an Inner Cache, used here primarily to store Tags associated + * with caches created by this backend. Note: If Tags are lost, the cache + * should be completely cleaned as the mapping of tags to caches will + * have been irrevocably lost. + * + * @param Zend_Cache_Core + * @return void + */ + public function setInnerCache(Zend_Cache_Core $cache) + { + $this->_tagCache = $cache; + $this->_options['tag_cache'] = $cache; + } + + /** + * Get the Inner Cache if set + * + * @return Zend_Cache_Core + */ + public function getInnerCache() + { + if ($this->_tagCache === null) { + Zend_Cache::throwException('An Inner Cache has not been set; use setInnerCache()'); + } + return $this->_tagCache; + } + + /** + * Verify path exists and is non-empty + * + * @param string $path + * @return bool + */ + protected function _verifyPath($path) + { + $path = realpath($path); + $base = realpath($this->_options['public_dir']); + return strncmp($path, $base, strlen($base)) !== 0; + } + + /** + * Determine the page to save from the request + * + * @return string + */ + protected function _detectId() + { + return $_SERVER['REQUEST_URI']; + } + + /** + * 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 + * @deprecated Not usable until perhaps ZF 2.0 + */ + protected static function _validateIdOrTag($string) + { + if (!is_string($string)) { + Zend_Cache::throwException('Invalid id or tag : must be a string'); + } + + // Internal only checked in Frontend - not here! + if (substr($string, 0, 9) == 'internal-') { + return; + } + + // Validation assumes no query string, fragments or scheme included - only the path + if (!preg_match( + '/^(?:\/(?:(?:%[[:xdigit:]]{2}|[A-Za-z0-9-_.!~*\'()\[\]:@&=+$,;])*)?)+$/', + $string + ) + ) { + Zend_Cache::throwException("Invalid id or tag '$string' : must be a valid URL path"); + } + } + + /** + * Detect an octal string and return its octal value for file permission ops + * otherwise return the non-string (assumed octal or decimal int already) + * + * @param string $val The potential octal in need of conversion + * @return int + */ + protected function _octdec($val) + { + if (is_string($val) && decoct(octdec($val)) == $val) { + return octdec($val); + } + return $val; + } + + /** + * Decode a request URI from the provided ID + * + * @param string $id + * @return string + */ + protected function _decodeId($id) + { + return pack('H*', $id); + } +} -- cgit v1.2.3