Zend_Cache_Backend_File 注解

本文详细介绍了Zend_Cache_Backend_File类的功能与实现原理,包括缓存管理、文件操作、读取控制等核心机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
class Zend_Cache_Backend_File extends Zend_Cache_Backend implements Zend_Cache_Backend_Interface
{

    
// ------------------
    // --- Properties ---
    // ------------------


    
/**
     * Available options
     *
     * =====> (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)
     *   '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 :
     * - Umask 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
     *
     * @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_umask' => 0700,
        
'file_name_prefix' => 'zend_cache'
    );

    
/**
     * backward compatibility becase of ZF-879 and ZF-1172 (it will be removed in ZF 1.1)
     *
     * @var array
     
*/
    
protected $_backwardCompatibilityArray = array(
        
'cacheDir' => 'cache_dir',
        
'fileLocking' => 'file_locking',
        
'readControl' => 'read_control',
        
'readControlType' => 'read_control_type',
        
'hashedDirectoryLevel' => 'hashed_directory_level',
        
'hashedDirectoryUmask' => 'hashed_directory_umask',
        
'fileNamePrefix' => 'file_name_prefix'
    );

    
// ----------------------
    // --- Public methods ---
    // ----------------------


    
/**
     * Constructor
     *
     * @param array $options associative array of options
     
*/
    
public function __construct($options = array())
    {
        parent
::__construct($options);
        
if (!is_null($this->_options['cache_dir'])) { // particular case for this option
            $this->setCacheDir($this->_options['cache_dir']);
        } 
else {
            
$this->_options['cache_dir'= self::getTmpDir() . DIRECTORY_SEPARATOR;
        }
        
if (isset($this->_options['file_name_prefix'])) { // particular case for this option
            if (!preg_match('~^[w]+$~', $this->_options['file_name_prefix'])) {
                Zend_Cache
::throwException('Invalid file_name_prefix : must use only [a-zA-A0-9_]');
            }
        }
    }

    
/**
     * Set the cache_dir (particular case of setOption() method)
     *
     * @param mixed $value
     
*/
    
public function setCacheDir($value)
    {
        
// add a trailing DIRECTORY_SEPARATOR if necessary
        $value = rtrim($value, '//'. DIRECTORY_SEPARATOR;
        
$this->setOption('cache_dir', $value);
    }

    
/**
     * Test if a cache is available for the given id and (if yes) return it (false else)
     * 根据提供的缓存id测试缓存是否可用 如果可用返回 被缓存的数据
     *  
     * @param string $id cache id 缓存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 (!($this->_test($id, $doNotTestCacheValidity))) {
            
// The cache is not hit ! 不存在的缓存
            return false;
        }
        
$file = $this->_file($id);
        
if (is_null($file)) {
            
return false;
        }
        
// There is an available cache file ! 缓存文件必须是存在并是可用
        $fp = @fopen($file, 'rb');
        
if (!$fpreturn false;
        
if ($this->_options['file_locking']) @flock($fp, LOCK_SH);    //文件锁定
        $length = @filesize($file);
        
$mqr = get_magic_quotes_runtime();
        
set_magic_quotes_runtime(0);    //关闭魔术引用 主要是避免对特殊字段的过滤
        if ($this->_options['read_control']) { //读出数据时进行检测是否有变化
            $hashControl = @fread($fp, 32); //读取缓存文件中的前32个字符
            $length = $length - 32;
        }
        
if ($length) {
            
$data = @fread($fp, $length);
        } 
else {
            
$data = '';
        }
        
set_magic_quotes_runtime($mqr);//恢复原来的设置
        if ($this->_options['file_locking']) @flock($fp, LOCK_UN);//解锁
        @fclose($fp);
        
if ($this->_options['read_control']) {
            
$hashData = $this->_hash($data, $this->_options['read_control_type']); //得到当前加密的 hash key
            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($file);
                
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)
    {
        
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)
     *
     *  注意 $data 必须是字符窜类型 
     * @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 保存成功返回 true
     
*/
    
public function save($data, $id, $tags = array(), $specificLifetime = false)
    {
        
//检测缓冲目录是否可写
        if ((!is_dir($this->_options['cache_dir'])) or (!is_writable($this->_options['cache_dir']))) {
            
$this->_log("Zend_Cache_Backend_File::save() : cache_dir doesn't exist or is not writable");
        }
        
$this->remove($id); // to avoid multiple files with the same cache id    避免多个文件使用同一个缓存id
        $lifetime = $this->getLifetime($specificLifetime);    //得到缓存生命期
        $expire = $this->_expireTime($lifetime);    //根据生命期得到过期时间 time() + $lifetime;
        $file = $this->_file($id, $expire);
        
$firstTry = true;
        
$result = false;
        
while (1 == 1) {
            
$fp = @fopen($file, "wb");    //二进制只写模式打开文件
            if ($fp) {
                
// we can open the file, so the directory structure is ok
                if ($this->_options['file_locking']) @flock($fp, LOCK_EX);
                
if ($this->_options['read_control']) {//读数据检测  
                    @fwrite($fp, $this->_hash($data, $this->_options['read_control_type']), 32); //根据缓冲内容得到32字节长度的hash key 内容经过加密
                }
                
$mqr = get_magic_quotes_runtime();
                
set_magic_quotes_runtime(0); //关闭魔术引用 主要是避免对特殊字段的过滤
                @fwrite($fp, $data);
                
if ($this->_options['file_locking']) @flock($fp, LOCK_UN);    //解锁文件
                @fclose($fp);
                
set_magic_quotes_runtime($mqr);
                
$result = true;
                
break;
            }
            
// we can't open the file but it's maybe only the directory structure
            // which has to be built

            if ($this->_options['hashed_directory_level']==0break;//不建产缓存目录
            if ((!$firstTry|| ($this->_options['hashed_directory_level'== 0)) {    //保存只运行一次
                // it's not a problem of directory structure

                break;
            }
            
$firstTry = false;//已存行一次的标记
            // In this case, maybe we just need to create the corresponding directory

            @mkdir($this->_path($id), $this->_options['hashed_directory_umask'], true);//建立缓存目录 700权限
            @chmod($this->_path($id), $this->_options['hashed_directory_umask']); // see #ZF-320 (this line is required in some configurations) 进入新建的目录
        }
        
if ($result) {
            
foreach ($tags as $tag) {
                
$this->_registerTag($id, $tag);    //注册标签
            }
        }
        
return $result;
    }

    
/**
     * Remove a cache record
     *
     * @param string $id cache id
     * @return boolean true if no problem
     
*/
    
public function remove($id)
    {
        
$result1 = true;
        
$files = @glob($this->_file($id, '*'));
        
if (count($files== 0) {
            
return false;
        }
        
foreach ($files as $file) {
            
$result1 = $result1 && $this->_remove($file);
        }
        
$result2 = $this->_unregisterTag($id);
        
return ($result1 && $result2);
    }

    
/**
     * Clean some cache records
     *
     * 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)
     *
     * @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 private method to hide the recursive stuff
        clearstatcache();
        
return $this->_clean($this->_options['cache_dir'], $mode, $tags);
    }

    
/**
     * PUBLIC METHOD FOR UNIT TESTING ONLY !
     *
     * Force a cache record to expire
     *
     * @param string $id cache id
     
*/
    
public function ___expire($id)
    {
        
$file = $this->_file($id);
        
if (!(is_null($file))) {
            
$file2 = $this->_file($id, 1);
            @
rename($file, $file2);
        }
    }

    
// -----------------------
    // --- Private methods ---
    // -----------------------


    
/**
     * 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
     
*/
    
private function _remove($file)
    {
        
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 => we are going to try to invalidate it");
            
return false;
        }
        
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
     
*/
    
private function _test($id, $doNotTestCacheValidity)
    {
        
clearstatcache();    // 清除文件状态缓存
        $file = $this->_file($id); //根据缓存ID 得到文件名(包含路径) 
        if (is_null($file)) {
            
return false;
        }
        
$fileName = @basename($file);    //返回路径中的文件名部分
        $expire = (int) $this->_fileNameToExpireField($fileName);    //根据文件名得到缓存的过期时间
        if ($doNotTestCacheValidity) { //不检测过期
            return $expire;
        }
        
if (time() <= $expire) {    //没过期
            return @filemtime($file);    //返回文件修改时间
        }
        
return false;
    }

    
/**
     * Clean some cache records (private method used for recursive stuff)
     *    清除缓存 私有方法 采用递归调用 最后是调用 remove 进行删除
     * 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)
     *
     * @param string $dir directory to clean
     * @param string $mode clean mode        清除的方式
     * @param tags array $tags array of tags
     * @return boolean true if no problem
     
*/
    
private 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 . '--*');
        
foreach ($glob as $file)  {
            
if (is_file($file)) {
                
if ($mode==Zend_Cache::CLEANING_MODE_ALL) {
                    
$result = ($result&& ($this->_remove($file));
                }
                
if ($mode==Zend_Cache::CLEANING_MODE_OLD) {
                    
$fileName = @basename($file);
                    
$expire = (int) $this->_fileNameToExpireField($fileName);
                    
if (time() > $expire) {
                        
$result = ($result&& ($this->_remove($file));
                    }
                }
                
if ($mode==Zend_Cache::CLEANING_MODE_MATCHING_TAG) {
                    
$matching = true;
                    
$id = $this->_fileNameToId(basename($file));
                    
if (!($this->_isATag($id))) {
                        
foreach ($tags as $tag) {
                            
if (!($this->_testTag($id, $tag))) {
                                
$matching = false;
                                
break;
                            }
                        }
                        
if ($matching) {
                            
$result = ($result&& ($this->remove($id));
                        }
                    }
                }
                
if ($mode==Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG) {
                    
$matching = false;
                    
$id = $this->_fileNameToId(basename($file));
                    
if (!($this->_isATag($id))) {
                        
foreach ($tags as $tag) {
                            
if ($this->_testTag($id, $tag)) {
                                
$matching = true;
                                
break;
                            }
                        }
                        
if (!$matching) {
                            
$result = ($result&& ($this->remove($id));
                        }
                    }
                }
            }
            
if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) {
                
// Recursive call
                $result = ($result&& ($this->_clean($file . DIRECTORY_SEPARATOR, $mode, $tags));
                
if ($mode=='all') {
                    
// if mode=='all', we try to drop the structure too
                    @rmdir($file);
                }
            }
        }
        
return $result;
    }

    
/**
     * Register a cache id with the given tag
     * 注册缓存标记 主要用于表示当前内容已缓存的记号
     * @param string $id cache id
     * @param string $tag tag
     * @return boolean true if no problem
     
*/
    
private function _registerTag($id, $tag
    {
        
//所 id 转成标签id后进行保存 内容为 1
        return $this->save('1', $this->_tagCacheId($id, $tag));
    }

    
/**
     * Unregister tags of a cache id
     * 删除缓存 id 代表的文件
     * @param string $id cache id
     * @return boolean true if no problem
     
*/
    
private function _unregisterTag($id)
    {
        
$filesToRemove = @glob($this->_file($this->_tagCacheId($id, '*'), '*'));
        
$result = true;
        
foreach ($filesToRemove as $file) {
            
$result = $result && ($this->_remove($file));
        }
        
return $result;
    }

    
/**
     * Test if a cache id was saved with the given tag
     * 检测缓存的标签是否有效
     * @param string $id cache id
     * @param string $tag tag name
     * @return true if the cache id was saved with the given tag
     
*/
    
private function _testTag($id, $tag)
    {
        
if ($this->test($this->_tagCacheId($id, $tag))) {
           
return true;
        }
        
return false;
    }

    
/**
     * Compute & return the expire time
     *
     * @return int expire time (unix timestamp)
     
*/
    
private function _expireTime($lifetime)
    {
        
if (is_null($lifetime)) {
            
return 9999999999;
        }
        
return time() + $lifetime;
    }

    
/**
     * Make a control key with the string containing datas
     * 根据缓存的内容 生成 32字节的key
     * @param string $data data
     * @param string $controlType type of control 'md5', 'crc32' or 'strlen'
     * @return string control key
     
*/
    
private function _hash($data, $controlType)
    {
        
switch ($controlType) {
        
case 'md5':
            
return md5($data);
        
case 'crc32':
            
return sprintf('% 32d', crc32($data));
        
case 'strlen':
            
return sprintf('% 32d', strlen($data));
        
default:
            Zend_Cache
::throwException("Incorrect hash function : $controlType");
        }
    }

    
/**
     * Return a special/reserved cache id for storing the given tag on the given id
     * 根据缓存id 标签 产生 标记缓冲的 id 
     * @param string $id cache id
     * @param string $tag tag name
     * @return string cache id for the tag
     
*/
    
private function _tagCacheId($id, $tag) {
        
return 'internal-' . $id . '-' . $tag;
    }

    
/**
     * Return true is the given id is a tag
     * 检查缓存id是不是内部的标签
     * @param string $id
     * @return boolean
     
*/
    
private function _isATag($id)
    {
        
if (substr($id, 0, 9== 'internal-') {
            
return true;
        }
        
return false;
    }

    
/**
     * Transform a cache id into a file name and return it
     * 根据 缓存ID 得到最后保存成的文件名格式
     * 
     * @param string $id cache id
     * @param int expire timestamp
     * @return string file name
     
*/
    
private function _idToFileName($id, $expire)
    {
        
$prefix = $this->_options['file_name_prefix'];
        
$result = $prefix . '---' . $id . '---' . $expire;
        
return $result;
    }

    
/**
     * Get the father cache id from the tag cache id
     * 根据标记缓存id得到代表的缓冲id 
     * @param string $id tag cache id
     * @return string father cache id
     
*/
    
private function _tagCacheIdToFatherCacheId($id)
    {
        
return preg_replace('~internal-(w*)-.*$~', '$1', $id);
    }

    
/**
     * Return the expire field from the file name
     * 根据文件名得到过期后的时间 (缓存文件字命名方式)
     * zend_cache---md5加密过的缓存ID---过期时间(时间戳格式)
     * @param string $fileName
     * @return string expire field
     
*/
    
private function _fileNameToExpireField($fileName)
    {
        
$prefix = $this->_options['file_name_prefix'];
        
return preg_replace('~^' . $prefix . '---.*---(d*)$~', '$1', $fileName);
    }

    
/**
     * Transform a file name into cache id and return it
     *
     * @param string $fileName file name
     * @return string cache id
     
*/
    
private function _fileNameToId($fileName)
    {
        
$prefix = $this->_options['file_name_prefix'];
        
return preg_replace('~^' . $prefix . '---(.*)---.*$~', '$1', $fileName);
    }

    
/**
     * Return the complete directory path of a filename (including hashedDirectoryStructure)
     * 返回文件所在的完整路径 
     * 
     * @param string $id cache id
     * @return string complete directory path
     
*/
    
private function _path($id)
    {
        
$root = $this->_options['cache_dir'];
        
$prefix = $this->_options['file_name_prefix'];
        
if ($this->_options['hashed_directory_level']>0) {
            
if ($this->_isATag($id)) {//是内部标签
                // we store tags in the same directory than the father

                $id2 = $this->_tagCacheIdToFatherCacheId($id);
                
$hash = md5($this->_tagCacheIdToFatherCacheId($id));
            } 
else {
                
$hash = md5($id);
            }
            
for ($i=0 ; $i<$this->_options['hashed_directory_level'] ; $i++) {
                
$root = $root . $prefix . '--' . substr($hash, 0, $i + 1. DIRECTORY_SEPARATOR;
            }
        }
        
return $root;
    }

    
/**
     * Make and return a file name (with path)
     * 根据缓存ID 得到字符型的文件名(包含路径) 
     * 
     * if $expire is null (default), the function try to guess the real file name
     * (if it fails (no cache files or several cache files for this id, the method returns null)
     *
     * @param string $id cache id
     * @param int expire timestamp
     * @return string file name (with path) 返回字符型的文件名(包含路径)
     
*/
    
private function _file($id, $expire = null)
    {
        
$path = $this->_path($id); //得到文件所在的完整路径
        if (is_null($expire)) {//没指定过期时间
            //_idToFileName 得到类似 zend_cache---92a8a98916d320e6a273dbaa91a320f5---*

            $glob = @glob($path . $this->_idToFileName($id, '*'));    //缓存所在路径 寻找有没有类似的文件 即得到真实的 缓存文件名 (包括路径)
            $nbr = count($glob);
            
if ($nbr == 1) {    //找到了一个
                return $glob[0];
            }
            
return null;
        }
        
$fileName = $this->_idToFileName($id, $expire);
        
return $path . $fileName;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值