diff --git a/CHANGELOG b/CHANGELOG index a432f71a7c..bc496ff733 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -101,6 +101,7 @@ Version 1.1.14 work in progress - Enh #2275: Added primary log rotation by copy and truncate to CFileLogRoute (bdstevens) - Enh #2415: Cancel current ajax request before create a new one in CGridView and CListView (gusnips) - Enh #2416: Avoid instantiating HTMLPurifier on each CHtmlPurifier::purify() call. Allow to pass array as argument of CHtmlPurifier::purify() (twisted1919) +- Enh #2435: CFileCache entry expiration time could now be embedded into the cache file instead of changing file's modification time to be in future (resurtm) - Enh #2459: Absolute session timeout in CWebUser (ivokund) - Enh #2494: Allow to configure CBaseListView emptyText container tag name (ifdattic) - Chg: Upgraded HTMLPurifier to v4.5.0 (samdark) diff --git a/framework/caching/CFileCache.php b/framework/caching/CFileCache.php index 02b55df27a..f8ed789663 100644 --- a/framework/caching/CFileCache.php +++ b/framework/caching/CFileCache.php @@ -41,6 +41,15 @@ class CFileCache extends CCache * The value of this property should not exceed 16 (less than 3 is recommended). */ public $directoryLevel=0; + /** + * @var boolean whether cache entry expiration time should be embedded into a physical file. + * Defaults to false meaning that the file modification time will be used to store expire value. + * True value means that first ten bytes of the file would be reserved and used to store expiration time. + * On some systems PHP is not allowed to change file modification time to be in future even with 777 + * permissions, so this property could be useful in this case. + * @since 1.1.14 + */ + public $embedExpiry=false; private $_gcProbability=100; private $_gced=false; @@ -103,8 +112,8 @@ protected function flushValues() protected function getValue($key) { $cacheFile=$this->getCacheFile($key); - if(($time=@filemtime($cacheFile))>time()) - return @file_get_contents($cacheFile); + if(($time=$this->filemtime($cacheFile))>time()) + return @file_get_contents($cacheFile,false,null,$this->embedExpiry ? 10 : -1); elseif($time>0) @unlink($cacheFile); return false; @@ -134,10 +143,10 @@ protected function setValue($key,$value,$expire) $cacheFile=$this->getCacheFile($key); if($this->directoryLevel>0) @mkdir(dirname($cacheFile),0777,true); - if(@file_put_contents($cacheFile,$value,LOCK_EX)!==false) + if(@file_put_contents($cacheFile,$this->embedExpiry ? $expire.$value : $value,LOCK_EX)!==false) { @chmod($cacheFile,0777); - return @touch($cacheFile,$expire); + return $this->embedExpiry ? true : @touch($cacheFile,$expire); } else return false; @@ -155,7 +164,7 @@ protected function setValue($key,$value,$expire) protected function addValue($key,$value,$expire) { $cacheFile=$this->getCacheFile($key); - if(@filemtime($cacheFile)>time()) + if($this->filemtime($cacheFile)>time()) return false; return $this->setValue($key,$value,$expire); } @@ -212,9 +221,22 @@ public function gc($expiredOnly=true,$path=null) $fullPath=$path.DIRECTORY_SEPARATOR.$file; if(is_dir($fullPath)) $this->gc($expiredOnly,$fullPath); - elseif($expiredOnly && @filemtime($fullPath)filemtime($fullPath)embedExpiry) + return (int)@file_get_contents($path,false,null,0,10); + else + return @filemtime($path); + } } diff --git a/tests/framework/caching/CFileCacheTest.php b/tests/framework/caching/CFileCacheTest.php new file mode 100644 index 0000000000..82ca716672 --- /dev/null +++ b/tests/framework/caching/CFileCacheTest.php @@ -0,0 +1,53 @@ +'testApp', + 'components'=>array( + 'cache'=>array('class'=>'CFileCache'), + ), + )); + $app->reset(); + $cache=$app->cache; + + $cache->set('testKey1','testValue1',2); + $files=glob(Yii::getPathOfAlias('application.runtime.cache').'/*.bin'); + $this->assertEquals(time()+2,filemtime($files[0])); + + $cache->set('testKey2','testValue2',2); + sleep(1); + $this->assertEquals('testValue2',$cache->get('testKey2')); + + $cache->set('testKey3','testValue3',2); + sleep(3); + $this->assertEquals(false,$cache->get('testKey2')); + + + $app=new TestApplication(array( + 'id'=>'testApp', + 'components'=>array( + 'cache'=>array('class'=>'CFileCache','embedExpiry'=>true), + ), + )); + $app->reset(); + $cache=$app->cache; + + $cache->set('testKey4','testValue4',2); + $files=glob(Yii::getPathOfAlias('application.runtime.cache').'/*.bin'); + $this->assertEquals(time(),filemtime($files[0])); + + $cache->set('testKey5','testValue5',2); + sleep(1); + $this->assertEquals('testValue5',$cache->get('testKey5')); + + $cache->set('testKey6','testValue6',2); + sleep(3); + $this->assertEquals(false,$cache->get('testKey6')); + } +}