From 903ae8a581fac1e6917fc3e31d2ad8fb91df80c3 Mon Sep 17 00:00:00 2001 From: ctrlaltca <> Date: Thu, 12 Jul 2012 11:21:01 +0000 Subject: standardize the use of unix eol; use svn properties to enforce native eol --- framework/Caching/TAPCCache.php | 266 +++---- framework/Caching/TCache.php | 1440 ++++++++++++++++++------------------ framework/Caching/TDbCache.php | 1156 ++++++++++++++--------------- framework/Caching/TSqliteCache.php | 448 +++++------ framework/Caching/TXCache.php | 262 +++---- 5 files changed, 1786 insertions(+), 1786 deletions(-) (limited to 'framework/Caching') diff --git a/framework/Caching/TAPCCache.php b/framework/Caching/TAPCCache.php index 49cbd137..e0117a0c 100644 --- a/framework/Caching/TAPCCache.php +++ b/framework/Caching/TAPCCache.php @@ -1,133 +1,133 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Caching - */ - -/** - * TAPCCache class - * - * TAPCCache implements a cache application module based on {@link http://www.php.net/apc APC}. - * - * By definition, cache does not ensure the existence of a value - * even if it never expires. Cache is not meant to be an persistent storage. - * - * To use this module, the APC PHP extension must be loaded and set in the php.ini file the following: - * - * apc.cache_by_default=0 - * - * - * Some usage examples of TAPCCache are as follows, - * - * $cache=new TAPCCache; // TAPCCache may also be loaded as a Prado application module - * $cache->init(null); - * $cache->add('object',$object); - * $object2=$cache->get('object'); - * - * - * If loaded, TAPCCache will register itself with {@link TApplication} as the - * cache module. It can be accessed via {@link TApplication::getCache()}. - * - * TAPCCache may be configured in application configuration file as follows - * - * - * - * - * @author Alban Hanry - * @author Knut Urdalen - * @version $Id$ - * @package System.Caching - * @since 3.0b - */ -class TAPCCache extends TCache -{ - /** - * Initializes this module. - * This method is required by the IModule interface. - * @param TXmlElement configuration for this module, can be null - * @throws TConfigurationException if apc extension is not installed or not started, check your php.ini - */ - public function init($config) - { - if(!extension_loaded('apc')) - throw new TConfigurationException('apccache_extension_required'); - - if(ini_get('apc.enabled') == false) - throw new TConfigurationException('apccache_extension_not_enabled'); - - if(substr(php_sapi_name(), 0, 3) === 'cli' and ini_get('apc.enable_cli') == false) - throw new TConfigurationException('apccache_extension_not_enabled_cli'); - - parent::init($config); - } - - /** - * Retrieves a value from cache with a specified key. - * This is the implementation of the method declared in the parent class. - * @param string a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. - */ - protected function getValue($key) - { - return apc_fetch($key); - } - - /** - * Stores a value identified by a key in cache. - * This is the implementation of the method declared in the parent class. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - protected function setValue($key,$value,$expire) - { - return apc_store($key,$value,$expire); - } - - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * This is the implementation of the method declared in the parent class. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - protected function addValue($key,$value,$expire) - { - if(function_exists('apc_add')) { - return apc_add($key,$value,$expire); - } else { - throw new TNotSupportedException('apccache_add_unsupported'); - } - } - - /** - * Deletes a value with the specified key from cache - * This is the implementation of the method declared in the parent class. - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - protected function deleteValue($key) - { - return apc_delete($key); - } - - /** - * Deletes all values from cache. - * Be careful of performing this operation if the cache is shared by multiple applications. - */ - public function flush() - { - return apc_clear_cache('user'); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Caching + */ + +/** + * TAPCCache class + * + * TAPCCache implements a cache application module based on {@link http://www.php.net/apc APC}. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * To use this module, the APC PHP extension must be loaded and set in the php.ini file the following: + * + * apc.cache_by_default=0 + * + * + * Some usage examples of TAPCCache are as follows, + * + * $cache=new TAPCCache; // TAPCCache may also be loaded as a Prado application module + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TAPCCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TAPCCache may be configured in application configuration file as follows + * + * + * + * + * @author Alban Hanry + * @author Knut Urdalen + * @version $Id$ + * @package System.Caching + * @since 3.0b + */ +class TAPCCache extends TCache +{ + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if apc extension is not installed or not started, check your php.ini + */ + public function init($config) + { + if(!extension_loaded('apc')) + throw new TConfigurationException('apccache_extension_required'); + + if(ini_get('apc.enabled') == false) + throw new TConfigurationException('apccache_extension_not_enabled'); + + if(substr(php_sapi_name(), 0, 3) === 'cli' and ini_get('apc.enable_cli') == false) + throw new TConfigurationException('apccache_extension_not_enabled_cli'); + + parent::init($config); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return apc_fetch($key); + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return apc_store($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + if(function_exists('apc_add')) { + return apc_add($key,$value,$expire); + } else { + throw new TNotSupportedException('apccache_add_unsupported'); + } + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return apc_delete($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return apc_clear_cache('user'); + } +} + diff --git a/framework/Caching/TCache.php b/framework/Caching/TCache.php index d4f74bf0..00d69c3c 100644 --- a/framework/Caching/TCache.php +++ b/framework/Caching/TCache.php @@ -1,720 +1,720 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Caching - */ - -Prado::using('System.Collections.TList'); - -/** - * TCache class - * - * TCache is the base class for cache classes with different cache storage implementation. - * - * TCache implements the interface {@link ICache} with the following methods, - * - {@link get} : retrieve the value with a key (if any) from cache - * - {@link set} : store the value with a key into cache - * - {@link add} : store the value only if cache does not have this key - * - {@link delete} : delete the value with the specified key from cache - * - {@link flush} : delete all values from cache - * - * Each value is associated with an expiration time. The {@link get} operation - * ensures that any expired value will not be returned. The expiration time by - * the number of seconds. A expiration time 0 represents never expire. - * - * By definition, cache does not ensure the existence of a value - * even if it never expires. Cache is not meant to be an persistent storage. - * - * Child classes must implement the following methods: - * - {@link getValue} - * - {@link setValue} - * - {@link addValue} - * - {@link deleteValue} - * and optionally {@link flush} - * - * Since version 3.1.2, TCache implements the ArrayAccess interface such that - * the cache acts as an array. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.0 - */ -abstract class TCache extends TModule implements ICache, ArrayAccess -{ - private $_prefix=null; - private $_primary=true; - - /** - * Initializes the cache module. - * This method initializes the cache key prefix and registers the cache module - * with the application if the cache is primary. - * @param TXmlElement the module configuration - */ - public function init($config) - { - if($this->_prefix===null) - $this->_prefix=$this->getApplication()->getUniqueID(); - if($this->_primary) - { - if($this->getApplication()->getCache()===null) - $this->getApplication()->setCache($this); - else - throw new TConfigurationException('cache_primary_duplicated',get_class($this)); - } - } - - /** - * @return boolean whether this cache module is used as primary/system cache. - * A primary cache is used by PRADO core framework to cache data such as - * parsed templates, themes, etc. - */ - public function getPrimaryCache() - { - return $this->_primary; - } - - /** - * @param boolean whether this cache module is used as primary/system cache. Defaults to false. - * @see getPrimaryCache - */ - public function setPrimaryCache($value) - { - $this->_primary=TPropertyValue::ensureBoolean($value); - } - - /** - * @return string a unique prefix for the keys of cached values. - * If it is not explicitly set, it will take the value of {@link TApplication::getUniqueID}. - */ - public function getKeyPrefix() - { - return $this->_prefix; - } - - /** - * @param string a unique prefix for the keys of cached values - */ - public function setKeyPrefix($value) - { - $this->_prefix=$value; - } - - /** - * @param string a key identifying a value to be cached - * @return sring a key generated from the provided key which ensures the uniqueness across applications - */ - protected function generateUniqueKey($key) - { - return md5($this->_prefix.$key); - } - - /** - * Retrieves a value from cache with a specified key. - * @param string a key identifying the cached value - * @return mixed the value stored in cache, false if the value is not in the cache or expired. - */ - public function get($id) - { - if(($data=$this->getValue($this->generateUniqueKey($id)))!==false) - { - if(!is_array($data)) - return false; - if(!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged()) - return $data[0]; - } - return false; - } - - /** - * Stores a value identified by a key into cache. - * If the cache already contains such a key, the existing value and - * expiration time will be replaced with the new ones. If the value is - * empty, the cache key will be deleted. - * - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function set($id,$value,$expire=0,$dependency=null) - { - if(empty($value) && $expire === 0) - $this->delete($id); - else - { - $data=array($value,$dependency); - return $this->setValue($this->generateUniqueKey($id),$data,$expire); - } - } - - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * Nothing will be done if the cache already contains the key or if value is empty. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function add($id,$value,$expire=0,$dependency=null) - { - if(empty($value) && $expire === 0) - return false; - $data=array($value,$dependency); - return $this->addValue($this->generateUniqueKey($id),$data,$expire); - } - - /** - * Deletes a value with the specified key from cache - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - public function delete($id) - { - return $this->deleteValue($this->generateUniqueKey($id)); - } - - /** - * Deletes all values from cache. - * Be careful of performing this operation if the cache is shared by multiple applications. - * Child classes may implement this method to realize the flush operation. - * @throws TNotSupportedException if this method is not overridden by child classes - */ - public function flush() - { - throw new TNotSupportedException('cache_flush_unsupported'); - } - - /** - * Retrieves a value from cache with a specified key. - * This method should be implemented by child classes to store the data - * in specific cache storage. The uniqueness and dependency are handled - * in {@link get()} already. So only the implementation of data retrieval - * is needed. - * @param string a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. - */ - abstract protected function getValue($key); - - /** - * Stores a value identified by a key in cache. - * This method should be implemented by child classes to store the data - * in specific cache storage. The uniqueness and dependency are handled - * in {@link set()} already. So only the implementation of data storage - * is needed. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - abstract protected function setValue($key,$value,$expire); - - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * This method should be implemented by child classes to store the data - * in specific cache storage. The uniqueness and dependency are handled - * in {@link add()} already. So only the implementation of data storage - * is needed. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - abstract protected function addValue($key,$value,$expire); - - /** - * Deletes a value with the specified key from cache - * This method should be implemented by child classes to delete the data from actual cache storage. - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - abstract protected function deleteValue($key); - - /** - * Returns whether there is a cache entry with a specified key. - * This method is required by the interface ArrayAccess. - * @param string a key identifying the cached value - * @return boolean - */ - public function offsetExists($id) - { - return $this->get($id) !== false; - } - - /** - * Retrieves the value from cache with a specified key. - * This method is required by the interface ArrayAccess. - * @param string a key identifying the cached value - * @return mixed the value stored in cache, false if the value is not in the cache or expired. - */ - public function offsetGet($id) - { - return $this->get($id); - } - - /** - * Stores the value identified by a key into cache. - * If the cache already contains such a key, the existing value will be - * replaced with the new ones. To add expiration and dependencies, use the set() method. - * This method is required by the interface ArrayAccess. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - */ - public function offsetSet($id, $value) - { - $this->set($id, $value); - } - - /** - * Deletes the value with the specified key from cache - * This method is required by the interface ArrayAccess. - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - public function offsetUnset($id) - { - $this->delete($id); - } -} - - -/** - * TCacheDependency class. - * - * TCacheDependency is the base class implementing {@link ICacheDependency} interface. - * Descendant classes must implement {@link getHasChanged()} to provide - * actual dependency checking logic. - * - * The property value of {@link getHasChanged HasChanged} tells whether - * the dependency is changed or not. - * - * You may disable the dependency checking by setting {@link setEnabled Enabled} - * to false. - * - * Note, since the dependency objects often need to be serialized so that - * they can persist across requests, you may need to implement __sleep() and - * __wakeup() if the dependency objects contain resource handles which are - * not serializable. - * - * Currently, the following dependency classes are provided in the PRADO release: - * - {@link TFileCacheDependency}: checks whether a file is changed or not - * - {@link TDirectoryCacheDependency}: checks whether a directory is changed or not - * - {@link TGlobalStateCacheDependency}: checks whether a global state is changed or not - * - {@link TChainedCacheDependency}: checks whether any of a list of dependencies is changed or not - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.1.0 - */ -abstract class TCacheDependency extends TComponent implements ICacheDependency -{ -} - - -/** - * TFileCacheDependency class. - * - * TFileCacheDependency performs dependency checking based on the - * last modification time of the file specified via {@link setFileName FileName}. - * The dependency is reported as unchanged if and only if the file's - * last modification time remains unchanged. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.1.0 - */ -class TFileCacheDependency extends TCacheDependency -{ - private $_fileName; - private $_timestamp; - - /** - * Constructor. - * @param string name of the file whose change is to be checked. - */ - public function __construct($fileName) - { - $this->setFileName($fileName); - } - - /** - * @return string the name of the file whose change is to be checked - */ - public function getFileName() - { - return $this->_fileName; - } - - /** - * @param string the name of the file whose change is to be checked - */ - public function setFileName($value) - { - $this->_fileName=$value; - $this->_timestamp=@filemtime($value); - } - - /** - * @return int the last modification time of the file - */ - public function getTimestamp() - { - return $this->_timestamp; - } - - /** - * Performs the actual dependency checking. - * This method returns true if the last modification time of the file is changed. - * @return boolean whether the dependency is changed or not. - */ - public function getHasChanged() - { - return @filemtime($this->_fileName)!==$this->_timestamp; - } -} - -/** - * TDirectoryCacheDependency class. - * - * TDirectoryCacheDependency performs dependency checking based on the - * modification time of the files contained in the specified directory. - * The directory being checked is specified via {@link setDirectory Directory}. - * - * By default, all files under the specified directory and subdirectories - * will be checked. If the last modification time of any of them is changed - * or if different number of files are contained in a directory, the dependency - * is reported as changed. By specifying {@link setRecursiveCheck RecursiveCheck} - * and {@link setRecursiveLevel RecursiveLevel}, one can limit the checking - * to a certain depth of the subdirectories. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.1.0 - */ -class TDirectoryCacheDependency extends TCacheDependency -{ - private $_recursiveCheck=true; - private $_recursiveLevel=-1; - private $_timestamps; - private $_directory; - - /** - * Constructor. - * @param string the directory to be checked - */ - public function __construct($directory) - { - $this->setDirectory($directory); - } - - /** - * @return string the directory to be checked - */ - public function getDirectory() - { - return $this->_directory; - } - - /** - * @param string the directory to be checked - * @throws TInvalidDataValueException if the directory does not exist - */ - public function setDirectory($directory) - { - if(($path=realpath($directory))===false || !is_dir($path)) - throw new TInvalidDataValueException('directorycachedependency_directory_invalid',$directory); - $this->_directory=$path; - $this->_timestamps=$this->generateTimestamps($path); - } - - /** - * @return boolean whether the subdirectories of the directory will also be checked. - * It defaults to true. - */ - public function getRecursiveCheck() - { - return $this->_recursiveCheck; - } - - /** - * @param boolean whether the subdirectories of the directory will also be checked. - */ - public function setRecursiveCheck($value) - { - $this->_recursiveCheck=TPropertyValue::ensureBoolean($value); - } - - /** - * @return int the depth of the subdirectories to be checked. - * It defaults to -1, meaning unlimited depth. - */ - public function getRecursiveLevel() - { - return $this->_recursiveLevel; - } - - /** - * Sets a value indicating the depth of the subdirectories to be checked. - * This is meaningful only when {@link getRecursiveCheck RecursiveCheck} - * is true. - * @param int the depth of the subdirectories to be checked. - * If the value is less than 0, it means unlimited depth. - * If the value is 0, it means checking the files directly under the specified directory. - */ - public function setRecursiveLevel($value) - { - $this->_recursiveLevel=TPropertyValue::ensureInteger($value); - } - - /** - * Performs the actual dependency checking. - * This method returns true if the directory is changed. - * @return boolean whether the dependency is changed or not. - */ - public function getHasChanged() - { - return $this->generateTimestamps($this->_directory)!=$this->_timestamps; - } - - /** - * Checks to see if the file should be checked for dependency. - * This method is invoked when dependency of the whole directory is being checked. - * By default, it always returns true, meaning the file should be checked. - * You may override this method to check only certain files. - * @param string the name of the file that may be checked for dependency. - * @return boolean whether this file should be checked. - */ - protected function validateFile($fileName) - { - return true; - } - - /** - * Checks to see if the specified subdirectory should be checked for dependency. - * This method is invoked when dependency of the whole directory is being checked. - * By default, it always returns true, meaning the subdirectory should be checked. - * You may override this method to check only certain subdirectories. - * @param string the name of the subdirectory that may be checked for dependency. - * @return boolean whether this subdirectory should be checked. - */ - protected function validateDirectory($directory) - { - return true; - } - - /** - * Determines the last modification time for files under the directory. - * This method may go recursively into subdirectories if - * {@link setRecursiveCheck RecursiveCheck} is set true. - * @param string the directory name - * @param int level of the recursion - * @return array list of file modification time indexed by the file path - */ - protected function generateTimestamps($directory,$level=0) - { - if(($dir=opendir($directory))===false) - throw new TIOException('directorycachedependency_directory_invalid',$directory); - $timestamps=array(); - while(($file=readdir($dir))!==false) - { - $path=$directory.DIRECTORY_SEPARATOR.$file; - if($file==='.' || $file==='..') - continue; - else if(is_dir($path)) - { - if(($this->_recursiveLevel<0 || $level<$this->_recursiveLevel) && $this->validateDirectory($path)) - $timestamps=array_merge($this->generateTimestamps($path,$level+1)); - } - else if($this->validateFile($path)) - $timestamps[$path]=filemtime($path); - } - closedir($dir); - return $timestamps; - } -} - - -/** - * TGlobalStateCacheDependency class. - * - * TGlobalStateCacheDependency checks if a global state is changed or not. - * If the global state is changed, the dependency is reported as changed. - * To specify which global state this dependency should check with, - * set {@link setStateName StateName} to the name of the global state. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.1.0 - */ -class TGlobalStateCacheDependency extends TCacheDependency -{ - private $_stateName; - private $_stateValue; - - /** - * Constructor. - * @param string the name of the global state - */ - public function __construct($name) - { - $this->setStateName($name); - } - - /** - * @return string the name of the global state - */ - public function getStateName() - { - return $this->_stateName; - } - - /** - * @param string the name of the global state - * @see TApplication::setGlobalState - */ - public function setStateName($value) - { - $this->_stateName=$value; - $this->_stateValue=Prado::getApplication()->getGlobalState($value); - } - - /** - * Performs the actual dependency checking. - * This method returns true if the specified global state is changed. - * @return boolean whether the dependency is changed or not. - */ - public function getHasChanged() - { - return $this->_stateValue!==Prado::getApplication()->getGlobalState($this->_stateName); - } -} - - -/** - * TChainedCacheDependency class. - * - * TChainedCacheDependency represents a list of cache dependency objects - * and performs the dependency checking based on the checking results of - * these objects. If any of them reports a dependency change, TChainedCacheDependency - * will return true for the checking. - * - * To add dependencies to TChainedCacheDependency, use {@link getDependencies Dependencies} - * which gives a {@link TCacheDependencyList} instance and can be used like an array - * (see {@link TList} for more details}). - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.1.0 - */ -class TChainedCacheDependency extends TCacheDependency -{ - private $_dependencies=null; - - /** - * @return TCacheDependencyList list of dependency objects - */ - public function getDependencies() - { - if($this->_dependencies===null) - $this->_dependencies=new TCacheDependencyList; - return $this->_dependencies; - } - - /** - * Performs the actual dependency checking. - * This method returns true if any of the dependency objects - * reports a dependency change. - * @return boolean whether the dependency is changed or not. - */ - public function getHasChanged() - { - if($this->_dependencies!==null) - { - foreach($this->_dependencies as $dependency) - if($dependency->getHasChanged()) - return true; - } - return false; - } -} - - -/** - * TApplicationStateCacheDependency class. - * - * TApplicationStateCacheDependency performs dependency checking based on - * the mode of the currently running PRADO application. - * The dependency is reportedly as unchanged if and only if the application - * is running in performance mode. - * - * You may chain this dependency together with other dependencies - * so that only when the application is not in performance mode the other dependencies - * will be checked. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.1.0 - */ -class TApplicationStateCacheDependency extends TCacheDependency -{ - /** - * Performs the actual dependency checking. - * This method returns true if the currently running application is not in performance mode. - * @return boolean whether the dependency is changed or not. - */ - public function getHasChanged() - { - return Prado::getApplication()->getMode()!==TApplicationMode::Performance; - } -} - -/** - * TCacheDependencyList class. - * - * TCacheDependencyList represents a list of cache dependency objects. - * Only objects implementing {@link ICacheDependency} can be added into this list. - * - * TCacheDependencyList can be used like an array. See {@link TList} - * for more details. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.1.0 - */ -class TCacheDependencyList extends TList -{ - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by performing additional type checking - * for each newly added item. - * @param integer the specified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a dependency instance - */ - public function insertAt($index,$item) - { - if($item instanceof ICacheDependency) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('cachedependencylist_cachedependency_required'); - } -} - -?> + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Caching + */ + +Prado::using('System.Collections.TList'); + +/** + * TCache class + * + * TCache is the base class for cache classes with different cache storage implementation. + * + * TCache implements the interface {@link ICache} with the following methods, + * - {@link get} : retrieve the value with a key (if any) from cache + * - {@link set} : store the value with a key into cache + * - {@link add} : store the value only if cache does not have this key + * - {@link delete} : delete the value with the specified key from cache + * - {@link flush} : delete all values from cache + * + * Each value is associated with an expiration time. The {@link get} operation + * ensures that any expired value will not be returned. The expiration time by + * the number of seconds. A expiration time 0 represents never expire. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * Child classes must implement the following methods: + * - {@link getValue} + * - {@link setValue} + * - {@link addValue} + * - {@link deleteValue} + * and optionally {@link flush} + * + * Since version 3.1.2, TCache implements the ArrayAccess interface such that + * the cache acts as an array. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.0 + */ +abstract class TCache extends TModule implements ICache, ArrayAccess +{ + private $_prefix=null; + private $_primary=true; + + /** + * Initializes the cache module. + * This method initializes the cache key prefix and registers the cache module + * with the application if the cache is primary. + * @param TXmlElement the module configuration + */ + public function init($config) + { + if($this->_prefix===null) + $this->_prefix=$this->getApplication()->getUniqueID(); + if($this->_primary) + { + if($this->getApplication()->getCache()===null) + $this->getApplication()->setCache($this); + else + throw new TConfigurationException('cache_primary_duplicated',get_class($this)); + } + } + + /** + * @return boolean whether this cache module is used as primary/system cache. + * A primary cache is used by PRADO core framework to cache data such as + * parsed templates, themes, etc. + */ + public function getPrimaryCache() + { + return $this->_primary; + } + + /** + * @param boolean whether this cache module is used as primary/system cache. Defaults to false. + * @see getPrimaryCache + */ + public function setPrimaryCache($value) + { + $this->_primary=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string a unique prefix for the keys of cached values. + * If it is not explicitly set, it will take the value of {@link TApplication::getUniqueID}. + */ + public function getKeyPrefix() + { + return $this->_prefix; + } + + /** + * @param string a unique prefix for the keys of cached values + */ + public function setKeyPrefix($value) + { + $this->_prefix=$value; + } + + /** + * @param string a key identifying a value to be cached + * @return sring a key generated from the provided key which ensures the uniqueness across applications + */ + protected function generateUniqueKey($key) + { + return md5($this->_prefix.$key); + } + + /** + * Retrieves a value from cache with a specified key. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function get($id) + { + if(($data=$this->getValue($this->generateUniqueKey($id)))!==false) + { + if(!is_array($data)) + return false; + if(!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged()) + return $data[0]; + } + return false; + } + + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. If the value is + * empty, the cache key will be deleted. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($id,$value,$expire=0,$dependency=null) + { + if(empty($value) && $expire === 0) + $this->delete($id); + else + { + $data=array($value,$dependency); + return $this->setValue($this->generateUniqueKey($id),$data,$expire); + } + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * Nothing will be done if the cache already contains the key or if value is empty. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($id,$value,$expire=0,$dependency=null) + { + if(empty($value) && $expire === 0) + return false; + $data=array($value,$dependency); + return $this->addValue($this->generateUniqueKey($id),$data,$expire); + } + + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($id) + { + return $this->deleteValue($this->generateUniqueKey($id)); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + * Child classes may implement this method to realize the flush operation. + * @throws TNotSupportedException if this method is not overridden by child classes + */ + public function flush() + { + throw new TNotSupportedException('cache_flush_unsupported'); + } + + /** + * Retrieves a value from cache with a specified key. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link get()} already. So only the implementation of data retrieval + * is needed. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + abstract protected function getValue($key); + + /** + * Stores a value identified by a key in cache. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link set()} already. So only the implementation of data storage + * is needed. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + abstract protected function setValue($key,$value,$expire); + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link add()} already. So only the implementation of data storage + * is needed. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + abstract protected function addValue($key,$value,$expire); + + /** + * Deletes a value with the specified key from cache + * This method should be implemented by child classes to delete the data from actual cache storage. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + abstract protected function deleteValue($key); + + /** + * Returns whether there is a cache entry with a specified key. + * This method is required by the interface ArrayAccess. + * @param string a key identifying the cached value + * @return boolean + */ + public function offsetExists($id) + { + return $this->get($id) !== false; + } + + /** + * Retrieves the value from cache with a specified key. + * This method is required by the interface ArrayAccess. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function offsetGet($id) + { + return $this->get($id); + } + + /** + * Stores the value identified by a key into cache. + * If the cache already contains such a key, the existing value will be + * replaced with the new ones. To add expiration and dependencies, use the set() method. + * This method is required by the interface ArrayAccess. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + */ + public function offsetSet($id, $value) + { + $this->set($id, $value); + } + + /** + * Deletes the value with the specified key from cache + * This method is required by the interface ArrayAccess. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function offsetUnset($id) + { + $this->delete($id); + } +} + + +/** + * TCacheDependency class. + * + * TCacheDependency is the base class implementing {@link ICacheDependency} interface. + * Descendant classes must implement {@link getHasChanged()} to provide + * actual dependency checking logic. + * + * The property value of {@link getHasChanged HasChanged} tells whether + * the dependency is changed or not. + * + * You may disable the dependency checking by setting {@link setEnabled Enabled} + * to false. + * + * Note, since the dependency objects often need to be serialized so that + * they can persist across requests, you may need to implement __sleep() and + * __wakeup() if the dependency objects contain resource handles which are + * not serializable. + * + * Currently, the following dependency classes are provided in the PRADO release: + * - {@link TFileCacheDependency}: checks whether a file is changed or not + * - {@link TDirectoryCacheDependency}: checks whether a directory is changed or not + * - {@link TGlobalStateCacheDependency}: checks whether a global state is changed or not + * - {@link TChainedCacheDependency}: checks whether any of a list of dependencies is changed or not + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.1.0 + */ +abstract class TCacheDependency extends TComponent implements ICacheDependency +{ +} + + +/** + * TFileCacheDependency class. + * + * TFileCacheDependency performs dependency checking based on the + * last modification time of the file specified via {@link setFileName FileName}. + * The dependency is reported as unchanged if and only if the file's + * last modification time remains unchanged. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.1.0 + */ +class TFileCacheDependency extends TCacheDependency +{ + private $_fileName; + private $_timestamp; + + /** + * Constructor. + * @param string name of the file whose change is to be checked. + */ + public function __construct($fileName) + { + $this->setFileName($fileName); + } + + /** + * @return string the name of the file whose change is to be checked + */ + public function getFileName() + { + return $this->_fileName; + } + + /** + * @param string the name of the file whose change is to be checked + */ + public function setFileName($value) + { + $this->_fileName=$value; + $this->_timestamp=@filemtime($value); + } + + /** + * @return int the last modification time of the file + */ + public function getTimestamp() + { + return $this->_timestamp; + } + + /** + * Performs the actual dependency checking. + * This method returns true if the last modification time of the file is changed. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + return @filemtime($this->_fileName)!==$this->_timestamp; + } +} + +/** + * TDirectoryCacheDependency class. + * + * TDirectoryCacheDependency performs dependency checking based on the + * modification time of the files contained in the specified directory. + * The directory being checked is specified via {@link setDirectory Directory}. + * + * By default, all files under the specified directory and subdirectories + * will be checked. If the last modification time of any of them is changed + * or if different number of files are contained in a directory, the dependency + * is reported as changed. By specifying {@link setRecursiveCheck RecursiveCheck} + * and {@link setRecursiveLevel RecursiveLevel}, one can limit the checking + * to a certain depth of the subdirectories. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.1.0 + */ +class TDirectoryCacheDependency extends TCacheDependency +{ + private $_recursiveCheck=true; + private $_recursiveLevel=-1; + private $_timestamps; + private $_directory; + + /** + * Constructor. + * @param string the directory to be checked + */ + public function __construct($directory) + { + $this->setDirectory($directory); + } + + /** + * @return string the directory to be checked + */ + public function getDirectory() + { + return $this->_directory; + } + + /** + * @param string the directory to be checked + * @throws TInvalidDataValueException if the directory does not exist + */ + public function setDirectory($directory) + { + if(($path=realpath($directory))===false || !is_dir($path)) + throw new TInvalidDataValueException('directorycachedependency_directory_invalid',$directory); + $this->_directory=$path; + $this->_timestamps=$this->generateTimestamps($path); + } + + /** + * @return boolean whether the subdirectories of the directory will also be checked. + * It defaults to true. + */ + public function getRecursiveCheck() + { + return $this->_recursiveCheck; + } + + /** + * @param boolean whether the subdirectories of the directory will also be checked. + */ + public function setRecursiveCheck($value) + { + $this->_recursiveCheck=TPropertyValue::ensureBoolean($value); + } + + /** + * @return int the depth of the subdirectories to be checked. + * It defaults to -1, meaning unlimited depth. + */ + public function getRecursiveLevel() + { + return $this->_recursiveLevel; + } + + /** + * Sets a value indicating the depth of the subdirectories to be checked. + * This is meaningful only when {@link getRecursiveCheck RecursiveCheck} + * is true. + * @param int the depth of the subdirectories to be checked. + * If the value is less than 0, it means unlimited depth. + * If the value is 0, it means checking the files directly under the specified directory. + */ + public function setRecursiveLevel($value) + { + $this->_recursiveLevel=TPropertyValue::ensureInteger($value); + } + + /** + * Performs the actual dependency checking. + * This method returns true if the directory is changed. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + return $this->generateTimestamps($this->_directory)!=$this->_timestamps; + } + + /** + * Checks to see if the file should be checked for dependency. + * This method is invoked when dependency of the whole directory is being checked. + * By default, it always returns true, meaning the file should be checked. + * You may override this method to check only certain files. + * @param string the name of the file that may be checked for dependency. + * @return boolean whether this file should be checked. + */ + protected function validateFile($fileName) + { + return true; + } + + /** + * Checks to see if the specified subdirectory should be checked for dependency. + * This method is invoked when dependency of the whole directory is being checked. + * By default, it always returns true, meaning the subdirectory should be checked. + * You may override this method to check only certain subdirectories. + * @param string the name of the subdirectory that may be checked for dependency. + * @return boolean whether this subdirectory should be checked. + */ + protected function validateDirectory($directory) + { + return true; + } + + /** + * Determines the last modification time for files under the directory. + * This method may go recursively into subdirectories if + * {@link setRecursiveCheck RecursiveCheck} is set true. + * @param string the directory name + * @param int level of the recursion + * @return array list of file modification time indexed by the file path + */ + protected function generateTimestamps($directory,$level=0) + { + if(($dir=opendir($directory))===false) + throw new TIOException('directorycachedependency_directory_invalid',$directory); + $timestamps=array(); + while(($file=readdir($dir))!==false) + { + $path=$directory.DIRECTORY_SEPARATOR.$file; + if($file==='.' || $file==='..') + continue; + else if(is_dir($path)) + { + if(($this->_recursiveLevel<0 || $level<$this->_recursiveLevel) && $this->validateDirectory($path)) + $timestamps=array_merge($this->generateTimestamps($path,$level+1)); + } + else if($this->validateFile($path)) + $timestamps[$path]=filemtime($path); + } + closedir($dir); + return $timestamps; + } +} + + +/** + * TGlobalStateCacheDependency class. + * + * TGlobalStateCacheDependency checks if a global state is changed or not. + * If the global state is changed, the dependency is reported as changed. + * To specify which global state this dependency should check with, + * set {@link setStateName StateName} to the name of the global state. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.1.0 + */ +class TGlobalStateCacheDependency extends TCacheDependency +{ + private $_stateName; + private $_stateValue; + + /** + * Constructor. + * @param string the name of the global state + */ + public function __construct($name) + { + $this->setStateName($name); + } + + /** + * @return string the name of the global state + */ + public function getStateName() + { + return $this->_stateName; + } + + /** + * @param string the name of the global state + * @see TApplication::setGlobalState + */ + public function setStateName($value) + { + $this->_stateName=$value; + $this->_stateValue=Prado::getApplication()->getGlobalState($value); + } + + /** + * Performs the actual dependency checking. + * This method returns true if the specified global state is changed. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + return $this->_stateValue!==Prado::getApplication()->getGlobalState($this->_stateName); + } +} + + +/** + * TChainedCacheDependency class. + * + * TChainedCacheDependency represents a list of cache dependency objects + * and performs the dependency checking based on the checking results of + * these objects. If any of them reports a dependency change, TChainedCacheDependency + * will return true for the checking. + * + * To add dependencies to TChainedCacheDependency, use {@link getDependencies Dependencies} + * which gives a {@link TCacheDependencyList} instance and can be used like an array + * (see {@link TList} for more details}). + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.1.0 + */ +class TChainedCacheDependency extends TCacheDependency +{ + private $_dependencies=null; + + /** + * @return TCacheDependencyList list of dependency objects + */ + public function getDependencies() + { + if($this->_dependencies===null) + $this->_dependencies=new TCacheDependencyList; + return $this->_dependencies; + } + + /** + * Performs the actual dependency checking. + * This method returns true if any of the dependency objects + * reports a dependency change. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + if($this->_dependencies!==null) + { + foreach($this->_dependencies as $dependency) + if($dependency->getHasChanged()) + return true; + } + return false; + } +} + + +/** + * TApplicationStateCacheDependency class. + * + * TApplicationStateCacheDependency performs dependency checking based on + * the mode of the currently running PRADO application. + * The dependency is reportedly as unchanged if and only if the application + * is running in performance mode. + * + * You may chain this dependency together with other dependencies + * so that only when the application is not in performance mode the other dependencies + * will be checked. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.1.0 + */ +class TApplicationStateCacheDependency extends TCacheDependency +{ + /** + * Performs the actual dependency checking. + * This method returns true if the currently running application is not in performance mode. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + return Prado::getApplication()->getMode()!==TApplicationMode::Performance; + } +} + +/** + * TCacheDependencyList class. + * + * TCacheDependencyList represents a list of cache dependency objects. + * Only objects implementing {@link ICacheDependency} can be added into this list. + * + * TCacheDependencyList can be used like an array. See {@link TList} + * for more details. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.1.0 + */ +class TCacheDependencyList extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional type checking + * for each newly added item. + * @param integer the specified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a dependency instance + */ + public function insertAt($index,$item) + { + if($item instanceof ICacheDependency) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('cachedependencylist_cachedependency_required'); + } +} + +?> diff --git a/framework/Caching/TDbCache.php b/framework/Caching/TDbCache.php index 7a149346..b4ddd086 100644 --- a/framework/Caching/TDbCache.php +++ b/framework/Caching/TDbCache.php @@ -1,578 +1,578 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Caching - */ - -Prado::using('System.Data.TDbConnection'); - -/** - * TDbCache class - * - * TDbCache implements a cache application module by storing cached data in a database. - * - * TDbCache relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to retrieve - * data from databases. In order to use TDbCache, you need to enable the PDO extension - * as well as the corresponding PDO DB driver. For example, to use SQLite database - * to store cached data, you need both php_pdo and php_pdo_sqlite extensions. - * - * By default, TDbCache creates and uses an SQLite database under the application - * runtime directory. You may change this default setting by specifying the following - * properties: - * - {@link setConnectionID ConnectionID} or - * - {@link setConnectionString ConnectionString}, {@link setUsername Username} and {@link setPassword Pasword}. - * - * The cached data is stored in a table in the specified database. - * By default, the name of the table is called 'pradocache'. If the table does not - * exist in the database, it will be automatically created with the following structure: - * - * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT) - * CREATE INDEX IX_itemkey ON pradocache (itemkey) - * CREATE INDEX IX_expire ON pradocache (expire) - * - * - * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable - * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.) - * - * Important: Make sure that the indices are non-unique! - * - * If you want to change the cache table name, or if you want to create the table by yourself, - * you may set {@link setCacheTableName CacheTableName} and {@link setAutoCreateCacheTable AutoCreateCacheTableName} properties. - * - * {@link setFlushInterval FlushInterval} control how often expired items will be removed from cache. - * If you prefer to remove expired items manualy e.g. via cronjob you can disable automatic deletion by setting FlushInterval to '0'. - * - * The following basic cache operations are implemented: - * - {@link get} : retrieve the value with a key (if any) from cache - * - {@link set} : store the value with a key into cache - * - {@link add} : store the value only if cache does not have this key - * - {@link delete} : delete the value with the specified key from cache - * - {@link flush} : delete all values from cache - * - * Each value is associated with an expiration time. The {@link get} operation - * ensures that any expired value will not be returned. The expiration time by - * the number of seconds. A expiration time 0 represents never expire. - * - * By definition, cache does not ensure the existence of a value - * even if it never expires. Cache is not meant to be an persistent storage. - * - * Do not use the same database file for multiple applications using TDbCache. - * Also note, cache is shared by all user sessions of an application. - * - * Some usage examples of TDbCache are as follows, - * - * $cache=new TDbCache; // TDbCache may also be loaded as a Prado application module - * $cache->init(null); - * $cache->add('object',$object); - * $object2=$cache->get('object'); - * - * - * If loaded, TDbCache will register itself with {@link TApplication} as the - * cache module. It can be accessed via {@link TApplication::getCache()}. - * - * TDbCache may be configured in application configuration file as follows - * - * - * - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.1.0 - */ -class TDbCache extends TCache -{ - /** - * @var string the ID of TDataSourceConfig module - */ - private $_connID=''; - /** - * @var TDbConnection the DB connection instance - */ - private $_db; - /** - * @var string name of the DB cache table - */ - private $_cacheTable='pradocache'; - /** - * @var integer Interval expired items will be removed from cache - */ - private $_flushInterval=60; - /** - * @var boolean - */ - private $_cacheInitialized = false; - /** - * @var boolean - */ - private $_createCheck= false; - /** - * @var boolean whether the cache DB table should be created automatically - */ - private $_autoCreate=true; - private $_username=''; - private $_password=''; - private $_connectionString=''; - - /** - * Destructor. - * Disconnect the db connection. - */ - public function __destruct() - { - if($this->_db!==null) - $this->_db->setActive(false); - } - - /** - * Initializes this module. - * This method is required by the IModule interface. - * attach {@link doInitializeCache} to TApplication.OnLoadStateComplete event - * attach {@link doFlushCacheExpired} to TApplication.OnSaveState event - * - * @param TXmlElement configuration for this module, can be null - */ - public function init($config) - { - $this -> getApplication() -> attachEventHandler('OnLoadStateComplete', array($this, 'doInitializeCache')); - $this -> getApplication() -> attachEventHandler('OnSaveState', array($this, 'doFlushCacheExpired')); - parent::init($config); - } - - /** - * Event listener for TApplication.OnSaveState - * @return void - * @since 3.1.5 - * @see flushCacheExpired - */ - public function doFlushCacheExpired() - { - $this->flushCacheExpired(false); - } - - /** - * Event listener for TApplication.OnLoadStateComplete - * - * @return void - * @since 3.1.5 - * @see initializeCache - */ - public function doInitializeCache() - { - $this->initializeCache(); - } - - /** - * Initialize TDbCache - * - * If {@link setAutoCreateCacheTable AutoCreateCacheTableName} is 'true' check existence of cache table - * and create table if does not exist. - * - * @param boolean Force override global state check - * @return void - * @throws TConfigurationException if any error happens during creating database or cache table. - * @since 3.1.5 - */ - protected function initializeCache($force=false) - { - if($this->_cacheInitialized && !$force) return; - $db=$this->getDbConnection(); - try - { - $key = 'TDbCache:' . $this->_cacheTable . ':created'; - if($force) - $this -> _createCheck = false; - else - $this -> _createCheck = $this -> getApplication() -> getGlobalState($key, 0); - - if($this->_autoCreate && !$this -> _createCheck) { - - Prado::trace(($force ? 'Force initializing: ' : 'Initializing: ') . $this -> id . ', ' . $this->_cacheTable, 'System.Caching.TDbCache'); - - $sql='SELECT 1 FROM '.$this->_cacheTable.' WHERE 0=1'; - $db->createCommand($sql)->queryScalar(); - - $this -> _createCheck = true; - $this -> getApplication() -> setGlobalState($key, time()); - } - } - catch(Exception $e) - { - // DB table not exists - if($this->_autoCreate) - { - Prado::trace('Autocreate: ' . $this->_cacheTable, 'System.Caching.TDbCache'); - - $driver=$db->getDriverName(); - if($driver==='mysql') - $blob='LONGBLOB'; - else if($driver==='pgsql') - $blob='BYTEA'; - else - $blob='BLOB'; - - $sql='CREATE TABLE '.$this->_cacheTable." (itemkey CHAR(128) PRIMARY KEY, value $blob, expire INTEGER)"; - $db->createCommand($sql)->execute(); - - $sql='CREATE INDEX IX_expire ON ' . $this->_cacheTable . ' (expire)'; - $db->createCommand($sql)->execute(); - - $this -> _createCheck = true; - $this -> getApplication() -> setGlobalState($key, time()); - } - else - throw new TConfigurationException('db_cachetable_inexistent',$this->_cacheTable); - } - $this->_cacheInitialized = true; - } - - /** - * Flush expired values from cache depending on {@link setFlushInterval FlushInterval} - * @param boolean override {@link setFlushInterval FlushInterval} and force deletion of expired items - * @return void - * @since 3.1.5 - */ - public function flushCacheExpired($force=false) - { - $interval = $this -> getFlushInterval(); - if(!$force && $interval === 0) return; - - $key = 'TDbCache:' . $this->_cacheTable . ':flushed'; - $now = time(); - $next = $interval + (integer)$this -> getApplication() -> getGlobalState($key, 0); - - if($force || $next <= $now) - { - if(!$this->_cacheInitialized) $this->initializeCache(); - Prado::trace(($force ? 'Force flush of expired items: ' : 'Flush expired items: ') . $this -> id . ', ' . $this->_cacheTable, 'System.Caching.TDbCache'); - $sql='DELETE FROM '.$this->_cacheTable.' WHERE expire<>0 AND expire<'.$now; - $this->getDbConnection()->createCommand($sql)->execute(); - $this -> getApplication() -> setGlobalState($key, $now); - } - } - - /** - * @return integer Interval in sec expired items will be removed from cache. Default to 60 - * @since 3.1.5 - */ - public function getFlushInterval() - { - return $this->_flushInterval; - } - - /** - * Sets interval expired items will be removed from cache - * - * To disable automatic deletion of expired items, - * e.g. for external flushing via cron you can set value to '0' - * - * @param integer Interval in sec - * @since 3.1.5 - */ - public function setFlushInterval($value) - { - $this->_flushInterval = (integer) $value; - } - - /** - * Creates the DB connection. - * @param string the module ID for TDataSourceConfig - * @return TDbConnection the created DB connection - * @throws TConfigurationException if module ID is invalid or empty - */ - protected function createDbConnection() - { - if($this->_connID!=='') - { - $config=$this->getApplication()->getModule($this->_connID); - if($config instanceof TDataSourceConfig) - return $config->getDbConnection(); - else - throw new TConfigurationException('dbcache_connectionid_invalid',$this->_connID); - } - else - { - $db=new TDbConnection; - if($this->_connectionString!=='') - { - $db->setConnectionString($this->_connectionString); - if($this->_username!=='') - $db->setUsername($this->_username); - if($this->_password!=='') - $db->setPassword($this->_password); - } - else - { - // default to SQLite3 database - $dbFile=$this->getApplication()->getRuntimePath().'/sqlite3.cache'; - $db->setConnectionString('sqlite:'.$dbFile); - } - return $db; - } - } - - /** - * @return TDbConnection the DB connection instance - */ - public function getDbConnection() - { - if($this->_db===null) - $this->_db=$this->createDbConnection(); - - $this->_db->setActive(true); - return $this->_db; - } - - /** - * @return string the ID of a {@link TDataSourceConfig} module. Defaults to empty string, meaning not set. - * @since 3.1.1 - */ - public function getConnectionID() - { - return $this->_connID; - } - - /** - * Sets the ID of a TDataSourceConfig module. - * The datasource module will be used to establish the DB connection for this cache module. - * The database connection can also be specified via {@link setConnectionString ConnectionString}. - * When both ConnectionID and ConnectionString are specified, the former takes precedence. - * @param string ID of the {@link TDataSourceConfig} module - * @since 3.1.1 - */ - public function setConnectionID($value) - { - $this->_connID=$value; - } - - /** - * @return string The Data Source Name, or DSN, contains the information required to connect to the database. - */ - public function getConnectionString() - { - return $this->_connectionString; - } - - /** - * @param string The Data Source Name, or DSN, contains the information required to connect to the database. - * @see http://www.php.net/manual/en/function.pdo-construct.php - */ - public function setConnectionString($value) - { - $this->_connectionString=$value; - } - - /** - * @return string the username for establishing DB connection. Defaults to empty string. - */ - public function getUsername() - { - return $this->_username; - } - - /** - * @param string the username for establishing DB connection - */ - public function setUsername($value) - { - $this->_username=$value; - } - - /** - * @return string the password for establishing DB connection. Defaults to empty string. - */ - public function getPassword() - { - return $this->_password; - } - - /** - * @param string the password for establishing DB connection - */ - public function setPassword($value) - { - $this->_password=$value; - } - - /** - * @return string the name of the DB table to store cache content. Defaults to 'pradocache'. - * @see setAutoCreateCacheTable - */ - public function getCacheTableName() - { - return $this->_cacheTable; - } - - /** - * Sets the name of the DB table to store cache content. - * Note, if {@link setAutoCreateCacheTable AutoCreateCacheTable} is false - * and you want to create the DB table manually by yourself, - * you need to make sure the DB table is of the following structure: - * - * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT) - * CREATE INDEX IX_itemkey ON pradocache (itemkey) - * CREATE INDEX IX_expire ON pradocache (expire) - * - * - * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable - * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.) - * - * Important: Make sure that the indices are non-unique! - * - * @param string the name of the DB table to store cache content - * @see setAutoCreateCacheTable - */ - public function setCacheTableName($value) - { - $this->_cacheTable=$value; - } - - /** - * @return boolean whether the cache DB table should be automatically created if not exists. Defaults to true. - * @see setAutoCreateCacheTable - */ - public function getAutoCreateCacheTable() - { - return $this->_autoCreate; - } - - /** - * @param boolean whether the cache DB table should be automatically created if not exists. - * @see setCacheTableName - */ - public function setAutoCreateCacheTable($value) - { - $this->_autoCreate=TPropertyValue::ensureBoolean($value); - } - - /** - * Retrieves a value from cache with a specified key. - * This is the implementation of the method declared in the parent class. - * @param string a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. - */ - protected function getValue($key) - { - if(!$this->_cacheInitialized) $this->initializeCache(); - try { - $sql='SELECT value FROM '.$this->_cacheTable.' WHERE itemkey=\''.$key.'\' AND (expire=0 OR expire>'.time().') ORDER BY expire DESC'; - $command=$this->getDbConnection()->createCommand($sql); - return Prado::unserialize($command->queryScalar()); - } - catch(Exception $e) - { - $this->initializeCache(true); - return Prado::unserialize($command->queryScalar()); - } - } - - /** - * Stores a value identified by a key in cache. - * This is the implementation of the method declared in the parent class. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - protected function setValue($key,$value,$expire) - { - $this->deleteValue($key); - return $this->addValue($key,$value,$expire); - } - - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * This is the implementation of the method declared in the parent class. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - protected function addValue($key,$value,$expire) - { - if(!$this->_cacheInitialized) $this->initializeCache(); - $expire=($expire<=0)?0:time()+$expire; - $sql="INSERT INTO {$this->_cacheTable} (itemkey,value,expire) VALUES(:key,:value,$expire)"; - try - { - $command=$this->getDbConnection()->createCommand($sql); - $command->bindValue(':key',$key,PDO::PARAM_STR); - $command->bindValue(':value',Prado::serialize($value),PDO::PARAM_LOB); - $command->execute(); - return true; - } - catch(Exception $e) - { - try - { - $this->initializeCache(true); - $command->execute(); - return true; - } - catch(Exception $e) - { - return false; - } - } - } - - /** - * Deletes a value with the specified key from cache - * This is the implementation of the method declared in the parent class. - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - protected function deleteValue($key) - { - if(!$this->_cacheInitialized) $this->initializeCache(); - try - { - $command=$this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable} WHERE itemkey=:key"); - $command->bindValue(':key',$key,PDO::PARAM_STR); - $command->execute(); - return true; - } - catch(Exception $e) - { - $this->initializeCache(true); - $command->execute(); - return true; - } - } - - /** - * Deletes all values from cache. - * Be careful of performing this operation if the cache is shared by multiple applications. - */ - public function flush() - { - if(!$this->_cacheInitialized) $this->initializeCache(); - try - { - $command = $this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable}"); - $command->execute(); - } - catch(Exception $e) - { - try - { - $this->initializeCache(true); - $command->execute(); - return true; - } - catch(Exception $e) - { - return false; - } - } - return true; - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Caching + */ + +Prado::using('System.Data.TDbConnection'); + +/** + * TDbCache class + * + * TDbCache implements a cache application module by storing cached data in a database. + * + * TDbCache relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to retrieve + * data from databases. In order to use TDbCache, you need to enable the PDO extension + * as well as the corresponding PDO DB driver. For example, to use SQLite database + * to store cached data, you need both php_pdo and php_pdo_sqlite extensions. + * + * By default, TDbCache creates and uses an SQLite database under the application + * runtime directory. You may change this default setting by specifying the following + * properties: + * - {@link setConnectionID ConnectionID} or + * - {@link setConnectionString ConnectionString}, {@link setUsername Username} and {@link setPassword Pasword}. + * + * The cached data is stored in a table in the specified database. + * By default, the name of the table is called 'pradocache'. If the table does not + * exist in the database, it will be automatically created with the following structure: + * + * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT) + * CREATE INDEX IX_itemkey ON pradocache (itemkey) + * CREATE INDEX IX_expire ON pradocache (expire) + * + * + * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable + * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.) + * + * Important: Make sure that the indices are non-unique! + * + * If you want to change the cache table name, or if you want to create the table by yourself, + * you may set {@link setCacheTableName CacheTableName} and {@link setAutoCreateCacheTable AutoCreateCacheTableName} properties. + * + * {@link setFlushInterval FlushInterval} control how often expired items will be removed from cache. + * If you prefer to remove expired items manualy e.g. via cronjob you can disable automatic deletion by setting FlushInterval to '0'. + * + * The following basic cache operations are implemented: + * - {@link get} : retrieve the value with a key (if any) from cache + * - {@link set} : store the value with a key into cache + * - {@link add} : store the value only if cache does not have this key + * - {@link delete} : delete the value with the specified key from cache + * - {@link flush} : delete all values from cache + * + * Each value is associated with an expiration time. The {@link get} operation + * ensures that any expired value will not be returned. The expiration time by + * the number of seconds. A expiration time 0 represents never expire. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * Do not use the same database file for multiple applications using TDbCache. + * Also note, cache is shared by all user sessions of an application. + * + * Some usage examples of TDbCache are as follows, + * + * $cache=new TDbCache; // TDbCache may also be loaded as a Prado application module + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TDbCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TDbCache may be configured in application configuration file as follows + * + * + * + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.1.0 + */ +class TDbCache extends TCache +{ + /** + * @var string the ID of TDataSourceConfig module + */ + private $_connID=''; + /** + * @var TDbConnection the DB connection instance + */ + private $_db; + /** + * @var string name of the DB cache table + */ + private $_cacheTable='pradocache'; + /** + * @var integer Interval expired items will be removed from cache + */ + private $_flushInterval=60; + /** + * @var boolean + */ + private $_cacheInitialized = false; + /** + * @var boolean + */ + private $_createCheck= false; + /** + * @var boolean whether the cache DB table should be created automatically + */ + private $_autoCreate=true; + private $_username=''; + private $_password=''; + private $_connectionString=''; + + /** + * Destructor. + * Disconnect the db connection. + */ + public function __destruct() + { + if($this->_db!==null) + $this->_db->setActive(false); + } + + /** + * Initializes this module. + * This method is required by the IModule interface. + * attach {@link doInitializeCache} to TApplication.OnLoadStateComplete event + * attach {@link doFlushCacheExpired} to TApplication.OnSaveState event + * + * @param TXmlElement configuration for this module, can be null + */ + public function init($config) + { + $this -> getApplication() -> attachEventHandler('OnLoadStateComplete', array($this, 'doInitializeCache')); + $this -> getApplication() -> attachEventHandler('OnSaveState', array($this, 'doFlushCacheExpired')); + parent::init($config); + } + + /** + * Event listener for TApplication.OnSaveState + * @return void + * @since 3.1.5 + * @see flushCacheExpired + */ + public function doFlushCacheExpired() + { + $this->flushCacheExpired(false); + } + + /** + * Event listener for TApplication.OnLoadStateComplete + * + * @return void + * @since 3.1.5 + * @see initializeCache + */ + public function doInitializeCache() + { + $this->initializeCache(); + } + + /** + * Initialize TDbCache + * + * If {@link setAutoCreateCacheTable AutoCreateCacheTableName} is 'true' check existence of cache table + * and create table if does not exist. + * + * @param boolean Force override global state check + * @return void + * @throws TConfigurationException if any error happens during creating database or cache table. + * @since 3.1.5 + */ + protected function initializeCache($force=false) + { + if($this->_cacheInitialized && !$force) return; + $db=$this->getDbConnection(); + try + { + $key = 'TDbCache:' . $this->_cacheTable . ':created'; + if($force) + $this -> _createCheck = false; + else + $this -> _createCheck = $this -> getApplication() -> getGlobalState($key, 0); + + if($this->_autoCreate && !$this -> _createCheck) { + + Prado::trace(($force ? 'Force initializing: ' : 'Initializing: ') . $this -> id . ', ' . $this->_cacheTable, 'System.Caching.TDbCache'); + + $sql='SELECT 1 FROM '.$this->_cacheTable.' WHERE 0=1'; + $db->createCommand($sql)->queryScalar(); + + $this -> _createCheck = true; + $this -> getApplication() -> setGlobalState($key, time()); + } + } + catch(Exception $e) + { + // DB table not exists + if($this->_autoCreate) + { + Prado::trace('Autocreate: ' . $this->_cacheTable, 'System.Caching.TDbCache'); + + $driver=$db->getDriverName(); + if($driver==='mysql') + $blob='LONGBLOB'; + else if($driver==='pgsql') + $blob='BYTEA'; + else + $blob='BLOB'; + + $sql='CREATE TABLE '.$this->_cacheTable." (itemkey CHAR(128) PRIMARY KEY, value $blob, expire INTEGER)"; + $db->createCommand($sql)->execute(); + + $sql='CREATE INDEX IX_expire ON ' . $this->_cacheTable . ' (expire)'; + $db->createCommand($sql)->execute(); + + $this -> _createCheck = true; + $this -> getApplication() -> setGlobalState($key, time()); + } + else + throw new TConfigurationException('db_cachetable_inexistent',$this->_cacheTable); + } + $this->_cacheInitialized = true; + } + + /** + * Flush expired values from cache depending on {@link setFlushInterval FlushInterval} + * @param boolean override {@link setFlushInterval FlushInterval} and force deletion of expired items + * @return void + * @since 3.1.5 + */ + public function flushCacheExpired($force=false) + { + $interval = $this -> getFlushInterval(); + if(!$force && $interval === 0) return; + + $key = 'TDbCache:' . $this->_cacheTable . ':flushed'; + $now = time(); + $next = $interval + (integer)$this -> getApplication() -> getGlobalState($key, 0); + + if($force || $next <= $now) + { + if(!$this->_cacheInitialized) $this->initializeCache(); + Prado::trace(($force ? 'Force flush of expired items: ' : 'Flush expired items: ') . $this -> id . ', ' . $this->_cacheTable, 'System.Caching.TDbCache'); + $sql='DELETE FROM '.$this->_cacheTable.' WHERE expire<>0 AND expire<'.$now; + $this->getDbConnection()->createCommand($sql)->execute(); + $this -> getApplication() -> setGlobalState($key, $now); + } + } + + /** + * @return integer Interval in sec expired items will be removed from cache. Default to 60 + * @since 3.1.5 + */ + public function getFlushInterval() + { + return $this->_flushInterval; + } + + /** + * Sets interval expired items will be removed from cache + * + * To disable automatic deletion of expired items, + * e.g. for external flushing via cron you can set value to '0' + * + * @param integer Interval in sec + * @since 3.1.5 + */ + public function setFlushInterval($value) + { + $this->_flushInterval = (integer) $value; + } + + /** + * Creates the DB connection. + * @param string the module ID for TDataSourceConfig + * @return TDbConnection the created DB connection + * @throws TConfigurationException if module ID is invalid or empty + */ + protected function createDbConnection() + { + if($this->_connID!=='') + { + $config=$this->getApplication()->getModule($this->_connID); + if($config instanceof TDataSourceConfig) + return $config->getDbConnection(); + else + throw new TConfigurationException('dbcache_connectionid_invalid',$this->_connID); + } + else + { + $db=new TDbConnection; + if($this->_connectionString!=='') + { + $db->setConnectionString($this->_connectionString); + if($this->_username!=='') + $db->setUsername($this->_username); + if($this->_password!=='') + $db->setPassword($this->_password); + } + else + { + // default to SQLite3 database + $dbFile=$this->getApplication()->getRuntimePath().'/sqlite3.cache'; + $db->setConnectionString('sqlite:'.$dbFile); + } + return $db; + } + } + + /** + * @return TDbConnection the DB connection instance + */ + public function getDbConnection() + { + if($this->_db===null) + $this->_db=$this->createDbConnection(); + + $this->_db->setActive(true); + return $this->_db; + } + + /** + * @return string the ID of a {@link TDataSourceConfig} module. Defaults to empty string, meaning not set. + * @since 3.1.1 + */ + public function getConnectionID() + { + return $this->_connID; + } + + /** + * Sets the ID of a TDataSourceConfig module. + * The datasource module will be used to establish the DB connection for this cache module. + * The database connection can also be specified via {@link setConnectionString ConnectionString}. + * When both ConnectionID and ConnectionString are specified, the former takes precedence. + * @param string ID of the {@link TDataSourceConfig} module + * @since 3.1.1 + */ + public function setConnectionID($value) + { + $this->_connID=$value; + } + + /** + * @return string The Data Source Name, or DSN, contains the information required to connect to the database. + */ + public function getConnectionString() + { + return $this->_connectionString; + } + + /** + * @param string The Data Source Name, or DSN, contains the information required to connect to the database. + * @see http://www.php.net/manual/en/function.pdo-construct.php + */ + public function setConnectionString($value) + { + $this->_connectionString=$value; + } + + /** + * @return string the username for establishing DB connection. Defaults to empty string. + */ + public function getUsername() + { + return $this->_username; + } + + /** + * @param string the username for establishing DB connection + */ + public function setUsername($value) + { + $this->_username=$value; + } + + /** + * @return string the password for establishing DB connection. Defaults to empty string. + */ + public function getPassword() + { + return $this->_password; + } + + /** + * @param string the password for establishing DB connection + */ + public function setPassword($value) + { + $this->_password=$value; + } + + /** + * @return string the name of the DB table to store cache content. Defaults to 'pradocache'. + * @see setAutoCreateCacheTable + */ + public function getCacheTableName() + { + return $this->_cacheTable; + } + + /** + * Sets the name of the DB table to store cache content. + * Note, if {@link setAutoCreateCacheTable AutoCreateCacheTable} is false + * and you want to create the DB table manually by yourself, + * you need to make sure the DB table is of the following structure: + * + * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT) + * CREATE INDEX IX_itemkey ON pradocache (itemkey) + * CREATE INDEX IX_expire ON pradocache (expire) + * + * + * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable + * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.) + * + * Important: Make sure that the indices are non-unique! + * + * @param string the name of the DB table to store cache content + * @see setAutoCreateCacheTable + */ + public function setCacheTableName($value) + { + $this->_cacheTable=$value; + } + + /** + * @return boolean whether the cache DB table should be automatically created if not exists. Defaults to true. + * @see setAutoCreateCacheTable + */ + public function getAutoCreateCacheTable() + { + return $this->_autoCreate; + } + + /** + * @param boolean whether the cache DB table should be automatically created if not exists. + * @see setCacheTableName + */ + public function setAutoCreateCacheTable($value) + { + $this->_autoCreate=TPropertyValue::ensureBoolean($value); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + if(!$this->_cacheInitialized) $this->initializeCache(); + try { + $sql='SELECT value FROM '.$this->_cacheTable.' WHERE itemkey=\''.$key.'\' AND (expire=0 OR expire>'.time().') ORDER BY expire DESC'; + $command=$this->getDbConnection()->createCommand($sql); + return Prado::unserialize($command->queryScalar()); + } + catch(Exception $e) + { + $this->initializeCache(true); + return Prado::unserialize($command->queryScalar()); + } + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + $this->deleteValue($key); + return $this->addValue($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + if(!$this->_cacheInitialized) $this->initializeCache(); + $expire=($expire<=0)?0:time()+$expire; + $sql="INSERT INTO {$this->_cacheTable} (itemkey,value,expire) VALUES(:key,:value,$expire)"; + try + { + $command=$this->getDbConnection()->createCommand($sql); + $command->bindValue(':key',$key,PDO::PARAM_STR); + $command->bindValue(':value',Prado::serialize($value),PDO::PARAM_LOB); + $command->execute(); + return true; + } + catch(Exception $e) + { + try + { + $this->initializeCache(true); + $command->execute(); + return true; + } + catch(Exception $e) + { + return false; + } + } + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + if(!$this->_cacheInitialized) $this->initializeCache(); + try + { + $command=$this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable} WHERE itemkey=:key"); + $command->bindValue(':key',$key,PDO::PARAM_STR); + $command->execute(); + return true; + } + catch(Exception $e) + { + $this->initializeCache(true); + $command->execute(); + return true; + } + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + if(!$this->_cacheInitialized) $this->initializeCache(); + try + { + $command = $this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable}"); + $command->execute(); + } + catch(Exception $e) + { + try + { + $this->initializeCache(true); + $command->execute(); + return true; + } + catch(Exception $e) + { + return false; + } + } + return true; + } +} diff --git a/framework/Caching/TSqliteCache.php b/framework/Caching/TSqliteCache.php index 2668e3cc..6cba94ae 100644 --- a/framework/Caching/TSqliteCache.php +++ b/framework/Caching/TSqliteCache.php @@ -1,224 +1,224 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Caching - */ - -/** - * TSqliteCache class - * - * TSqliteCache implements a cache application module based on SQLite database. - * - * THIS CLASS IS DEPRECATED since it relies on the sqlite PHP extension, that is - * no longer loaded by default since PHP 5.1. You are discouraged from using it: - * use {@link TDbCache} instead. - * - * Since PRADO v3.1.0, a new DB-based cache module called {@link TDbCache} - * is provided. If you have PDO extension installed, you may consider using - * the new cache module instead as it allows you to use different database - * to store the cached data. - * - * The database file is specified by the {@link setDbFile DbFile} property. - * If not set, the database file will be created under the system state path. - * If the specified database file does not exist, it will be created automatically. - * Make sure the directory containing the specified DB file and the file itself is - * writable by the Web server process. - * - * The following basic cache operations are implemented: - * - {@link get} : retrieve the value with a key (if any) from cache - * - {@link set} : store the value with a key into cache - * - {@link add} : store the value only if cache does not have this key - * - {@link delete} : delete the value with the specified key from cache - * - {@link flush} : delete all values from cache - * - * Each value is associated with an expiration time. The {@link get} operation - * ensures that any expired value will not be returned. The expiration time by - * the number of seconds. A expiration time 0 represents never expire. - * - * By definition, cache does not ensure the existence of a value - * even if it never expires. Cache is not meant to be an persistent storage. - * - * Do not use the same database file for multiple applications using TSqliteCache. - * Also note, cache is shared by all user sessions of an application. - * - * Some usage examples of TSqliteCache are as follows, - * - * $cache=new TSqliteCache; // TSqliteCache may also be loaded as a Prado application module - * $cache->setDbFile($dbFilePath); - * $cache->init(null); - * $cache->add('object',$object); - * $object2=$cache->get('object'); - * - * - * If loaded, TSqliteCache will register itself with {@link TApplication} as the - * cache module. It can be accessed via {@link TApplication::getCache()}. - * - * TSqliteCache may be configured in application configuration file as follows - * - * - * - * where {@link getDbFile DbFile} is a property specifying the location of the - * SQLite DB file (in the namespace format). - * - * @author Qiang Xue - * @version $Id$ - * @package System.Caching - * @since 3.0 - */ -class TSqliteCache extends TCache -{ - /** - * name of the table storing cache data - */ - const CACHE_TABLE='cache'; - /** - * extension of the db file name - */ - const DB_FILE_EXT='.db'; - - /** - * @var boolean if the module has been initialized - */ - private $_initialized=false; - /** - * @var SQLiteDatabase the sqlite database instance - */ - private $_db=null; - /** - * @var string the database file name - */ - private $_file=null; - - /** - * Destructor. - * Disconnect the db connection. - */ - public function __destruct() - { - $this->_db=null; - } - - /** - * Initializes this module. - * This method is required by the IModule interface. It checks if the DbFile - * property is set, and creates a SQLiteDatabase instance for it. - * The database or the cache table does not exist, they will be created. - * Expired values are also deleted. - * @param TXmlElement configuration for this module, can be null - * @throws TConfigurationException if sqlite extension is not installed, - * DbFile is set invalid, or any error happens during creating database or cache table. - */ - public function init($config) - { - if(!function_exists('sqlite_open')) - throw new TConfigurationException('sqlitecache_extension_required'); - if($this->_file===null) - $this->_file=$this->getApplication()->getRuntimePath().'/sqlite.cache'; - $error=''; - if(($this->_db=new SQLiteDatabase($this->_file,0666,$error))===false) - throw new TConfigurationException('sqlitecache_connection_failed',$error); - if(@$this->_db->query('DELETE FROM '.self::CACHE_TABLE.' WHERE expire<>0 AND expire<'.time())===false) - { - if($this->_db->query('CREATE TABLE '.self::CACHE_TABLE.' (key CHAR(128) PRIMARY KEY, value BLOB, expire INT)')===false) - throw new TConfigurationException('sqlitecache_table_creation_failed',sqlite_error_string(sqlite_last_error())); - } - $this->_initialized=true; - parent::init($config); - } - - /** - * @return string database file path (in namespace form) - */ - public function getDbFile() - { - return $this->_file; - } - - /** - * @param string database file path (in namespace form) - * @throws TInvalidOperationException if the module is already initialized - * @throws TConfigurationException if the file is not in proper namespace format - */ - public function setDbFile($value) - { - if($this->_initialized) - throw new TInvalidOperationException('sqlitecache_dbfile_unchangeable'); - else if(($this->_file=Prado::getPathOfNamespace($value,self::DB_FILE_EXT))===null) - throw new TConfigurationException('sqlitecache_dbfile_invalid',$value); - } - - /** - * Retrieves a value from cache with a specified key. - * This is the implementation of the method declared in the parent class. - * @param string a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. - */ - protected function getValue($key) - { - $sql='SELECT value FROM '.self::CACHE_TABLE.' WHERE key=\''.$key.'\' AND (expire=0 OR expire>'.time().') LIMIT 1'; - if(($ret=$this->_db->query($sql))!=false && ($row=$ret->fetch(SQLITE_ASSOC))!==false) - return Prado::unserialize($row['value']); - else - return false; - } - - /** - * Stores a value identified by a key in cache. - * This is the implementation of the method declared in the parent class. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - protected function setValue($key,$value,$expire) - { - $expire=($expire<=0)?0:time()+$expire; - $sql='REPLACE INTO '.self::CACHE_TABLE.' VALUES(\''.$key.'\',\''.sqlite_escape_string(Prado::serialize($value)).'\','.$expire.')'; - return $this->_db->query($sql)!==false; - } - - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * This is the implementation of the method declared in the parent class. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - protected function addValue($key,$value,$expire) - { - $expire=($expire<=0)?0:time()+$expire; - $sql='INSERT INTO '.self::CACHE_TABLE.' VALUES(\''.$key.'\',\''.sqlite_escape_string(Prado::serialize($value)).'\','.$expire.')'; - return @$this->_db->query($sql)!==false; - } - - /** - * Deletes a value with the specified key from cache - * This is the implementation of the method declared in the parent class. - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - protected function deleteValue($key) - { - $sql='DELETE FROM '.self::CACHE_TABLE.' WHERE key=\''.$key.'\''; - return $this->_db->query($sql)!==false; - } - - /** - * Deletes all values from cache. - * Be careful of performing this operation if the cache is shared by multiple applications. - */ - public function flush() - { - return $this->_db->query('DELETE FROM '.self::CACHE_TABLE)!==false; - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Caching + */ + +/** + * TSqliteCache class + * + * TSqliteCache implements a cache application module based on SQLite database. + * + * THIS CLASS IS DEPRECATED since it relies on the sqlite PHP extension, that is + * no longer loaded by default since PHP 5.1. You are discouraged from using it: + * use {@link TDbCache} instead. + * + * Since PRADO v3.1.0, a new DB-based cache module called {@link TDbCache} + * is provided. If you have PDO extension installed, you may consider using + * the new cache module instead as it allows you to use different database + * to store the cached data. + * + * The database file is specified by the {@link setDbFile DbFile} property. + * If not set, the database file will be created under the system state path. + * If the specified database file does not exist, it will be created automatically. + * Make sure the directory containing the specified DB file and the file itself is + * writable by the Web server process. + * + * The following basic cache operations are implemented: + * - {@link get} : retrieve the value with a key (if any) from cache + * - {@link set} : store the value with a key into cache + * - {@link add} : store the value only if cache does not have this key + * - {@link delete} : delete the value with the specified key from cache + * - {@link flush} : delete all values from cache + * + * Each value is associated with an expiration time. The {@link get} operation + * ensures that any expired value will not be returned. The expiration time by + * the number of seconds. A expiration time 0 represents never expire. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * Do not use the same database file for multiple applications using TSqliteCache. + * Also note, cache is shared by all user sessions of an application. + * + * Some usage examples of TSqliteCache are as follows, + * + * $cache=new TSqliteCache; // TSqliteCache may also be loaded as a Prado application module + * $cache->setDbFile($dbFilePath); + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TSqliteCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TSqliteCache may be configured in application configuration file as follows + * + * + * + * where {@link getDbFile DbFile} is a property specifying the location of the + * SQLite DB file (in the namespace format). + * + * @author Qiang Xue + * @version $Id$ + * @package System.Caching + * @since 3.0 + */ +class TSqliteCache extends TCache +{ + /** + * name of the table storing cache data + */ + const CACHE_TABLE='cache'; + /** + * extension of the db file name + */ + const DB_FILE_EXT='.db'; + + /** + * @var boolean if the module has been initialized + */ + private $_initialized=false; + /** + * @var SQLiteDatabase the sqlite database instance + */ + private $_db=null; + /** + * @var string the database file name + */ + private $_file=null; + + /** + * Destructor. + * Disconnect the db connection. + */ + public function __destruct() + { + $this->_db=null; + } + + /** + * Initializes this module. + * This method is required by the IModule interface. It checks if the DbFile + * property is set, and creates a SQLiteDatabase instance for it. + * The database or the cache table does not exist, they will be created. + * Expired values are also deleted. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if sqlite extension is not installed, + * DbFile is set invalid, or any error happens during creating database or cache table. + */ + public function init($config) + { + if(!function_exists('sqlite_open')) + throw new TConfigurationException('sqlitecache_extension_required'); + if($this->_file===null) + $this->_file=$this->getApplication()->getRuntimePath().'/sqlite.cache'; + $error=''; + if(($this->_db=new SQLiteDatabase($this->_file,0666,$error))===false) + throw new TConfigurationException('sqlitecache_connection_failed',$error); + if(@$this->_db->query('DELETE FROM '.self::CACHE_TABLE.' WHERE expire<>0 AND expire<'.time())===false) + { + if($this->_db->query('CREATE TABLE '.self::CACHE_TABLE.' (key CHAR(128) PRIMARY KEY, value BLOB, expire INT)')===false) + throw new TConfigurationException('sqlitecache_table_creation_failed',sqlite_error_string(sqlite_last_error())); + } + $this->_initialized=true; + parent::init($config); + } + + /** + * @return string database file path (in namespace form) + */ + public function getDbFile() + { + return $this->_file; + } + + /** + * @param string database file path (in namespace form) + * @throws TInvalidOperationException if the module is already initialized + * @throws TConfigurationException if the file is not in proper namespace format + */ + public function setDbFile($value) + { + if($this->_initialized) + throw new TInvalidOperationException('sqlitecache_dbfile_unchangeable'); + else if(($this->_file=Prado::getPathOfNamespace($value,self::DB_FILE_EXT))===null) + throw new TConfigurationException('sqlitecache_dbfile_invalid',$value); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $sql='SELECT value FROM '.self::CACHE_TABLE.' WHERE key=\''.$key.'\' AND (expire=0 OR expire>'.time().') LIMIT 1'; + if(($ret=$this->_db->query($sql))!=false && ($row=$ret->fetch(SQLITE_ASSOC))!==false) + return Prado::unserialize($row['value']); + else + return false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + $expire=($expire<=0)?0:time()+$expire; + $sql='REPLACE INTO '.self::CACHE_TABLE.' VALUES(\''.$key.'\',\''.sqlite_escape_string(Prado::serialize($value)).'\','.$expire.')'; + return $this->_db->query($sql)!==false; + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + $expire=($expire<=0)?0:time()+$expire; + $sql='INSERT INTO '.self::CACHE_TABLE.' VALUES(\''.$key.'\',\''.sqlite_escape_string(Prado::serialize($value)).'\','.$expire.')'; + return @$this->_db->query($sql)!==false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + $sql='DELETE FROM '.self::CACHE_TABLE.' WHERE key=\''.$key.'\''; + return $this->_db->query($sql)!==false; + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return $this->_db->query('DELETE FROM '.self::CACHE_TABLE)!==false; + } +} + diff --git a/framework/Caching/TXCache.php b/framework/Caching/TXCache.php index 6750d5b8..31f2a4d2 100644 --- a/framework/Caching/TXCache.php +++ b/framework/Caching/TXCache.php @@ -1,131 +1,131 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id: TXCache.php 1994 2007-06-11 16:02:28Z knut $ - * @package System.Caching - */ - -/** - * TXCache class - * - * TXCache implements a cache application module based on {@link http://xcache.lighttpd.net/ xcache}. - * - * By definition, cache does not ensure the existence of a value - * even if it never expires. Cache is not meant to be an persistent storage. - * - * To use this module, the xcache PHP extension must be loaded and configured in the php.ini. - * - * Some usage examples of TXCache are as follows, - * - * $cache=new TXCache; // TXCache may also be loaded as a Prado application module - * $cache->init(null); - * $cache->add('object',$object); - * $object2=$cache->get('object'); - * - * - * If loaded, TXCache will register itself with {@link TApplication} as the - * cache module. It can be accessed via {@link TApplication::getCache()}. - * - * TXCache may be configured in application configuration file as follows - * - * - * - * - * @author Wei Zhuo - * @version $Id: TXCache.php 1994 2007-06-11 16:02:28Z knut $ - * @package System.Caching - * @since 3.1.1 - */ -class TXCache extends TCache -{ - /** - * Initializes this module. - * This method is required by the IModule interface. - * @param TXmlElement configuration for this module, can be null - * @throws TConfigurationException if xcache extension is not installed or not started, check your php.ini - */ - public function init($config) - { - if(!function_exists('xcache_isset')) - throw new TConfigurationException('xcache_extension_required'); - - $enabled = (int)ini_get('xcache.cacher') !== 0; - $var_size = (int)ini_get('xcache.var_size'); - - if(!($enabled && $var_size > 0)) - throw new TConfigurationException('xcache_extension_not_enabled'); - - parent::init($config); - } - - /** - * Retrieves a value from cache with a specified key. - * This is the implementation of the method declared in the parent class. - * @param string a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. - */ - protected function getValue($key) - { - return xcache_isset($key) ? xcache_get($key) : false; - } - - /** - * Stores a value identified by a key in cache. - * This is the implementation of the method declared in the parent class. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - protected function setValue($key,$value,$expire) - { - return xcache_set($key,$value,$expire); - } - - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * This is the implementation of the method declared in the parent class. - * - * @param string the key identifying the value to be cached - * @param string the value to be cached - * @param integer the number of seconds in which the cached value will expire. 0 means never expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - protected function addValue($key,$value,$expire) - { - return !xcache_isset($key) ? $this->setValue($key,$value,$expire) : false; - } - - /** - * Deletes a value with the specified key from cache - * This is the implementation of the method declared in the parent class. - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - protected function deleteValue($key) - { - return xcache_unset($key); - } - - /** - * Deletes all values from cache. - * Be careful of performing this operation if the cache is shared by multiple applications. - */ - public function flush() - { - for($i=0, $max=xcache_count(XC_TYPE_VAR); $i<$max; $i++) - { - if(xcache_clear_cache(XC_TYPE_VAR, $i)===false) - return false; - } - return true; - } -} - -?> + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TXCache.php 1994 2007-06-11 16:02:28Z knut $ + * @package System.Caching + */ + +/** + * TXCache class + * + * TXCache implements a cache application module based on {@link http://xcache.lighttpd.net/ xcache}. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * To use this module, the xcache PHP extension must be loaded and configured in the php.ini. + * + * Some usage examples of TXCache are as follows, + * + * $cache=new TXCache; // TXCache may also be loaded as a Prado application module + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TXCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TXCache may be configured in application configuration file as follows + * + * + * + * + * @author Wei Zhuo + * @version $Id: TXCache.php 1994 2007-06-11 16:02:28Z knut $ + * @package System.Caching + * @since 3.1.1 + */ +class TXCache extends TCache +{ + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if xcache extension is not installed or not started, check your php.ini + */ + public function init($config) + { + if(!function_exists('xcache_isset')) + throw new TConfigurationException('xcache_extension_required'); + + $enabled = (int)ini_get('xcache.cacher') !== 0; + $var_size = (int)ini_get('xcache.var_size'); + + if(!($enabled && $var_size > 0)) + throw new TConfigurationException('xcache_extension_not_enabled'); + + parent::init($config); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return xcache_isset($key) ? xcache_get($key) : false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return xcache_set($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return !xcache_isset($key) ? $this->setValue($key,$value,$expire) : false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return xcache_unset($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + for($i=0, $max=xcache_count(XC_TYPE_VAR); $i<$max; $i++) + { + if(xcache_clear_cache(XC_TYPE_VAR, $i)===false) + return false; + } + return true; + } +} + +?> -- cgit v1.2.3