From f76f11a2cb228b4161bd84d9856c148cb49f1bd7 Mon Sep 17 00:00:00 2001
From: xue <>
Date: Sun, 3 Dec 2006 22:57:44 +0000
Subject: added cache dependency classes.

---
 framework/Caching/TCache.php | 439 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 438 insertions(+), 1 deletion(-)

(limited to 'framework/Caching/TCache.php')

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
-- 
cgit v1.2.3