|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Cache |
|
17 * @subpackage Zend_Cache_Backend |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
20 * @version $Id: ZendPlatform.php 20096 2010-01-06 02:05:09Z bkarwin $ |
|
21 */ |
|
22 |
|
23 /** |
|
24 * @see Zend_Cache_Backend_Interface |
|
25 */ |
|
26 require_once 'Zend/Cache/Backend.php'; |
|
27 |
|
28 /** |
|
29 * @see Zend_Cache_Backend_Interface |
|
30 */ |
|
31 require_once 'Zend/Cache/Backend/Interface.php'; |
|
32 |
|
33 |
|
34 /** |
|
35 * Impementation of Zend Cache Backend using the Zend Platform (Output Content Caching) |
|
36 * |
|
37 * @package Zend_Cache |
|
38 * @subpackage Zend_Cache_Backend |
|
39 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
40 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
41 */ |
|
42 class Zend_Cache_Backend_ZendPlatform extends Zend_Cache_Backend implements Zend_Cache_Backend_Interface |
|
43 { |
|
44 /** |
|
45 * internal ZP prefix |
|
46 */ |
|
47 const TAGS_PREFIX = "internal_ZPtag:"; |
|
48 |
|
49 /** |
|
50 * Constructor |
|
51 * Validate that the Zend Platform is loaded and licensed |
|
52 * |
|
53 * @param array $options Associative array of options |
|
54 * @throws Zend_Cache_Exception |
|
55 * @return void |
|
56 */ |
|
57 public function __construct(array $options = array()) |
|
58 { |
|
59 if (!function_exists('accelerator_license_info')) { |
|
60 Zend_Cache::throwException('The Zend Platform extension must be loaded for using this backend !'); |
|
61 } |
|
62 if (!function_exists('accelerator_get_configuration')) { |
|
63 $licenseInfo = accelerator_license_info(); |
|
64 Zend_Cache::throwException('The Zend Platform extension is not loaded correctly: '.$licenseInfo['failure_reason']); |
|
65 } |
|
66 $accConf = accelerator_get_configuration(); |
|
67 if (@!$accConf['output_cache_licensed']) { |
|
68 Zend_Cache::throwException('The Zend Platform extension does not have the proper license to use content caching features'); |
|
69 } |
|
70 if (@!$accConf['output_cache_enabled']) { |
|
71 Zend_Cache::throwException('The Zend Platform content caching feature must be enabled for using this backend, set the \'zend_accelerator.output_cache_enabled\' directive to On !'); |
|
72 } |
|
73 if (!is_writable($accConf['output_cache_dir'])) { |
|
74 Zend_Cache::throwException('The cache copies directory \''. ini_get('zend_accelerator.output_cache_dir') .'\' must be writable !'); |
|
75 } |
|
76 parent:: __construct($options); |
|
77 } |
|
78 |
|
79 /** |
|
80 * Test if a cache is available for the given id and (if yes) return it (false else) |
|
81 * |
|
82 * @param string $id Cache id |
|
83 * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested |
|
84 * @return string Cached data (or false) |
|
85 */ |
|
86 public function load($id, $doNotTestCacheValidity = false) |
|
87 { |
|
88 // doNotTestCacheValidity implemented by giving zero lifetime to the cache |
|
89 if ($doNotTestCacheValidity) { |
|
90 $lifetime = 0; |
|
91 } else { |
|
92 $lifetime = $this->_directives['lifetime']; |
|
93 } |
|
94 $res = output_cache_get($id, $lifetime); |
|
95 if($res) { |
|
96 return $res[0]; |
|
97 } else { |
|
98 return false; |
|
99 } |
|
100 } |
|
101 |
|
102 |
|
103 /** |
|
104 * Test if a cache is available or not (for the given id) |
|
105 * |
|
106 * @param string $id Cache id |
|
107 * @return mixed|false false (a cache is not available) or "last modified" timestamp (int) of the available cache record |
|
108 */ |
|
109 public function test($id) |
|
110 { |
|
111 $result = output_cache_get($id, $this->_directives['lifetime']); |
|
112 if ($result) { |
|
113 return $result[1]; |
|
114 } |
|
115 return false; |
|
116 } |
|
117 |
|
118 /** |
|
119 * Save some string datas into a cache record |
|
120 * |
|
121 * Note : $data is always "string" (serialization is done by the |
|
122 * core not by the backend) |
|
123 * |
|
124 * @param string $data Data to cache |
|
125 * @param string $id Cache id |
|
126 * @param array $tags Array of strings, the cache record will be tagged by each string entry |
|
127 * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) |
|
128 * @return boolean true if no problem |
|
129 */ |
|
130 public function save($data, $id, $tags = array(), $specificLifetime = false) |
|
131 { |
|
132 if (!($specificLifetime === false)) { |
|
133 $this->_log("Zend_Cache_Backend_ZendPlatform::save() : non false specifc lifetime is unsuported for this backend"); |
|
134 } |
|
135 |
|
136 $lifetime = $this->_directives['lifetime']; |
|
137 $result1 = output_cache_put($id, array($data, time())); |
|
138 $result2 = (count($tags) == 0); |
|
139 |
|
140 foreach ($tags as $tag) { |
|
141 $tagid = self::TAGS_PREFIX.$tag; |
|
142 $old_tags = output_cache_get($tagid, $lifetime); |
|
143 if ($old_tags === false) { |
|
144 $old_tags = array(); |
|
145 } |
|
146 $old_tags[$id] = $id; |
|
147 output_cache_remove_key($tagid); |
|
148 $result2 = output_cache_put($tagid, $old_tags); |
|
149 } |
|
150 |
|
151 return $result1 && $result2; |
|
152 } |
|
153 |
|
154 |
|
155 /** |
|
156 * Remove a cache record |
|
157 * |
|
158 * @param string $id Cache id |
|
159 * @return boolean True if no problem |
|
160 */ |
|
161 public function remove($id) |
|
162 { |
|
163 return output_cache_remove_key($id); |
|
164 } |
|
165 |
|
166 |
|
167 /** |
|
168 * Clean some cache records |
|
169 * |
|
170 * Available modes are : |
|
171 * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) |
|
172 * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) |
|
173 * This mode is not supported in this backend |
|
174 * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags |
|
175 * ($tags can be an array of strings or a single string) |
|
176 * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => unsupported |
|
177 * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags |
|
178 * ($tags can be an array of strings or a single string) |
|
179 * |
|
180 * @param string $mode Clean mode |
|
181 * @param array $tags Array of tags |
|
182 * @throws Zend_Cache_Exception |
|
183 * @return boolean True if no problem |
|
184 */ |
|
185 public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) |
|
186 { |
|
187 switch ($mode) { |
|
188 case Zend_Cache::CLEANING_MODE_ALL: |
|
189 case Zend_Cache::CLEANING_MODE_OLD: |
|
190 $cache_dir = ini_get('zend_accelerator.output_cache_dir'); |
|
191 if (!$cache_dir) { |
|
192 return false; |
|
193 } |
|
194 $cache_dir .= '/.php_cache_api/'; |
|
195 return $this->_clean($cache_dir, $mode); |
|
196 break; |
|
197 case Zend_Cache::CLEANING_MODE_MATCHING_TAG: |
|
198 $idlist = null; |
|
199 foreach ($tags as $tag) { |
|
200 $next_idlist = output_cache_get(self::TAGS_PREFIX.$tag, $this->_directives['lifetime']); |
|
201 if ($idlist) { |
|
202 $idlist = array_intersect_assoc($idlist, $next_idlist); |
|
203 } else { |
|
204 $idlist = $next_idlist; |
|
205 } |
|
206 if (count($idlist) == 0) { |
|
207 // if ID list is already empty - we may skip checking other IDs |
|
208 $idlist = null; |
|
209 break; |
|
210 } |
|
211 } |
|
212 if ($idlist) { |
|
213 foreach ($idlist as $id) { |
|
214 output_cache_remove_key($id); |
|
215 } |
|
216 } |
|
217 return true; |
|
218 break; |
|
219 case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: |
|
220 $this->_log("Zend_Cache_Backend_ZendPlatform::clean() : CLEANING_MODE_NOT_MATCHING_TAG is not supported by the Zend Platform backend"); |
|
221 return false; |
|
222 break; |
|
223 case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: |
|
224 $idlist = null; |
|
225 foreach ($tags as $tag) { |
|
226 $next_idlist = output_cache_get(self::TAGS_PREFIX.$tag, $this->_directives['lifetime']); |
|
227 if ($idlist) { |
|
228 $idlist = array_merge_recursive($idlist, $next_idlist); |
|
229 } else { |
|
230 $idlist = $next_idlist; |
|
231 } |
|
232 if (count($idlist) == 0) { |
|
233 // if ID list is already empty - we may skip checking other IDs |
|
234 $idlist = null; |
|
235 break; |
|
236 } |
|
237 } |
|
238 if ($idlist) { |
|
239 foreach ($idlist as $id) { |
|
240 output_cache_remove_key($id); |
|
241 } |
|
242 } |
|
243 return true; |
|
244 break; |
|
245 default: |
|
246 Zend_Cache::throwException('Invalid mode for clean() method'); |
|
247 break; |
|
248 } |
|
249 } |
|
250 |
|
251 /** |
|
252 * Clean a directory and recursivly go over it's subdirectories |
|
253 * |
|
254 * Remove all the cached files that need to be cleaned (according to mode and files mtime) |
|
255 * |
|
256 * @param string $dir Path of directory ot clean |
|
257 * @param string $mode The same parameter as in Zend_Cache_Backend_ZendPlatform::clean() |
|
258 * @return boolean True if ok |
|
259 */ |
|
260 private function _clean($dir, $mode) |
|
261 { |
|
262 $d = @dir($dir); |
|
263 if (!$d) { |
|
264 return false; |
|
265 } |
|
266 $result = true; |
|
267 while (false !== ($file = $d->read())) { |
|
268 if ($file == '.' || $file == '..') { |
|
269 continue; |
|
270 } |
|
271 $file = $d->path . $file; |
|
272 if (is_dir($file)) { |
|
273 $result = ($this->_clean($file .'/', $mode)) && ($result); |
|
274 } else { |
|
275 if ($mode == Zend_Cache::CLEANING_MODE_ALL) { |
|
276 $result = ($this->_remove($file)) && ($result); |
|
277 } else if ($mode == Zend_Cache::CLEANING_MODE_OLD) { |
|
278 // Files older than lifetime get deleted from cache |
|
279 if ($this->_directives['lifetime'] !== null) { |
|
280 if ((time() - @filemtime($file)) > $this->_directives['lifetime']) { |
|
281 $result = ($this->_remove($file)) && ($result); |
|
282 } |
|
283 } |
|
284 } |
|
285 } |
|
286 } |
|
287 $d->close(); |
|
288 return $result; |
|
289 } |
|
290 |
|
291 /** |
|
292 * Remove a file |
|
293 * |
|
294 * If we can't remove the file (because of locks or any problem), we will touch |
|
295 * the file to invalidate it |
|
296 * |
|
297 * @param string $file Complete file path |
|
298 * @return boolean True if ok |
|
299 */ |
|
300 private function _remove($file) |
|
301 { |
|
302 if (!@unlink($file)) { |
|
303 # If we can't remove the file (because of locks or any problem), we will touch |
|
304 # the file to invalidate it |
|
305 $this->_log("Zend_Cache_Backend_ZendPlatform::_remove() : we can't remove $file => we are going to try to invalidate it"); |
|
306 if ($this->_directives['lifetime'] === null) { |
|
307 return false; |
|
308 } |
|
309 if (!file_exists($file)) { |
|
310 return false; |
|
311 } |
|
312 return @touch($file, time() - 2*abs($this->_directives['lifetime'])); |
|
313 } |
|
314 return true; |
|
315 } |
|
316 |
|
317 } |