Core.php
13.4 KB
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Cache
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
/**
* @package Zend_Cache
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Cache_Core
{
// ------------------
// --- Properties ---
// ------------------
/**
* Backend Object
*
* @var object
*/
private $_backend = null;
/**
* Available options
*
* ====> (boolean) writeControl :
* - Enable / disable write control (the cache is read just after writing to detect corrupt entries)
* - Enable write control will lightly slow the cache writing but not the cache reading
* Write control can detect some corrupt cache files but maybe it's not a perfect control
*
* ====> (boolean) caching :
* - Enable / disable caching
* (can be very usefull for the debug of cached scripts)
*
* ====> (boolean) automaticSerialization :
* - Enable / disable automatic serialization
* - It can be used to save directly datas which aren't strings (but it's slower)
*
* ====> (int) automaticCleaningFactor :
* - Disable / Tune the automatic cleaning process
* - The automatic cleaning process destroy too old (for the given life time)
* cache files when a new cache file is written :
* 0 => no automatic cache cleaning
* 1 => systematic cache cleaning
* x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
*
* ====> (int) lifetime :
* - Cache lifetime (in seconds)
* - If null, the cache is valid forever.
*
* ====> (boolean) logging :
* - If set to true, logging is activated (but the system is slower)
*
* @var array available options
*/
protected $_options = array(
'writeControl' => true,
'caching' => true,
'automaticSerialization' => false,
'automaticCleaningFactor' => 10,
'lifetime' => 3600,
'logging' => false
);
/**
* Array of options which have to be transfered to backend
*/
protected static $_directivesList = array('lifetime', 'logging');
/**
* Not used for the core, just a sort a hint to get a common setOption() method (for the core and for frontends)
*/
protected $_specificOptions = array();
/**
* Last used cache id
*
* @var string $_lastId
*/
private $_lastId = null;
// ----------------------
// --- Public methods ---
// ----------------------
/**
* Constructor
*
* @param array $options associative array of options
*/
public function __construct($options = array())
{
if (!is_array($options)) {
Zend_Cache::throwException('Options parameter must be an array');
}
while (list($name, $value) = each($options)) {
$this->_setOption($name, $value);
}
}
/**
* Set the backend
*
* @param object $backendObject
*/
public function setBackend($backendObject)
{
if (!is_object($backendObject)) {
Zend_Cache::throwException('Incorrect backend object !');
}
$this->_backend= $backendObject;
// some options (listed in $_directivesList) have to be given
// to the backend too (even if they are not "backend specific")
$directives = array();
foreach (Zend_Cache_Core::$_directivesList as $directive) {
$directives[$directive] = $this->_options[$directive];
}
$this->_backend->setDirectives($directives);
}
/**
* Public frontend to set an option
*
* There is an additional validation (relatively to the protected _setOption method)
*
* @param string $name name of the option
* @param mixed $value value of the option
*/
public function setOption($name, $value)
{
if (is_string($name)) {
// backward compatibily becase of ZF-879 (it will be removed in ZF 1.1)
if ($name=='lifeTime') {
$name = 'lifetime';
}
if (array_key_exists($name, $this->_options)) {
// This is a Core option
$this->_setOption($name, $value);
return;
}
if (array_key_exists($name, $this->_specificOptions)) {
// This a specic option of this frontend
$this->_specificOptions[$name] = $value;
return;
}
}
Zend_Cache::throwException("Incorrect option name : $name");
}
/**
* Set an option
*
* @param string $name name of the option
* @param mixed $value value of the option
*/
private function _setOption($name, $value)
{
// backward compatibily becase of ZF-879 (it will be removed in ZF 1.1)
if ($name=='lifeTime') {
$name = 'lifetime';
}
if (!is_string($name) || !array_key_exists($name, $this->_options)) {
Zend_Cache::throwException("Incorrect option name : $name");
}
$this->_options[$name] = $value;
if ($name=='logging') {
if ((!class_exists('Zend_Log', false)) && ($value)) {
Zend_Cache::throwException('logging feature is on but Zend_Log is not "loaded"');
}
}
}
/**
* Force a new lifetime
*
* The new value is set for the core/frontend but for the backend too (directive)
*
* @param int $newLifetime new lifetime (in seconds)
*/
public function setLifetime($newLifetime)
{
$this->_options['lifetime'] = $newLifetime;
$this->_backend->setDirectives(array(
'lifetime' => $newLifetime
));
}
/**
* Test if a cache is available for the given id and (if yes) return it (false else)
*
* @param string $id cache id
* @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
* @param boolean $doNotUnserialize do not serialize (even if automaticSerialization is true) => for internal use
* @return mixed cached datas (or false)
*/
public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false)
{
if (!$this->_options['caching']) {
return false;
}
$this->_lastId = $id;
self::_validateIdOrTag($id);
$data = $this->_backend->load($id, $doNotTestCacheValidity);
if ($data===false) {
// no cache available
return false;
}
if ((!$doNotUnserialize) && $this->_options['automaticSerialization']) {
// we need to unserialize before sending the result
return unserialize($data);
}
return $data;
}
/**
* THIS METHOD IS DEPRECATED : USE LOAD() INSTEAD (same syntax) !
*
* it will be removed in ZF 1.1 !
*/
public function get($id, $doNotTestCacheValidity = false, $doNotUnserialize = false)
{
if ($this->_options['logging']) {
Zend_Log::log("get() method is deprecated => use load() method instead (same syntax) !", Zend_Log::LEVEL_WARNING);
}
return $this->load($id, $doNotTestCacheValidity, $doNotUnserialize);
}
/**
* Test if a cache is available for the given id
*
* @param string $id cache id
* @return boolean true is a cache is available, false else
*/
public function test($id)
{
if (!$this->_options['caching']) {
return false;
}
self::_validateIdOrTag($id);
$this->_lastId = $id;
return $this->_backend->test($id);
}
/**
* Save some data in a cache
*
* @param mixed $data data to put in cache (can be another type than string if automaticSerialization is on)
* @param cache $id cache id (if not set, the last cache id will be used)
* @param array $tags cache tags
* @param int $specificLifetime if != false, set a specific lifetime for this cache record (null => infinite lifetime)
* @return boolean true if no problem
*/
public function save($data, $id = null, $tags = array(), $specificLifetime = false)
{
if (!$this->_options['caching']) {
return true;
}
if (is_null($id)) {
$id = $this->_lastId;
}
self::_validateIdOrTag($id);
self::_validateTagsArray($tags);
if ($this->_options['automaticSerialization']) {
// we need to serialize datas before storing them
$data = serialize($data);
} else {
if (!is_string($data)) {
Zend_Cache::throwException("Datas must be string or set automaticSerialization = true");
}
}
// automatic cleaning
if ($this->_options['automaticCleaningFactor'] > 0) {
$rand = rand(1, $this->_options['automaticCleaningFactor']);
if ($rand==1) {
$this->clean('old');
}
}
$result = $this->_backend->save($data, $id, $tags, $specificLifetime);
if (!$result) {
// maybe the cache is corrupted, so we remove it !
if ($this->_options['logging']) {
Zend_Log::log("Zend_Cache_Core::save() : impossible to save cache (id=$id)", Zend_Log::LEVEL_WARNING);
}
$this->remove($id);
return false;
}
if ($this->_options['writeControl']) {
$data2 = $this->_backend->load($id, true);
if ($data!=$data2) {
if ($this->_options['logging']) {
Zend_Log::log('Zend_Cache_Core::save() / writeControl : written and read data do not match', Zend_Log::LEVEL_WARNING);
}
$this->remove($id);
return false;
}
}
return true;
}
/**
* Remove a cache
*
* @param string $id cache id to remove
* @return boolean true if ok
*/
public function remove($id)
{
if (!$this->_options['caching']) {
return true;
}
self::_validateIdOrTag($id);
return $this->_backend->remove($id);
}
/**
* Clean cache entries
*
* Available modes are :
* 'all' (default) => remove all cache entries ($tags is not used)
* 'old' => remove too old cache entries ($tags is not used)
* 'matchingTag' => remove cache entries matching all given tags
* ($tags can be an array of strings or a single string)
* 'notMatchingTag' => remove cache entries not matching one of the given tags
* ($tags can be an array of strings or a single string)
*
* @param string $mode
* @param mixed $parameters
* @return boolean true if ok
*/
public function clean($mode = 'all', $tags = array())
{
if (!$this->_options['caching']) {
return true;
}
if (!in_array($mode, array(Zend_Cache::CLEANING_MODE_ALL, Zend_Cache::CLEANING_MODE_OLD, Zend_Cache::CLEANING_MODE_MATCHING_TAG, Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG))) {
Zend_Cache::throwException('Invalid cleaning mode');
}
self::_validateTagsArray($tags);
return $this->_backend->clean($mode, $tags);
}
// ------------------------------------
// --- Private or protected methods ---
// ------------------------------------
/**
* Validate a cache id or a tag (security, reliable filenames, reserved prefixes...)
*
* Throw an exception if a problem is found
*
* @param string $string cache id or tag
*/
private static function _validateIdOrTag($string)
{
if (!is_string($string)) {
Zend_Cache::throwException('Invalid id or tag : must be a string');
}
if (substr($string, 0, 9) == 'internal-') {
Zend_Cache::throwException('"internal-*" ids or tags are reserved');
}
if (!preg_match('~^[\w]+$~', $string)) {
Zend_Cache::throwException('Invalid id or tag : must use only [a-zA-Z0-9_]');
}
}
/**
* Validate a tags array (security, reliable filenames, reserved prefixes...)
*
* Throw an exception if a problem is found
*
* @param array $tags array of tags
*/
private static function _validateTagsArray($tags)
{
if (!is_array($tags)) {
Zend_Cache::throwException('Invalid tags array : must be an array');
}
foreach($tags as $tag) {
self::_validateIdOrTag($tag);
}
reset($tags);
}
}