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'); +	} +} + +?>  | 
