1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* To change this license header, choose License Headers in Project Properties. |
4
|
|
|
* To change this template file, choose Tools | Templates |
5
|
|
|
* and open the template in the editor. |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace Columnis\Model; |
9
|
|
|
|
10
|
|
|
use Exception as BaseException; |
11
|
|
|
use GlobIterator; |
12
|
|
|
use Zend\Cache\Exception; |
13
|
|
|
use Zend\Cache\Storage\Adapter\Filesystem; |
14
|
|
|
use Zend\Cache\Storage\Adapter\FilesystemOptions; |
15
|
|
|
use Zend\Cache\Storage\Adapter\FilesystemIterator; |
16
|
|
|
use Zend\Stdlib\ErrorHandler; |
17
|
|
|
use ArrayObject; |
18
|
|
|
|
19
|
|
|
class HtmlCache extends Filesystem |
20
|
|
|
{ |
21
|
|
|
protected $extension = '.html'; |
22
|
|
|
|
23
|
|
|
public function getExtension() |
24
|
|
|
{ |
25
|
|
|
return $this->extension; |
26
|
|
|
} |
27
|
|
|
|
28
|
|
|
public function setExtension($extension) |
29
|
|
|
{ |
30
|
|
|
$this->extension = $extension; |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Set options. |
35
|
|
|
* |
36
|
|
|
* @param FilesystemOptions $options |
37
|
|
|
* @return Filesystem |
38
|
|
|
* @see getOptions() |
39
|
|
|
*/ |
40
|
1 |
|
public function setOptions($options) |
41
|
|
|
{ |
42
|
1 |
|
if (!$options instanceof FilesystemOptions) { |
43
|
1 |
|
$options = new FilesystemOptions($options); |
44
|
1 |
|
} |
45
|
|
|
|
46
|
1 |
|
return parent::setOptions($options); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Get options. |
51
|
|
|
* |
52
|
|
|
* @return FilesystemOptions |
53
|
|
|
* @see setOptions() |
54
|
|
|
*/ |
55
|
1 |
|
public function getOptions() |
56
|
|
|
{ |
57
|
1 |
|
if (!$this->options) { |
58
|
|
|
$this->setOptions(new FilesystemOptions()); |
59
|
|
|
} |
60
|
1 |
|
return $this->options; |
61
|
|
|
} |
62
|
|
|
/* FlushableInterface */ |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Flush the whole storage |
66
|
|
|
* |
67
|
|
|
* @throws Exception\RuntimeException |
68
|
|
|
* @return bool |
69
|
|
|
*/ |
70
|
|
|
public function flush() |
71
|
|
|
{ |
72
|
|
|
$flags = GlobIterator::SKIP_DOTS | GlobIterator::CURRENT_AS_PATHNAME; |
73
|
|
|
$dir = $this->getOptions()->getCacheDir(); |
74
|
|
|
$clearFolder = null; |
75
|
|
|
$clearFolder = function ($dir) use (& $clearFolder, $flags) { |
76
|
|
|
$it = new GlobIterator($dir.DIRECTORY_SEPARATOR.'*', $flags); |
77
|
|
|
foreach ($it as $pathname) { |
78
|
|
|
if ($it->isDir()) { |
79
|
|
|
$clearFolder($pathname); |
80
|
|
|
rmdir($pathname); |
81
|
|
|
} else { |
82
|
|
|
unlink($pathname); |
83
|
|
|
} |
84
|
|
|
} |
85
|
|
|
}; |
86
|
|
|
|
87
|
|
|
ErrorHandler::start(); |
88
|
|
|
$clearFolder($dir); |
89
|
|
|
$error = ErrorHandler::stop(); |
90
|
|
|
if ($error) { |
91
|
|
|
throw new Exception\RuntimeException( |
92
|
|
|
"Flushing directory '{$dir}' failed", |
93
|
|
|
0, |
94
|
|
|
$error |
95
|
|
|
); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
return true; |
99
|
|
|
} |
100
|
|
|
/* ClearExpiredInterface */ |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Remove expired items |
104
|
|
|
* |
105
|
|
|
* @return bool |
106
|
|
|
* |
107
|
|
|
* @triggers clearExpired.exception(ExceptionEvent) |
108
|
|
|
*/ |
109
|
|
|
public function clearExpired() |
110
|
|
|
{ |
111
|
|
|
$options = $this->getOptions(); |
112
|
|
|
$namespace = $options->getNamespace(); |
113
|
|
|
$prefix = ($namespace === '') ? '' : $namespace.$options->getNamespaceSeparator(); |
114
|
|
|
|
115
|
|
|
$flags = GlobIterator::SKIP_DOTS | GlobIterator::CURRENT_AS_FILEINFO; |
116
|
|
|
$path = $options->getCacheDir() |
117
|
|
|
.str_repeat(DIRECTORY_SEPARATOR.$prefix.'*', $options->getDirLevel()) |
118
|
|
|
.DIRECTORY_SEPARATOR.$prefix.'*'.$this->getExtension(); |
119
|
|
|
$glob = new GlobIterator($path, $flags); |
120
|
|
|
$time = time(); |
121
|
|
|
$ttl = $options->getTtl(); |
122
|
|
|
|
123
|
|
|
ErrorHandler::start(); |
124
|
|
|
foreach ($glob as $entry) { |
125
|
|
|
$mtime = $entry->getMTime(); |
126
|
|
|
if ($time >= $mtime + $ttl) { |
127
|
|
|
$pathname = $entry->getPathname(); |
128
|
|
|
unlink($pathname); |
129
|
|
|
|
130
|
|
|
$tagPathname = substr($pathname, 0, -4).'.tag'; |
131
|
|
|
if (file_exists($tagPathname)) { |
132
|
|
|
unlink($tagPathname); |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
$error = ErrorHandler::stop(); |
137
|
|
|
if ($error) { |
138
|
|
|
$result = false; |
139
|
|
|
return $this->triggerException( |
140
|
|
|
__FUNCTION__, |
141
|
|
|
new ArrayObject(), |
142
|
|
|
$result, |
143
|
|
|
new Exception\RuntimeException( |
144
|
|
|
'Failed to clear expired items', |
145
|
|
|
0, |
146
|
|
|
$error |
147
|
|
|
) |
148
|
|
|
); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
return true; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Remove items matching given tags. |
156
|
|
|
* |
157
|
|
|
* If $disjunction only one of the given tags must match |
158
|
|
|
* else all given tags must match. |
159
|
|
|
* |
160
|
|
|
* @param string[] $tags |
161
|
|
|
* @param bool $disjunction |
162
|
|
|
* @return bool |
163
|
|
|
*/ |
164
|
|
|
public function clearByTags(array $tags, $disjunction = false) |
165
|
|
|
{ |
166
|
|
|
if (!$tags) { |
|
|
|
|
167
|
|
|
return true; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
$tagCount = count($tags); |
171
|
|
|
$options = $this->getOptions(); |
172
|
|
|
$namespace = $options->getNamespace(); |
173
|
|
|
$prefix = ($namespace === '') ? '' : $namespace.$options->getNamespaceSeparator(); |
174
|
|
|
|
175
|
|
|
$flags = GlobIterator::SKIP_DOTS | GlobIterator::CURRENT_AS_PATHNAME; |
176
|
|
|
$path = $options->getCacheDir() |
177
|
|
|
.str_repeat(DIRECTORY_SEPARATOR.$prefix.'*', $options->getDirLevel()) |
178
|
|
|
.DIRECTORY_SEPARATOR.$prefix.'*.tag'; |
179
|
|
|
$glob = new GlobIterator($path, $flags); |
180
|
|
|
|
181
|
|
|
foreach ($glob as $pathname) { |
182
|
|
|
$diff = array_diff( |
183
|
|
|
$tags, |
184
|
|
|
explode("\n", $this->getFileContent($pathname)) |
185
|
|
|
); |
186
|
|
|
|
187
|
|
|
$rem = false; |
188
|
|
|
if ($disjunction && count($diff) < $tagCount) { |
189
|
|
|
$rem = true; |
190
|
|
|
} elseif (!$disjunction && !$diff) { |
|
|
|
|
191
|
|
|
$rem = true; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
if ($rem) { |
195
|
|
|
unlink($pathname); |
196
|
|
|
|
197
|
|
|
$datPathname = substr($pathname, 0, -4).$this->getExtension(); |
198
|
|
|
if (file_exists($datPathname)) { |
199
|
|
|
unlink($datPathname); |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
return true; |
205
|
|
|
} |
206
|
|
|
/* IterableInterface */ |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Get the storage iterator |
210
|
|
|
* |
211
|
|
|
* @return FilesystemIterator |
212
|
|
|
*/ |
213
|
|
|
public function getIterator() |
214
|
|
|
{ |
215
|
|
|
$options = $this->getOptions(); |
216
|
|
|
$namespace = $options->getNamespace(); |
217
|
|
|
$prefix = ($namespace === '') ? '' : $namespace.$options->getNamespaceSeparator(); |
218
|
|
|
$path = $options->getCacheDir() |
219
|
|
|
.str_repeat(DIRECTORY_SEPARATOR.$prefix.'*', $options->getDirLevel()) |
220
|
|
|
.DIRECTORY_SEPARATOR.$prefix.'*'.$this->getExtension(); |
221
|
|
|
return new FilesystemIterator($this, $path, $prefix); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Internal method to get an item. |
226
|
|
|
* |
227
|
|
|
* @param string $normalizedKey |
228
|
|
|
* @param bool $success |
229
|
|
|
* @param mixed $casToken |
230
|
|
|
* @return null|string Data on success, null on failure |
231
|
|
|
* @throws Exception\ExceptionInterface |
232
|
|
|
*/ |
233
|
|
|
protected function internalGetItem( |
234
|
|
|
& $normalizedKey, |
235
|
|
|
& $success = null, |
236
|
|
|
& $casToken = null |
237
|
|
|
) { |
238
|
|
|
if (!$this->internalHasItem($normalizedKey)) { |
239
|
|
|
$success = false; |
240
|
|
|
return null; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
try { |
244
|
|
|
$filespec = $this->getFileSpec($normalizedKey); |
245
|
|
|
$data = $this->getFileContent($filespec.$this->getExtension()); |
246
|
|
|
|
247
|
|
|
// use filemtime + filesize as CAS token |
248
|
|
|
if (func_num_args() > 2) { |
249
|
|
|
$casToken = filemtime($filespec.$this->getExtension()).filesize($filespec.$this->getExtension()); |
250
|
|
|
} |
251
|
|
|
$success = true; |
252
|
|
|
return $data; |
253
|
|
|
} catch (BaseException $e) { |
254
|
|
|
$success = false; |
255
|
|
|
throw $e; |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* Internal method to get multiple items. |
261
|
|
|
* |
262
|
|
|
* @param array $normalizedKeys |
263
|
|
|
* @return array Associative array of keys and values |
264
|
|
|
* @throws Exception\ExceptionInterface |
265
|
|
|
*/ |
266
|
|
|
protected function internalGetItems(array & $normalizedKeys) |
267
|
|
|
{ |
268
|
|
|
$keys = $normalizedKeys; // Don't change argument passed by reference |
269
|
|
|
$result = array(); |
270
|
|
|
while ($keys) { |
|
|
|
|
271
|
|
|
// LOCK_NB if more than one items have to read |
272
|
|
|
$nonBlocking = count($keys) > 1; |
273
|
|
|
$wouldblock = null; |
274
|
|
|
|
275
|
|
|
// read items |
276
|
|
|
foreach ($keys as $i => $key) { |
277
|
|
|
if (!$this->internalHasItem($key)) { |
278
|
|
|
unset($keys[$i]); |
279
|
|
|
continue; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
$filespec = $this->getFileSpec($key); |
283
|
|
|
$data = $this->getFileContent( |
284
|
|
|
$filespec.$this->getExtension(), |
285
|
|
|
$nonBlocking, |
286
|
|
|
$wouldblock |
287
|
|
|
); |
288
|
|
|
if ($nonBlocking && $wouldblock) { |
289
|
|
|
continue; |
290
|
|
|
} else { |
291
|
|
|
unset($keys[$i]); |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
$result[$key] = $data; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
// TODO: Don't check ttl after first iteration |
298
|
|
|
// $options['ttl'] = 0; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
return $result; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
protected function internalHasItem(& $normalizedKey) |
305
|
|
|
{ |
306
|
|
|
$file = $this->getFileSpec($normalizedKey).$this->getExtension(); |
307
|
|
|
if (!file_exists($file)) { |
308
|
|
|
return false; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
$ttl = $this->getOptions()->getTtl(); |
312
|
|
|
if ($ttl) { |
313
|
|
|
ErrorHandler::start(); |
314
|
|
|
$mtime = filemtime($file); |
315
|
|
|
$error = ErrorHandler::stop(); |
316
|
|
|
if (!$mtime) { |
317
|
|
|
throw new Exception\RuntimeException( |
318
|
|
|
"Error getting mtime of file '{$file}'", |
319
|
|
|
0, |
320
|
|
|
$error |
321
|
|
|
); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
if (time() >= ($mtime + $ttl)) { |
325
|
|
|
return false; |
326
|
|
|
} |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
return true; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* Get metadata |
334
|
|
|
* |
335
|
|
|
* @param string $key |
336
|
|
|
* @return array|bool Metadata on success, false on failure |
337
|
|
|
*/ |
338
|
|
|
public function getMetadata($key) |
339
|
|
|
{ |
340
|
|
|
$options = $this->getOptions(); |
341
|
|
|
if ($options->getReadable() && $options->getClearStatCache()) { |
342
|
|
|
clearstatcache(); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
return parent::getMetadata($key); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* Get metadatas |
350
|
|
|
* |
351
|
|
|
* @param array $keys |
352
|
|
|
* @param array $options |
353
|
|
|
* @return array Associative array of keys and metadata |
354
|
|
|
*/ |
355
|
|
|
public function getMetadatas(array $keys, array $options = array()) |
356
|
|
|
{ |
357
|
|
|
$options = $this->getOptions(); |
358
|
|
|
if ($options->getReadable() && $options->getClearStatCache()) { |
359
|
|
|
clearstatcache(); |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
return parent::getMetadatas($keys); |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* Get info by key |
367
|
|
|
* |
368
|
|
|
* @param string $normalizedKey |
369
|
|
|
* @return array|bool Metadata on success, false on failure |
370
|
|
|
*/ |
371
|
|
|
protected function internalGetMetadata(& $normalizedKey) |
372
|
|
|
{ |
373
|
|
|
if (!$this->internalHasItem($normalizedKey)) { |
374
|
|
|
return false; |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
$options = $this->getOptions(); |
378
|
|
|
$filespec = $this->getFileSpec($normalizedKey); |
379
|
|
|
$file = $filespec.$this->getExtension(); |
380
|
|
|
|
381
|
|
|
$metadata = array( |
382
|
|
|
'filespec' => $filespec, |
383
|
|
|
'mtime' => filemtime($file) |
384
|
|
|
); |
385
|
|
|
|
386
|
|
|
if (!$options->getNoCtime()) { |
387
|
|
|
$metadata['ctime'] = filectime($file); |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
if (!$options->getNoAtime()) { |
391
|
|
|
$metadata['atime'] = fileatime($file); |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
return $metadata; |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* Internal method to get multiple metadata |
399
|
|
|
* |
400
|
|
|
* @param array $normalizedKeys |
401
|
|
|
* @return array Associative array of keys and metadata |
402
|
|
|
* @throws Exception\ExceptionInterface |
403
|
|
|
*/ |
404
|
|
|
protected function internalGetMetadatas(array & $normalizedKeys) |
405
|
|
|
{ |
406
|
|
|
$options = $this->getOptions(); |
407
|
|
|
$result = array(); |
408
|
|
|
|
409
|
|
|
foreach ($normalizedKeys as $normalizedKey) { |
410
|
|
|
$filespec = $this->getFileSpec($normalizedKey); |
411
|
|
|
$file = $filespec.$this->getExtension(); |
412
|
|
|
|
413
|
|
|
$metadata = array( |
414
|
|
|
'filespec' => $filespec, |
415
|
|
|
'mtime' => filemtime($file), |
416
|
|
|
); |
417
|
|
|
|
418
|
|
|
if (!$options->getNoCtime()) { |
419
|
|
|
$metadata['ctime'] = filectime($file); |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
if (!$options->getNoAtime()) { |
423
|
|
|
$metadata['atime'] = fileatime($file); |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
$result[$normalizedKey] = $metadata; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
return $result; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
/** |
433
|
|
|
* Internal method to store an item. |
434
|
|
|
* |
435
|
|
|
* @param string $normalizedKey |
436
|
|
|
* @param mixed $value |
437
|
|
|
* @return bool |
438
|
|
|
* @throws Exception\ExceptionInterface |
439
|
|
|
*/ |
440
|
|
|
protected function internalSetItem(& $normalizedKey, & $value) |
441
|
|
|
{ |
442
|
|
|
$filespec = $this->getFileSpec($normalizedKey); |
443
|
|
|
$this->prepareDirectoryStructure($filespec); |
444
|
|
|
|
445
|
|
|
// write data in non-blocking mode |
446
|
|
|
$wouldblock = null; |
447
|
|
|
$this->putFileContent( |
448
|
|
|
$filespec.$this->getExtension(), |
449
|
|
|
$value, |
450
|
|
|
true, |
451
|
|
|
$wouldblock |
452
|
|
|
); |
453
|
|
|
|
454
|
|
|
// delete related tag file (if present) |
455
|
|
|
$this->unlink($filespec.'.tag'); |
456
|
|
|
|
457
|
|
|
// Retry writing data in blocking mode if it was blocked before |
458
|
|
|
if ($wouldblock) { |
459
|
|
|
$this->putFileContent($filespec.$this->getExtension(), $value); |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
return true; |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
/** |
466
|
|
|
* Internal method to store multiple items. |
467
|
|
|
* |
468
|
|
|
* @param array $normalizedKeyValuePairs |
469
|
|
|
* @return array Array of not stored keys |
470
|
|
|
* @throws Exception\ExceptionInterface |
471
|
|
|
*/ |
472
|
|
|
protected function internalSetItems(array & $normalizedKeyValuePairs) |
473
|
|
|
{ |
474
|
|
|
|
475
|
|
|
// create an associated array of files and contents to write |
476
|
|
|
$contents = array(); |
477
|
|
|
foreach ($normalizedKeyValuePairs as $key => & $value) { |
478
|
|
|
$filespec = $this->getFileSpec($key); |
479
|
|
|
$this->prepareDirectoryStructure($filespec); |
480
|
|
|
|
481
|
|
|
// *.ext file |
482
|
|
|
$contents[$filespec.$this->getExtension()] = & $value; |
483
|
|
|
|
484
|
|
|
// *.tag file |
485
|
|
|
$this->unlink($filespec.'.tag'); |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
// write to disk |
489
|
|
|
while ($contents) { |
|
|
|
|
490
|
|
|
$nonBlocking = count($contents) > 1; |
491
|
|
|
$wouldblock = null; |
492
|
|
|
|
493
|
|
|
foreach ($contents as $file => & $content) { |
494
|
|
|
$this->putFileContent($file, $content, $nonBlocking, $wouldblock); |
495
|
|
|
if (!$nonBlocking || !$wouldblock) { |
|
|
|
|
496
|
|
|
unset($contents[$file]); |
497
|
|
|
} |
498
|
|
|
} |
499
|
|
|
} |
500
|
|
|
|
501
|
|
|
// return OK |
502
|
|
|
return array(); |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* Set an item only if token matches |
507
|
|
|
* |
508
|
|
|
* It uses the token received from getItem() to check if the item has |
509
|
|
|
* changed before overwriting it. |
510
|
|
|
* |
511
|
|
|
* @param mixed $token |
512
|
|
|
* @param string $key |
513
|
|
|
* @param mixed $value |
514
|
|
|
* @return bool |
515
|
|
|
* @throws Exception\ExceptionInterface |
516
|
|
|
* @see getItem() |
517
|
|
|
* @see setItem() |
518
|
|
|
*/ |
519
|
|
|
public function checkAndSetItem($token, $key, $value) |
520
|
|
|
{ |
521
|
|
|
$options = $this->getOptions(); |
522
|
|
|
if ($options->getWritable() && $options->getClearStatCache()) { |
523
|
|
|
clearstatcache(); |
524
|
|
|
} |
525
|
|
|
|
526
|
|
|
return parent::checkAndSetItem($token, $key, $value); |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
/** |
530
|
|
|
* Internal method to set an item only if token matches |
531
|
|
|
* |
532
|
|
|
* @param mixed $token |
533
|
|
|
* @param string $normalizedKey |
534
|
|
|
* @param mixed $value |
535
|
|
|
* @return bool |
536
|
|
|
* @throws Exception\ExceptionInterface |
537
|
|
|
* @see getItem() |
538
|
|
|
* @see setItem() |
539
|
|
|
*/ |
540
|
|
|
protected function internalCheckAndSetItem( |
541
|
|
|
& $token, |
542
|
|
|
& $normalizedKey, |
543
|
|
|
& $value |
544
|
|
|
) { |
545
|
|
|
if (!$this->internalHasItem($normalizedKey)) { |
546
|
|
|
return false; |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
// use filemtime + filesize as CAS token |
550
|
|
|
$file = $this->getFileSpec($normalizedKey).$this->getExtension(); |
551
|
|
|
$check = filemtime($file).filesize($file); |
552
|
|
|
if ($token !== $check) { |
553
|
|
|
return false; |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
return $this->internalSetItem($normalizedKey, $value); |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
/** |
560
|
|
|
* Reset lifetime of an item |
561
|
|
|
* |
562
|
|
|
* @param string $key |
563
|
|
|
* @return bool |
564
|
|
|
* @throws Exception\ExceptionInterface |
565
|
|
|
* |
566
|
|
|
* @triggers touchItem.pre(PreEvent) |
567
|
|
|
* @triggers touchItem.post(PostEvent) |
568
|
|
|
* @triggers touchItem.exception(ExceptionEvent) |
569
|
|
|
*/ |
570
|
|
|
public function touchItem($key) |
571
|
|
|
{ |
572
|
|
|
$options = $this->getOptions(); |
573
|
|
|
if ($options->getWritable() && $options->getClearStatCache()) { |
574
|
|
|
clearstatcache(); |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
return parent::touchItem($key); |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Reset lifetime of multiple items. |
582
|
|
|
* |
583
|
|
|
* @param array $keys |
584
|
|
|
* @return array Array of not updated keys |
585
|
|
|
* @throws Exception\ExceptionInterface |
586
|
|
|
* |
587
|
|
|
* @triggers touchItems.pre(PreEvent) |
588
|
|
|
* @triggers touchItems.post(PostEvent) |
589
|
|
|
* @triggers touchItems.exception(ExceptionEvent) |
590
|
|
|
*/ |
591
|
|
|
public function touchItems(array $keys) |
592
|
|
|
{ |
593
|
|
|
$options = $this->getOptions(); |
594
|
|
|
if ($options->getWritable() && $options->getClearStatCache()) { |
595
|
|
|
clearstatcache(); |
596
|
|
|
} |
597
|
|
|
|
598
|
|
|
return parent::touchItems($keys); |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
/** |
602
|
|
|
* Internal method to reset lifetime of an item |
603
|
|
|
* |
604
|
|
|
* @param string $normalizedKey |
605
|
|
|
* @return bool |
606
|
|
|
* @throws Exception\ExceptionInterface |
607
|
|
|
*/ |
608
|
|
|
protected function internalTouchItem(& $normalizedKey) |
609
|
|
|
{ |
610
|
|
|
if (!$this->internalHasItem($normalizedKey)) { |
611
|
|
|
return false; |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
$filespec = $this->getFileSpec($normalizedKey); |
615
|
|
|
|
616
|
|
|
ErrorHandler::start(); |
617
|
|
|
$touch = touch($filespec.$this->getExtension()); |
618
|
|
|
$error = ErrorHandler::stop(); |
619
|
|
|
if (!$touch) { |
620
|
|
|
throw new Exception\RuntimeException( |
621
|
|
|
"Error touching file '{$filespec}.'".$this->getExtension(), |
622
|
|
|
0, |
623
|
|
|
$error |
624
|
|
|
); |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
return true; |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
/** |
631
|
|
|
* Remove an item. |
632
|
|
|
* |
633
|
|
|
* @param string $key |
634
|
|
|
* @return bool |
635
|
|
|
* @throws Exception\ExceptionInterface |
636
|
|
|
* |
637
|
|
|
* @triggers removeItem.pre(PreEvent) |
638
|
|
|
* @triggers removeItem.post(PostEvent) |
639
|
|
|
* @triggers removeItem.exception(ExceptionEvent) |
640
|
|
|
*/ |
641
|
|
|
public function removeItem($key) |
642
|
|
|
{ |
643
|
|
|
$options = $this->getOptions(); |
644
|
|
|
if ($options->getWritable() && $options->getClearStatCache()) { |
645
|
|
|
clearstatcache(); |
646
|
|
|
} |
647
|
|
|
|
648
|
|
|
return parent::removeItem($key); |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
/** |
652
|
|
|
* Remove multiple items. |
653
|
|
|
* |
654
|
|
|
* @param array $keys |
655
|
|
|
* @return array Array of not removed keys |
656
|
|
|
* @throws Exception\ExceptionInterface |
657
|
|
|
* |
658
|
|
|
* @triggers removeItems.pre(PreEvent) |
659
|
|
|
* @triggers removeItems.post(PostEvent) |
660
|
|
|
* @triggers removeItems.exception(ExceptionEvent) |
661
|
|
|
*/ |
662
|
|
|
public function removeItems(array $keys) |
663
|
|
|
{ |
664
|
|
|
$options = $this->getOptions(); |
665
|
|
|
if ($options->getWritable() && $options->getClearStatCache()) { |
666
|
|
|
clearstatcache(); |
667
|
|
|
} |
668
|
|
|
|
669
|
|
|
return parent::removeItems($keys); |
670
|
|
|
} |
671
|
|
|
|
672
|
|
|
/** |
673
|
|
|
* Internal method to remove an item. |
674
|
|
|
* |
675
|
|
|
* @param string $normalizedKey |
676
|
|
|
* @return bool |
677
|
|
|
* @throws Exception\ExceptionInterface |
678
|
|
|
*/ |
679
|
|
|
protected function internalRemoveItem(& $normalizedKey) |
680
|
|
|
{ |
681
|
|
|
$filespec = $this->getFileSpec($normalizedKey); |
682
|
|
|
if (!file_exists($filespec.$this->getExtension())) { |
683
|
|
|
return false; |
684
|
|
|
} else { |
685
|
|
|
$this->unlink($filespec.$this->getExtension()); |
686
|
|
|
$this->unlink($filespec.'.tag'); |
687
|
|
|
} |
688
|
|
|
return true; |
689
|
|
|
} |
690
|
|
|
|
691
|
|
|
protected function getFileSpec($normalizedKey) |
692
|
|
|
{ |
693
|
|
|
$options = $this->getOptions(); |
694
|
|
|
$namespace = $options->getNamespace(); |
695
|
|
|
$prefix = ($namespace === '') ? '' : $namespace; |
696
|
|
|
$path = $options->getCacheDir().DIRECTORY_SEPARATOR; |
697
|
|
|
return $path.$prefix.DIRECTORY_SEPARATOR.$normalizedKey; |
698
|
|
|
} |
699
|
|
|
} |
700
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.