Zend_Cache_Backend_File 注解

 
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  ( ! $fp return   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 ' ] == 0 break ; // 不建产缓存目录
             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 ;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值