diff options
Diffstat (limited to 'framework/Caching/TCache.php')
-rw-r--r-- | framework/Caching/TCache.php | 1440 |
1 files changed, 720 insertions, 720 deletions
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 @@ -<?php
-/**
- * TCache and cache dependency classes.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @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 <qiang.xue@gmail.com>
- * @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 <qiang.xue@gmail.com>
- * @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 <qiang.xue@gmail.com>
- * @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 <qiang.xue@gmail.com>
- * @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 <qiang.xue@gmail.com>
- * @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 <qiang.xue@gmail.com>
- * @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 <qiang.xue@gmail.com>
- * @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 <qiang.xue@gmail.com>
- * @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');
- }
-}
-
-?>
+<?php +/** + * TCache and cache dependency classes. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @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 <qiang.xue@gmail.com> + * @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 <qiang.xue@gmail.com> + * @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 <qiang.xue@gmail.com> + * @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 <qiang.xue@gmail.com> + * @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 <qiang.xue@gmail.com> + * @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 <qiang.xue@gmail.com> + * @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 <qiang.xue@gmail.com> + * @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 <qiang.xue@gmail.com> + * @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'); + } +} + +?> |