summaryrefslogtreecommitdiff
path: root/framework/Caching/TDirectoryCacheDependency.php
blob: 66f7393d2e098512873fc463c889a056efbdd20a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
<?php
/**
 * TCache and cache dependency classes.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.pradosoft.com/
 * @copyright Copyright &copy; 2005-2014 PradoSoft
 * @license http://www.pradosoft.com/license/
 * @package System.Caching
 */

/**
 * 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>
 * @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;
	}
}