diff options
Diffstat (limited to 'framework')
| -rw-r--r-- | framework/Caching/TCache.php | 439 | ||||
| -rw-r--r-- | framework/Exceptions/messages.txt | 3 | ||||
| -rw-r--r-- | framework/interfaces.php | 12 | 
3 files changed, 448 insertions, 6 deletions
| diff --git a/framework/Caching/TCache.php b/framework/Caching/TCache.php index d974fe83..ca7348d4 100644 --- a/framework/Caching/TCache.php +++ b/framework/Caching/TCache.php @@ -1,6 +1,6 @@  <?php
  /**
 - * TCache class file.
 + * TCache and cache dependency classes.
   *
   * @author Qiang Xue <qiang.xue@gmail.com>
   * @link http://www.pradosoft.com/
 @@ -10,6 +10,8 @@   * @package System.Caching
   */
 +Prado::using('System.Collections.TList');
 +
  /**
   * TCache class
   *
 @@ -229,4 +231,439 @@ abstract class TCache extends TModule implements ICache  	abstract protected function deleteValue($key);
  }
 +
 +/**
 + * TCacheDependency class.
 + *
 + * TCacheDependency is the base class implementing {@link ICacheDependency} interface.
 + * Descendant classes must implement {@link checkChanges()} 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
 +{
 +	private $_enabled=true;
 +
 +	/**
 +	 * @return boolean whether this dependency is enabled. Default value is true.
 +	 */
 +	public function getEnabled()
 +	{
 +		return $this->_enabled;
 +	}
 +
 +	/**
 +	 * Sets a value indicating whether this cache dependency is enabled or not.
 +	 * Cache dependency checking is only performed when it is enabled.
 +	 * @param boolean whether this dependency is to be enabled.
 +	 */
 +	public function setEnabled($value)
 +	{
 +		$this->_enabled=TPropertyValue::ensureBoolean($value);
 +	}
 +
 +	/**
 +	 * @return boolean whether the dependency is changed or not.
 +	 * If the dependency checking is disabled, it always returns false.
 +	 */
 +	public function getHasChanged()
 +	{
 +		return $this->_enabled ? $this->checkChanges() : false;
 +	}
 +
 +	/**
 +	 * Performs the actual dependency checking.
 +	 * This method must be implemented by child classes.
 +	 * @return boolean whether the dependency is changed or not.
 +	 */
 +	abstract protected function checkChanges();
 +}
 +
 +
 +/**
 + * 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.
 +	 */
 +	protected function checkChanges()
 +	{
 +		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.
 +	 */
 +	protected function checkChanges()
 +	{
 +		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 recurisvely 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.
 +	 */
 +	protected function checkChanges()
 +	{
 +		return $this->_stateValue!==Prado::getApplication()->getGlobalState($value);
 +	}
 +}
 +
 +
 +/**
 + * 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.
 +	 */
 +	protected function checkChanges()
 +	{
 +		if($this->_dependencies!==null)
 +		{
 +			foreach($this->_dependencies as $dependency)
 +				if($dependency->getHasChanged())
 +					return true;
 +		}
 +		return false;
 +	}
 +}
 +
 +
 +/**
 + * 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 speicified 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');
 +	}
 +}
 +
  ?>
\ No newline at end of file diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index dcd3c749..d176b5f7 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -363,3 +363,6 @@ dbcommand_query_failed					= TDbCommand failed to execute the query SQL: {0}  dbcommand_column_empty					= TDbCommand returned an empty result and could not obtain the scalar.
  dbdatareader_rewind_invalid				= TDbDataReader is a forward-only stream. It can only be traversed once.
  dbtransaction_transaction_inactive		= TDbTransaction is inactive.
 +
 +directorycachedependency_directory_invalid = TDirectoryCacheDependency.Directory {0} does not refer to a valid directory.
 +cachedependencylist_cachedependency_required = Only objects implementing ICacheDependency can be added into TCacheDependencyList.
\ No newline at end of file diff --git a/framework/interfaces.php b/framework/interfaces.php index e5da7bcd..d0e5c6aa 100644 --- a/framework/interfaces.php +++ b/framework/interfaces.php @@ -229,6 +229,8 @@ interface ICache   * This interface must be implemented by classes meant to be used as
   * cache dependencies.
   *
 + * Classes implementing this interface must support serialization and unserialization.
 + *
   * @author Qiang Xue <qiang.xue@gmail.com>
   * @version $Id$
   * @package System
 @@ -282,9 +284,9 @@ interface IBindable  /**
   * IActiveControl interface.
 - * 
 + *
   * Active controls must implement IActiveControl interface.
 - * 
 + *
   * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
   * @version $Id$
   * @package System
 @@ -295,15 +297,15 @@ interface IActiveControl  	/**
  	 * @return TBaseActiveControl Active control properties.
  	 */
 -	public function getActiveControl(); 	
 +	public function getActiveControl();
  }
  /**
   * ICallbackEventHandler interface.
 - * 
 + *
   * If a control wants to respond to callback event, it must implement this
   * interface.
 - *  
 + *
   * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
   * @version $Id$
   * @package System
 | 
