1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* |
4
|
|
|
* This file is part of phpFastCache. |
5
|
|
|
* |
6
|
|
|
* @license MIT License (MIT) |
7
|
|
|
* |
8
|
|
|
* For full copyright and license information, please see the docs/CREDITS.txt file. |
9
|
|
|
* |
10
|
|
|
* @author Khoa Bui (khoaofgod) <[email protected]> http://www.phpfastcache.com |
11
|
|
|
* @author Georges.L (Geolim4) <[email protected]> |
12
|
|
|
* |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
namespace phpFastCache\Core; |
16
|
|
|
|
17
|
|
|
use InvalidArgumentException; |
18
|
|
|
use phpFastCache\Cache\ExtendedCacheItemInterface; |
19
|
|
|
use phpFastCache\Cache\ExtendedCacheItemPoolInterface; |
20
|
|
|
use phpFastCache\Exceptions\phpFastCacheDriverException; |
21
|
|
|
use phpFastCache\CacheManager; |
22
|
|
|
use Psr\Cache\CacheItemInterface; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Class DriverAbstract |
26
|
|
|
* @package phpFastCache\Core |
27
|
|
|
*/ |
28
|
|
|
abstract class DriverAbstract implements ExtendedCacheItemPoolInterface |
29
|
|
|
{ |
30
|
|
|
const DRIVER_CHECK_FAILURE = '%s is not installed or misconfigured, cannot continue.'; |
31
|
|
|
const DRIVER_TAGS_KEY_PREFIX = '_TAG_'; |
32
|
|
|
const DRIVER_DATA_WRAPPER_INDEX = 'd'; |
33
|
|
|
const DRIVER_TIME_WRAPPER_INDEX = 't'; |
34
|
|
|
const DRIVER_TAGS_WRAPPER_INDEX = 'g'; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var array |
38
|
|
|
*/ |
39
|
|
|
public $extension_dir = '_extensions'; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var array |
43
|
|
|
*/ |
44
|
|
|
public $tmp = []; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var array default options, this will be merge to Driver's Options |
48
|
|
|
*/ |
49
|
|
|
public $config = []; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var bool |
53
|
|
|
*/ |
54
|
|
|
public $fallback = false; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var mixed Instance of driver service |
58
|
|
|
*/ |
59
|
|
|
public $instance; |
60
|
|
|
|
61
|
|
|
|
62
|
|
|
public function __destruct() |
63
|
|
|
{ |
64
|
|
|
// clean up the memory and don't want for PHP clean for caching method "phpfastcache" |
65
|
|
|
if (isset($this->config[ 'instance' ]) && (int) $this->config[ 'cache_method' ] === 3) { |
66
|
|
|
CacheManager::cleanCachingMethod($this->config[ 'instance' ]); |
67
|
|
|
} |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @param $keyword |
72
|
|
|
* @return string |
73
|
|
|
*/ |
74
|
|
|
protected function encodeFilename($keyword) |
75
|
|
|
{ |
76
|
|
|
// return trim(trim(preg_replace('/[^a-zA-Z0-9]+/', '_', $keyword), '_')); |
|
|
|
|
77
|
|
|
// return rtrim(base64_encode($keyword), '='); |
|
|
|
|
78
|
|
|
return md5($keyword); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @param $keyword |
83
|
|
|
* @return bool |
84
|
|
|
*/ |
85
|
|
|
public function isExisting($keyword) |
86
|
|
|
{ |
87
|
|
|
if (method_exists($this, 'driver_isExisting')) { |
88
|
|
|
return $this->driver_isExisting($keyword); |
|
|
|
|
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
$data = $this->get($keyword); |
|
|
|
|
92
|
|
|
if ($data == null) { |
93
|
|
|
return false; |
94
|
|
|
} else { |
95
|
|
|
return true; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* @param $config_name |
102
|
|
|
* @param string $value |
103
|
|
|
*/ |
104
|
|
|
public function setup($config_name, $value = '') |
105
|
|
|
{ |
106
|
|
|
/** |
107
|
|
|
* Config for class |
108
|
|
|
*/ |
109
|
|
|
if (is_array($config_name)) { |
110
|
|
|
$this->config = array_merge($this->config, $config_name); |
111
|
|
|
} else { |
112
|
|
|
$this->config[ $config_name ] = $value; |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* @param $file |
119
|
|
|
* @return string |
120
|
|
|
* @throws \Exception |
121
|
|
|
*/ |
122
|
|
|
protected function readfile($file) |
123
|
|
|
{ |
124
|
|
|
if (function_exists('file_get_contents')) { |
125
|
|
|
return file_get_contents($file); |
|
|
|
|
126
|
|
|
} else { |
127
|
|
|
$string = ''; |
128
|
|
|
|
129
|
|
|
$file_handle = @fopen($file, 'r'); |
130
|
|
|
if (!$file_handle) { |
131
|
|
|
throw new phpFastCacheDriverException("Can't Read File", 96); |
132
|
|
|
|
133
|
|
|
} |
134
|
|
|
while (!feof($file_handle)) { |
135
|
|
|
$line = fgets($file_handle); |
136
|
|
|
$string .= $line; |
137
|
|
|
} |
138
|
|
|
fclose($file_handle); |
139
|
|
|
|
140
|
|
|
return $string; |
141
|
|
|
} |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Encode data types such as object/array |
146
|
|
|
* for driver that does not support |
147
|
|
|
* non-scalar value |
148
|
|
|
* @param $data |
149
|
|
|
* @return string |
150
|
|
|
*/ |
151
|
|
|
protected function encode($data) |
152
|
|
|
{ |
153
|
|
|
return serialize($data); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Decode data types such as object/array |
158
|
|
|
* for driver that does not support |
159
|
|
|
* non-scalar value |
160
|
|
|
* @param $value |
161
|
|
|
* @return mixed |
162
|
|
|
*/ |
163
|
|
|
protected function decode($value) |
164
|
|
|
{ |
165
|
|
|
$x = @unserialize($value); |
|
|
|
|
166
|
|
|
if ($x == false) { |
167
|
|
|
return $value; |
168
|
|
|
} else { |
169
|
|
|
return $x; |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Check phpModules or CGI |
175
|
|
|
* @return bool |
176
|
|
|
*/ |
177
|
|
|
protected function isPHPModule() |
178
|
|
|
{ |
179
|
|
|
if (PHP_SAPI === 'apache2handler') { |
180
|
|
|
return true; |
181
|
|
|
} else { |
182
|
|
|
if (strpos(PHP_SAPI, 'handler') !== false) { |
183
|
|
|
return true; |
184
|
|
|
} |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
return false; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* @param $class |
193
|
|
|
* @return bool |
194
|
|
|
*/ |
195
|
|
|
protected function isExistingDriver($class) |
196
|
|
|
{ |
197
|
|
|
return class_exists("\\phpFastCache\\Drivers\\{$class}"); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* @param $tag |
203
|
|
|
* @return string |
204
|
|
|
*/ |
205
|
|
|
protected function _getTagName($tag) |
206
|
|
|
{ |
207
|
|
|
return "__tag__" . $tag; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* @param \phpFastCache\Cache\ExtendedCacheItemInterface $item |
212
|
|
|
* @return array |
213
|
|
|
*/ |
214
|
|
|
public function driverPreWrap(ExtendedCacheItemInterface $item) |
215
|
|
|
{ |
216
|
|
|
return [ |
217
|
|
|
self::DRIVER_DATA_WRAPPER_INDEX => $item->get(), |
218
|
|
|
self::DRIVER_TIME_WRAPPER_INDEX => $item->getExpirationDate(), |
219
|
|
|
self::DRIVER_TAGS_WRAPPER_INDEX => $item->getTags(), |
220
|
|
|
]; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* @param array $wrapper |
225
|
|
|
* @return mixed |
226
|
|
|
*/ |
227
|
|
|
public function driverUnwrapData(array $wrapper) |
228
|
|
|
{ |
229
|
|
|
return $wrapper[ self::DRIVER_DATA_WRAPPER_INDEX ]; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* @param array $wrapper |
234
|
|
|
* @return mixed |
235
|
|
|
*/ |
236
|
|
|
public function driverUnwrapTags(array $wrapper) |
237
|
|
|
{ |
238
|
|
|
return $wrapper[ self::DRIVER_TAGS_WRAPPER_INDEX ]; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* @param array $wrapper |
244
|
|
|
* @return \DateTime |
245
|
|
|
*/ |
246
|
|
|
public function driverUnwrapTime(array $wrapper) |
247
|
|
|
{ |
248
|
|
|
return $wrapper[ self::DRIVER_TIME_WRAPPER_INDEX ]; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* @return string |
253
|
|
|
*/ |
254
|
|
|
public function getDriverName() |
255
|
|
|
{ |
256
|
|
|
static $driverName; |
257
|
|
|
|
258
|
|
|
return ($driverName ?: $driverName = ucfirst(substr(strrchr((new \ReflectionObject($this))->getNamespaceName(), '\\'), 1))); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* @param \phpFastCache\Cache\ExtendedCacheItemInterface $item |
263
|
|
|
* @return bool |
264
|
|
|
*/ |
265
|
|
|
public function driverWriteTags(ExtendedCacheItemInterface $item) |
266
|
|
|
{ |
267
|
|
|
$tagsItems = $this->getItems($this->getTagKeys($item->getTags())); |
268
|
|
|
|
269
|
|
|
foreach ($tagsItems as $tagsItem) { |
270
|
|
|
$data = $tagsItem->get(); |
271
|
|
|
$expTimestamp = $item->getExpirationDate()->getTimestamp(); |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Using the key will |
275
|
|
|
* avoid to use array_unique |
276
|
|
|
* that has slow performances |
277
|
|
|
*/ |
278
|
|
|
|
279
|
|
|
$tagsItem->set(array_merge((array) $data, [$item->getKey() => $expTimestamp])); |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Set the expiration date |
283
|
|
|
* of the $tagsItem based |
284
|
|
|
* on the older $item |
285
|
|
|
* expiration date |
286
|
|
|
*/ |
287
|
|
|
if ($expTimestamp > $tagsItem->getExpirationDate()->getTimestamp()) { |
288
|
|
|
$tagsItem->expiresAt($item->getExpirationDate()); |
289
|
|
|
} |
290
|
|
|
$this->driverWrite($tagsItem); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Also update removed tags to |
295
|
|
|
* keep the index up to date |
296
|
|
|
*/ |
297
|
|
|
$tagsItems = $this->getItems($this->getTagKeys($item->getRemovedTags())); |
298
|
|
|
|
299
|
|
|
foreach ($tagsItems as $tagsItem) { |
300
|
|
|
$data = (array) $tagsItem->get(); |
301
|
|
|
|
302
|
|
|
unset($data[ $item->getKey() ]); |
303
|
|
|
$tagsItem->set($data); |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Recalculate the expiration date |
307
|
|
|
* |
308
|
|
|
* If the $tagsItem does not have |
309
|
|
|
* any cache item references left |
310
|
|
|
* then remove it from tagsItems index |
311
|
|
|
*/ |
312
|
|
|
if (count($data)) { |
313
|
|
|
$tagsItem->expiresAt(max($data)); |
314
|
|
|
$this->driverWrite($tagsItem); |
315
|
|
|
} else { |
316
|
|
|
$this->driverDelete($tagsItem); |
317
|
|
|
} |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
return true; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* @param $key |
325
|
|
|
* @return string |
326
|
|
|
*/ |
327
|
|
|
public function getTagKey($key) |
328
|
|
|
{ |
329
|
|
|
return self::DRIVER_TAGS_KEY_PREFIX . $key; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* @param $key |
334
|
|
|
* @return string |
335
|
|
|
*/ |
336
|
|
|
public function getTagKeys(array $keys) |
337
|
|
|
{ |
338
|
|
|
foreach ($keys as &$key) { |
339
|
|
|
$key = $this->getTagKey($key); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
return $keys; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* @param string $tagName |
347
|
|
|
* @return \phpFastCache\Cache\ExtendedCacheItemInterface[] |
348
|
|
|
* @throws InvalidArgumentException |
349
|
|
|
*/ |
350
|
|
|
public function getItemsByTag($tagName) |
351
|
|
|
{ |
352
|
|
|
if (is_string($tagName)) { |
353
|
|
|
$driverResponse = $this->driverRead($this->getTagKey($tagName)); |
354
|
|
|
if ($driverResponse) { |
|
|
|
|
355
|
|
|
$items = (array) $this->driverUnwrapData($driverResponse); |
356
|
|
|
|
357
|
|
|
return $this->getItems(array_unique(array_keys($items))); |
358
|
|
|
} else { |
359
|
|
|
return []; |
360
|
|
|
} |
361
|
|
|
} else { |
362
|
|
|
throw new InvalidArgumentException('$tagName must be a string'); |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* @param array $tagNames |
368
|
|
|
* @return \phpFastCache\Cache\ExtendedCacheItemInterface[] |
369
|
|
|
* @throws InvalidArgumentException |
370
|
|
|
*/ |
371
|
|
|
public function getItemsByTags(array $tagNames) |
372
|
|
|
{ |
373
|
|
|
$items = []; |
374
|
|
|
foreach (array_unique($tagNames) as $tagName) { |
375
|
|
|
$items = array_merge($items, $this->getItemsByTag($tagName)); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
return $items; |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
/** |
382
|
|
|
* @param string $tagName |
383
|
|
|
* @return bool|null |
384
|
|
|
* @throws InvalidArgumentException |
385
|
|
|
*/ |
386
|
|
|
public function deleteItemsByTag($tagName) |
387
|
|
|
{ |
388
|
|
|
if (is_string($tagName)) { |
389
|
|
|
$return = null; |
390
|
|
|
foreach ($this->getItemsByTag($tagName) as $item) { |
391
|
|
|
$result = $this->driverDelete($item); |
392
|
|
|
if ($return !== false) { |
393
|
|
|
$return = $result; |
394
|
|
|
} |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
return $return; |
398
|
|
|
} else { |
399
|
|
|
throw new InvalidArgumentException('$tagName must be a string'); |
400
|
|
|
} |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
/** |
404
|
|
|
* @param array $tagNames |
405
|
|
|
* @return bool|null |
406
|
|
|
* @throws InvalidArgumentException |
407
|
|
|
*/ |
408
|
|
|
public function deleteItemsByTags(array $tagNames) |
409
|
|
|
{ |
410
|
|
|
$return = null; |
411
|
|
|
foreach ($tagNames as $tagName) { |
412
|
|
|
$result = $this->deleteItemsByTag($tagName); |
413
|
|
|
if ($return !== false) { |
414
|
|
|
$return = $result; |
415
|
|
|
} |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
return $return; |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Abstract Drivers Methods |
423
|
|
|
*/ |
424
|
|
|
|
425
|
|
|
/** |
426
|
|
|
* @param string $key |
427
|
|
|
* @return array [ |
428
|
|
|
* 'd' => 'THE ITEM DATA' |
429
|
|
|
* 't' => 'THE ITEM DATE EXPIRATION' |
430
|
|
|
* 'g' => 'THE ITEM TAGS' |
431
|
|
|
* ] |
432
|
|
|
* |
433
|
|
|
*/ |
434
|
|
|
abstract public function driverRead($key); |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* @param \Psr\Cache\CacheItemInterface $item |
438
|
|
|
* @return mixed |
439
|
|
|
*/ |
440
|
|
|
abstract public function driverWrite(CacheItemInterface $item); |
441
|
|
|
|
442
|
|
|
/** |
443
|
|
|
* @return bool |
444
|
|
|
*/ |
445
|
|
|
abstract public function driverClear(); |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* @return bool |
449
|
|
|
*/ |
450
|
|
|
abstract public function driverConnect(); |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* @param \Psr\Cache\CacheItemInterface $item |
454
|
|
|
* @return bool |
455
|
|
|
*/ |
456
|
|
|
abstract public function driverDelete(CacheItemInterface $item); |
457
|
|
|
|
458
|
|
|
/** |
459
|
|
|
* @param \Psr\Cache\CacheItemInterface $item |
460
|
|
|
* @return bool |
461
|
|
|
*/ |
462
|
|
|
abstract public function driverIsHit(CacheItemInterface $item); |
463
|
|
|
|
464
|
|
|
} |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.