These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace voku\cache; |
||
6 | |||
7 | use voku\cache\Exception\InvalidArgumentException; |
||
8 | |||
9 | /** |
||
10 | * Cache: global-cache class |
||
11 | * |
||
12 | * can use different cache-adapter: |
||
13 | * - Redis |
||
14 | * - Memcache / Memcached |
||
15 | * - APC / APCu |
||
16 | * - Xcache |
||
17 | * - Array |
||
18 | * - File / OpCache |
||
19 | */ |
||
20 | class Cache implements iCache |
||
21 | { |
||
22 | /** |
||
23 | * @var iAdapter |
||
24 | */ |
||
25 | protected $adapter; |
||
26 | |||
27 | /** |
||
28 | * @var iSerializer |
||
29 | */ |
||
30 | protected $serializer; |
||
31 | |||
32 | /** |
||
33 | * @var string |
||
34 | */ |
||
35 | protected $prefix = ''; |
||
36 | |||
37 | /** |
||
38 | * @var bool |
||
39 | */ |
||
40 | protected $isReady = false; |
||
41 | |||
42 | /** |
||
43 | * @var bool |
||
44 | */ |
||
45 | protected $isActive = true; |
||
46 | |||
47 | /** |
||
48 | * @var bool |
||
49 | */ |
||
50 | protected $useCheckForDev; |
||
51 | |||
52 | /** |
||
53 | * @var bool |
||
54 | */ |
||
55 | protected $useCheckForAdminSession; |
||
56 | |||
57 | /** |
||
58 | * @var bool |
||
59 | */ |
||
60 | protected $useCheckForServerIpIsClientIp; |
||
61 | |||
62 | /** |
||
63 | * @var string |
||
64 | */ |
||
65 | protected $disableCacheGetParameter; |
||
66 | |||
67 | /** |
||
68 | * @var bool |
||
69 | */ |
||
70 | protected $isAdminSession; |
||
71 | |||
72 | /** |
||
73 | * @var array |
||
74 | */ |
||
75 | protected static $STATIC_CACHE = []; |
||
76 | |||
77 | /** |
||
78 | * @var array |
||
79 | */ |
||
80 | protected static $STATIC_CACHE_EXPIRE = []; |
||
81 | |||
82 | /** |
||
83 | * @var array |
||
84 | */ |
||
85 | protected static $STATIC_CACHE_COUNTER = []; |
||
86 | |||
87 | /** |
||
88 | * @var int |
||
89 | */ |
||
90 | protected $staticCacheHitCounter = 10; |
||
91 | |||
92 | /** |
||
93 | * __construct |
||
94 | * |
||
95 | * @param iAdapter|null $adapter |
||
96 | * @param iSerializer|null $serializer |
||
97 | * @param bool $checkForUsage <p>admin-session || server-ip == client-ip || check for |
||
98 | * dev</p> |
||
99 | * @param bool $cacheEnabled <p>false will disable the cache (use it e.g. for global |
||
100 | * settings)</p> |
||
101 | * @param bool|string $isAdminSession <p>set a admin-id, if the user is a admin (so we can |
||
102 | * disable cache for this user) |
||
103 | * @param bool $useCheckForAdminSession <p>use $isAdminSession flag or not</p> |
||
104 | * @param bool $useCheckForDev <p>use checkForDev() or not</p> |
||
105 | * @param bool $useCheckForServerIpIsClientIp <p>use check for server-ip == client-ip or not</p> |
||
106 | * @param string $disableCacheGetParameter <p>set the _GET parameter for disabling the cache, |
||
107 | * disable this check via empty string</p> |
||
108 | */ |
||
109 | 65 | public function __construct( |
|
110 | iAdapter $adapter = null, |
||
111 | iSerializer $serializer = null, |
||
112 | bool $checkForUsage = true, |
||
113 | bool $cacheEnabled = true, |
||
114 | bool $isAdminSession = false, |
||
115 | bool $useCheckForDev = true, |
||
116 | bool $useCheckForAdminSession = true, |
||
117 | bool $useCheckForServerIpIsClientIp = true, |
||
118 | string $disableCacheGetParameter = 'testWithoutCache' |
||
119 | ) { |
||
120 | 65 | $this->isAdminSession = $isAdminSession; |
|
0 ignored issues
–
show
|
|||
121 | |||
122 | 65 | $this->useCheckForDev = $useCheckForDev; |
|
123 | 65 | $this->useCheckForAdminSession = $useCheckForAdminSession; |
|
124 | 65 | $this->useCheckForServerIpIsClientIp = $useCheckForServerIpIsClientIp; |
|
125 | |||
126 | 65 | $this->disableCacheGetParameter = $disableCacheGetParameter; |
|
127 | |||
128 | // First check if the cache is active at all. |
||
129 | 65 | $this->isActive = $cacheEnabled; |
|
130 | if ( |
||
131 | 65 | $this->isActive === true |
|
132 | && |
||
133 | 65 | $checkForUsage === true |
|
134 | ) { |
||
135 | $this->setActive($this->isCacheActiveForTheCurrentUser()); |
||
136 | } |
||
137 | |||
138 | // If the cache is active, then try to auto-connect to the best possible cache-system. |
||
139 | 65 | if ($this->isActive === true) { |
|
140 | 65 | $this->setPrefix($this->getTheDefaultPrefix()); |
|
141 | |||
142 | 65 | if ($adapter === null) { |
|
143 | $adapter = $this->autoConnectToAvailableCacheSystem(); |
||
144 | } |
||
145 | |||
146 | // INFO: Memcache(d) has his own "serializer", so don't use it twice |
||
147 | 65 | if (!\is_object($serializer) && $serializer === null) { |
|
148 | if ( |
||
149 | $adapter instanceof AdapterMemcached |
||
150 | || |
||
151 | $adapter instanceof AdapterMemcache |
||
152 | ) { |
||
153 | $serializer = new SerializerNo(); |
||
154 | } else { |
||
155 | // set default serializer |
||
156 | $serializer = new SerializerIgbinary(); |
||
157 | } |
||
158 | } |
||
159 | } |
||
160 | |||
161 | // Final checks ... |
||
162 | if ( |
||
163 | 65 | $serializer !== null |
|
164 | && |
||
165 | 65 | $adapter !== null |
|
166 | ) { |
||
167 | 65 | $this->setCacheIsReady(true); |
|
168 | |||
169 | 65 | $this->adapter = $adapter; |
|
170 | 65 | $this->serializer = $serializer; |
|
171 | } |
||
172 | 65 | } |
|
173 | |||
174 | /** |
||
175 | * enable / disable the cache |
||
176 | * |
||
177 | * @param bool $isActive |
||
178 | */ |
||
179 | public function setActive(bool $isActive) |
||
180 | { |
||
181 | $this->isActive = $isActive; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * check if the current use is a admin || dev || server == client |
||
186 | * |
||
187 | * @return bool |
||
188 | */ |
||
189 | public function isCacheActiveForTheCurrentUser(): bool |
||
190 | { |
||
191 | $active = true; |
||
192 | |||
193 | // test the cache, with this GET-parameter |
||
194 | if ($this->disableCacheGetParameter) { |
||
195 | $testCache = isset($_GET[$this->disableCacheGetParameter]) ? (int) $_GET[$this->disableCacheGetParameter] : 0; |
||
196 | } else { |
||
197 | $testCache = 0; |
||
198 | } |
||
199 | |||
200 | if ($testCache !== 1) { |
||
201 | if ( |
||
202 | // admin session is active |
||
203 | ( |
||
204 | $this->useCheckForAdminSession |
||
205 | && |
||
206 | $this->isAdminSession |
||
207 | ) |
||
208 | || |
||
209 | // server == client |
||
210 | ( |
||
211 | $this->useCheckForServerIpIsClientIp === true |
||
212 | && |
||
213 | isset($_SERVER['SERVER_ADDR']) |
||
214 | && |
||
215 | $_SERVER['SERVER_ADDR'] === $this->getClientIp() |
||
216 | ) |
||
217 | || |
||
218 | // user is a dev |
||
219 | ( |
||
220 | $this->useCheckForDev === true |
||
221 | && |
||
222 | $this->checkForDev() === true |
||
223 | ) |
||
224 | ) { |
||
225 | $active = false; |
||
226 | } |
||
227 | } |
||
228 | |||
229 | return $active; |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * returns the IP address of the client |
||
234 | * |
||
235 | * @param bool $trust_proxy_headers <p> |
||
236 | * Whether or not to trust the |
||
237 | * proxy headers HTTP_CLIENT_IP |
||
238 | * and HTTP_X_FORWARDED_FOR. ONLY |
||
239 | * use if your $_SERVER is behind a |
||
240 | * proxy that sets these values |
||
241 | * </p> |
||
242 | * |
||
243 | * @return string |
||
244 | */ |
||
245 | protected function getClientIp(bool $trust_proxy_headers = false): string |
||
246 | { |
||
247 | $remoteAddr = $_SERVER['REMOTE_ADDR'] ?? 'NO_REMOTE_ADDR'; |
||
248 | |||
249 | if ($trust_proxy_headers) { |
||
250 | return $remoteAddr; |
||
251 | } |
||
252 | |||
253 | if (!empty($_SERVER['HTTP_CLIENT_IP'])) { |
||
254 | $ip = $_SERVER['HTTP_CLIENT_IP']; |
||
255 | } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { |
||
256 | $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; |
||
257 | } else { |
||
258 | $ip = $remoteAddr; |
||
259 | } |
||
260 | |||
261 | return $ip; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * Check for local developer. |
||
266 | * |
||
267 | * @return bool |
||
268 | */ |
||
269 | protected function checkForDev(): bool |
||
270 | { |
||
271 | $return = false; |
||
272 | |||
273 | if (\function_exists('checkForDev')) { |
||
274 | $return = checkForDev(); |
||
275 | } else { |
||
276 | |||
277 | // for testing with dev-address |
||
278 | $noDev = isset($_GET['noDev']) ? (int) $_GET['noDev'] : 0; |
||
279 | $remoteAddr = $_SERVER['REMOTE_ADDR'] ?? 'NO_REMOTE_ADDR'; |
||
280 | |||
281 | if ( |
||
282 | $noDev !== 1 |
||
283 | && |
||
284 | ( |
||
285 | $remoteAddr === '127.0.0.1' |
||
286 | || |
||
287 | $remoteAddr === '::1' |
||
288 | || |
||
289 | \PHP_SAPI === 'cli' |
||
290 | ) |
||
291 | ) { |
||
292 | $return = true; |
||
293 | } |
||
294 | } |
||
295 | |||
296 | return $return; |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Set the default-prefix via "SERVER"-var + "SESSION"-language. |
||
301 | */ |
||
302 | 65 | protected function getTheDefaultPrefix(): string |
|
303 | { |
||
304 | 65 | return ($_SERVER['SERVER_NAME'] ?? '') . '_' . |
|
305 | 65 | ($_SERVER['THEME'] ?? '') . '_' . |
|
306 | 65 | ($_SERVER['STAGE'] ?? '') . '_' . |
|
307 | 65 | ($_SESSION['language'] ?? '') . '_' . |
|
308 | 65 | ($_SESSION['language_extra'] ?? ''); |
|
309 | } |
||
310 | |||
311 | /** |
||
312 | * Auto-connect to the available cache-system on the server. |
||
313 | * |
||
314 | * @return iAdapter |
||
315 | */ |
||
316 | protected function autoConnectToAvailableCacheSystem(): iAdapter |
||
317 | { |
||
318 | static $adapterCache; |
||
319 | |||
320 | if (\is_object($adapterCache) && $adapterCache instanceof iAdapter) { |
||
321 | return $adapterCache; |
||
322 | } |
||
323 | |||
324 | $memcached = null; |
||
325 | $isMemcachedAvailable = false; |
||
326 | if (\extension_loaded('memcached')) { |
||
327 | /** @noinspection PhpComposerExtensionStubsInspection */ |
||
328 | $memcached = new \Memcached(); |
||
329 | /** @noinspection PhpUsageOfSilenceOperatorInspection */ |
||
330 | $isMemcachedAvailable = @$memcached->addServer('127.0.0.1', 11211); |
||
331 | } |
||
332 | |||
333 | if ($isMemcachedAvailable === false) { |
||
334 | $memcached = null; |
||
335 | } |
||
336 | |||
337 | $adapterMemcached = new AdapterMemcached($memcached); |
||
338 | if ($adapterMemcached->installed() === true) { |
||
339 | |||
340 | // ------------------------------------------------------------- |
||
341 | // "Memcached" |
||
342 | // ------------------------------------------------------------- |
||
343 | $adapter = $adapterMemcached; |
||
344 | } else { |
||
345 | $memcache = null; |
||
346 | $isMemcacheAvailable = false; |
||
347 | /** @noinspection ClassConstantCanBeUsedInspection */ |
||
348 | if (\class_exists('\Memcache')) { |
||
349 | /** @noinspection PhpComposerExtensionStubsInspection */ |
||
350 | $memcache = new \Memcache(); |
||
351 | /** @noinspection PhpUsageOfSilenceOperatorInspection */ |
||
352 | $isMemcacheAvailable = @$memcache->connect('127.0.0.1', 11211); |
||
353 | } |
||
354 | |||
355 | if ($isMemcacheAvailable === false) { |
||
356 | $memcache = null; |
||
357 | } |
||
358 | |||
359 | $adapterMemcache = new AdapterMemcache($memcache); |
||
360 | if ($adapterMemcache->installed() === true) { |
||
361 | |||
362 | // ------------------------------------------------------------- |
||
363 | // "Memcache" |
||
364 | // ------------------------------------------------------------- |
||
365 | $adapter = $adapterMemcache; |
||
366 | } else { |
||
367 | $redis = null; |
||
368 | $isRedisAvailable = false; |
||
369 | if ( |
||
370 | \extension_loaded('redis') |
||
371 | && |
||
372 | \class_exists('\Predis\Client') |
||
373 | ) { |
||
374 | /** @noinspection PhpUndefinedNamespaceInspection */ |
||
375 | /** @noinspection PhpUndefinedClassInspection */ |
||
376 | $redis = new \Predis\Client( |
||
377 | [ |
||
378 | 'scheme' => 'tcp', |
||
379 | 'host' => '127.0.0.1', |
||
380 | 'port' => 6379, |
||
381 | 'timeout' => '2.0', |
||
382 | ] |
||
383 | ); |
||
384 | |||
385 | try { |
||
386 | /** @noinspection PhpUndefinedMethodInspection */ |
||
387 | $redis->connect(); |
||
388 | /** @noinspection PhpUndefinedMethodInspection */ |
||
389 | $isRedisAvailable = $redis->getConnection()->isConnected(); |
||
390 | } catch (\Exception $e) { |
||
391 | // nothing |
||
392 | } |
||
393 | } |
||
394 | |||
395 | if ($isRedisAvailable === false) { |
||
396 | $redis = null; |
||
397 | } |
||
398 | |||
399 | $adapterRedis = new AdapterPredis($redis); |
||
400 | if ($adapterRedis->installed() === true) { |
||
401 | |||
402 | // ------------------------------------------------------------- |
||
403 | // Redis |
||
404 | // ------------------------------------------------------------- |
||
405 | $adapter = $adapterRedis; |
||
406 | } else { |
||
407 | $adapterXcache = new AdapterXcache(); |
||
408 | if ($adapterXcache->installed() === true) { |
||
409 | |||
410 | // ------------------------------------------------------------- |
||
411 | // "Xcache" |
||
412 | // ------------------------------------------------------------- |
||
413 | $adapter = $adapterXcache; |
||
414 | } else { |
||
415 | $adapterApcu = new AdapterApcu(); |
||
416 | if ($adapterApcu->installed() === true) { |
||
417 | |||
418 | // ------------------------------------------------------------- |
||
419 | // "APCu" |
||
420 | // ------------------------------------------------------------- |
||
421 | $adapter = $adapterApcu; |
||
422 | } else { |
||
423 | $adapterApc = new AdapterApc(); |
||
424 | if ($adapterApc->installed() === true) { |
||
425 | |||
426 | // ------------------------------------------------------------- |
||
427 | // "APC" |
||
428 | // ------------------------------------------------------------- |
||
429 | $adapter = $adapterApc; |
||
430 | } else { |
||
431 | $adapterObCache = new AdapterOpCache(); |
||
432 | if ($adapterObCache->installed() === true) { |
||
433 | |||
434 | // ------------------------------------------------------------- |
||
435 | // OpCache (via PHP-files) |
||
436 | // ------------------------------------------------------------- |
||
437 | $adapter = $adapterObCache; |
||
438 | } else { |
||
439 | |||
440 | // ------------------------------------------------------------- |
||
441 | // Static-PHP-Cache |
||
442 | // ------------------------------------------------------------- |
||
443 | $adapter = new AdapterArray(); |
||
444 | } |
||
445 | } |
||
446 | } |
||
447 | } |
||
448 | } |
||
449 | } |
||
450 | } |
||
451 | |||
452 | // save to static cache |
||
453 | $adapterCache = $adapter; |
||
454 | |||
455 | return $adapter; |
||
456 | } |
||
457 | |||
458 | /** |
||
459 | * Set "isReady" state. |
||
460 | * |
||
461 | * @param bool $isReady |
||
462 | */ |
||
463 | 65 | protected function setCacheIsReady(bool $isReady) |
|
464 | { |
||
465 | 65 | $this->isReady = $isReady; |
|
466 | 65 | } |
|
467 | |||
468 | /** |
||
469 | * Get the "isReady" state. |
||
470 | * |
||
471 | * @return bool |
||
472 | */ |
||
473 | 5 | public function getCacheIsReady(): bool |
|
474 | { |
||
475 | 5 | return $this->isReady; |
|
476 | } |
||
477 | |||
478 | /** |
||
479 | * Get cached-item by key. |
||
480 | * |
||
481 | * @param string $key |
||
482 | * @param int $forceStaticCacheHitCounter |
||
483 | * |
||
484 | * @return mixed |
||
485 | */ |
||
486 | 31 | public function getItem(string $key, int $forceStaticCacheHitCounter = 0) |
|
487 | { |
||
488 | 31 | if (!$this->adapter instanceof iAdapter) { |
|
489 | return null; |
||
490 | } |
||
491 | |||
492 | 31 | $storeKey = $this->calculateStoreKey($key); |
|
493 | |||
494 | // check if we already using static-cache |
||
495 | 31 | $useStaticCache = true; |
|
496 | 31 | if ($this->adapter instanceof AdapterArray) { |
|
497 | 6 | $useStaticCache = false; |
|
498 | } |
||
499 | |||
500 | 31 | if (!isset(self::$STATIC_CACHE_COUNTER[$storeKey])) { |
|
501 | 24 | self::$STATIC_CACHE_COUNTER[$storeKey] = 0; |
|
502 | } |
||
503 | |||
504 | // get from static-cache |
||
505 | if ( |
||
506 | 31 | $useStaticCache === true |
|
507 | && |
||
508 | 31 | $this->checkForStaticCache($storeKey) === true |
|
509 | ) { |
||
510 | 8 | return self::$STATIC_CACHE[$storeKey]; |
|
511 | } |
||
512 | |||
513 | 29 | $serialized = $this->adapter->get($storeKey); |
|
514 | 29 | $value = $serialized ? $this->serializer->unserialize($serialized) : null; |
|
515 | |||
516 | 29 | self::$STATIC_CACHE_COUNTER[$storeKey]++; |
|
517 | |||
518 | // save into static-cache if needed |
||
519 | if ( |
||
520 | 29 | $useStaticCache === true |
|
521 | && |
||
522 | ( |
||
523 | ( |
||
524 | 23 | $forceStaticCacheHitCounter !== 0 |
|
525 | && |
||
526 | self::$STATIC_CACHE_COUNTER[$storeKey] >= $forceStaticCacheHitCounter |
||
527 | ) |
||
528 | || |
||
529 | ( |
||
530 | 23 | $this->staticCacheHitCounter !== 0 |
|
531 | && |
||
532 | 29 | self::$STATIC_CACHE_COUNTER[$storeKey] >= $this->staticCacheHitCounter |
|
533 | ) |
||
534 | ) |
||
535 | ) { |
||
536 | 9 | self::$STATIC_CACHE[$storeKey] = $value; |
|
537 | } |
||
538 | |||
539 | 29 | return $value; |
|
540 | } |
||
541 | |||
542 | /** |
||
543 | * Calculate store-key (prefix + $rawKey). |
||
544 | * |
||
545 | * @param string $rawKey |
||
546 | * |
||
547 | * @return string |
||
548 | */ |
||
549 | 53 | protected function calculateStoreKey(string $rawKey): string |
|
550 | { |
||
551 | 53 | $str = $this->getPrefix() . $rawKey; |
|
552 | |||
553 | 53 | if ($this->adapter instanceof AdapterFileAbstract) { |
|
554 | 24 | $str = $this->cleanStoreKey($str); |
|
555 | } |
||
556 | |||
557 | 53 | return $str; |
|
558 | } |
||
559 | |||
560 | /** |
||
561 | * Clean store-key (required e.g. for the "File"-Adapter). |
||
562 | * |
||
563 | * @param string $str |
||
564 | * |
||
565 | * @return string |
||
566 | */ |
||
567 | 24 | protected function cleanStoreKey(string $str): string |
|
568 | { |
||
569 | 24 | return \md5($str); |
|
570 | } |
||
571 | |||
572 | /** |
||
573 | * Get the prefix. |
||
574 | * |
||
575 | * @return string |
||
576 | */ |
||
577 | 53 | public function getPrefix(): string |
|
578 | { |
||
579 | 53 | return $this->prefix; |
|
580 | } |
||
581 | |||
582 | /** |
||
583 | * !!! Set the prefix. !!! |
||
584 | * |
||
585 | * WARNING: Do not use if you don't know what you do. Because this will overwrite the default prefix. |
||
586 | * |
||
587 | * @param string $prefix |
||
588 | */ |
||
589 | 65 | public function setPrefix(string $prefix) |
|
590 | { |
||
591 | 65 | $this->prefix = $prefix; |
|
592 | 65 | } |
|
593 | |||
594 | /** |
||
595 | * Get the current value, when the static cache is used. |
||
596 | * |
||
597 | * @return int |
||
598 | */ |
||
599 | public function getStaticCacheHitCounter(): int |
||
600 | { |
||
601 | return $this->staticCacheHitCounter; |
||
602 | } |
||
603 | |||
604 | /** |
||
605 | * Set the static-hit-counter: Who often do we hit the cache, before we use static cache? |
||
606 | * |
||
607 | * @param int $staticCacheHitCounter |
||
608 | */ |
||
609 | public function setStaticCacheHitCounter(int $staticCacheHitCounter) |
||
610 | { |
||
611 | $this->staticCacheHitCounter = $staticCacheHitCounter; |
||
612 | } |
||
613 | |||
614 | /** |
||
615 | * Set cache-item by key => value + date. |
||
616 | * |
||
617 | * @param string $key |
||
618 | * @param mixed $value |
||
619 | * @param \DateTime $date <p>If the date is in the past, we will remove the existing cache-item.</p> |
||
620 | * |
||
621 | * @throws InvalidArgumentException <p>If the $date is in the past.</p> |
||
622 | * |
||
623 | * @return bool |
||
624 | */ |
||
625 | 13 | public function setItemToDate(string $key, $value, \DateTime $date): bool |
|
626 | { |
||
627 | 13 | $ttl = $date->getTimestamp() - \time(); |
|
628 | |||
629 | 13 | if ($ttl <= 0) { |
|
630 | 1 | throw new InvalidArgumentException('Date in the past.'); |
|
631 | } |
||
632 | |||
633 | 12 | return $this->setItem($key, $value, $ttl); |
|
634 | } |
||
635 | |||
636 | /** |
||
637 | * Set cache-item by key => value + ttl. |
||
638 | * |
||
639 | * @param string $key |
||
640 | * @param mixed $value |
||
641 | * @param \DateInterval|int|null $ttl |
||
642 | * |
||
643 | * @throws InvalidArgumentException |
||
644 | * |
||
645 | * @return bool |
||
646 | */ |
||
647 | 32 | public function setItem(string $key, $value, $ttl = 0): bool |
|
648 | { |
||
649 | if ( |
||
650 | 32 | !$this->adapter instanceof iAdapter |
|
651 | || |
||
652 | 32 | !$this->serializer instanceof iSerializer |
|
653 | ) { |
||
654 | return false; |
||
655 | } |
||
656 | |||
657 | 32 | $storeKey = $this->calculateStoreKey($key); |
|
658 | 32 | $serialized = $this->serializer->serialize($value); |
|
659 | |||
660 | // update static-cache, if it's exists |
||
661 | 32 | if (\array_key_exists($storeKey, self::$STATIC_CACHE) === true) { |
|
662 | 5 | self::$STATIC_CACHE[$storeKey] = $value; |
|
663 | } |
||
664 | |||
665 | 32 | if ($ttl) { |
|
666 | 16 | if ($ttl instanceof \DateInterval) { |
|
667 | // Converting to a TTL in seconds |
||
668 | 1 | $dateTimeNow = new \DateTime('now'); |
|
669 | 1 | $ttl = $dateTimeNow->add($ttl)->getTimestamp() - \time(); |
|
670 | } |
||
671 | |||
672 | // always cache the TTL time, maybe we need this later ... |
||
673 | 16 | self::$STATIC_CACHE_EXPIRE[$storeKey] = ($ttl ? (int) $ttl + \time() : 0); |
|
674 | |||
675 | 16 | return $this->adapter->setExpired($storeKey, $serialized, $ttl); |
|
676 | } |
||
677 | |||
678 | 16 | return $this->adapter->set($storeKey, $serialized); |
|
679 | } |
||
680 | |||
681 | /** |
||
682 | * Remove a cached-item. |
||
683 | * |
||
684 | * @param string $key |
||
685 | * |
||
686 | * @return bool |
||
687 | */ |
||
688 | 5 | public function removeItem(string $key): bool |
|
689 | { |
||
690 | 5 | if (!$this->adapter instanceof iAdapter) { |
|
691 | return false; |
||
692 | } |
||
693 | |||
694 | 5 | $storeKey = $this->calculateStoreKey($key); |
|
695 | |||
696 | // remove static-cache |
||
697 | if ( |
||
698 | 5 | !empty(self::$STATIC_CACHE) |
|
699 | && |
||
700 | 5 | \array_key_exists($storeKey, self::$STATIC_CACHE) === true |
|
701 | ) { |
||
702 | unset(self::$STATIC_CACHE[$storeKey], self::$STATIC_CACHE_COUNTER[$storeKey], self::$STATIC_CACHE_EXPIRE[$storeKey] |
||
703 | ); |
||
704 | } |
||
705 | |||
706 | 5 | return $this->adapter->remove($storeKey); |
|
707 | } |
||
708 | |||
709 | /** |
||
710 | * Remove all cached-items. |
||
711 | * |
||
712 | * @return bool |
||
713 | */ |
||
714 | 3 | public function removeAll(): bool |
|
715 | { |
||
716 | 3 | if (!$this->adapter instanceof iAdapter) { |
|
717 | return false; |
||
718 | } |
||
719 | |||
720 | // remove static-cache |
||
721 | 3 | if (!empty(self::$STATIC_CACHE)) { |
|
722 | 3 | self::$STATIC_CACHE = []; |
|
723 | 3 | self::$STATIC_CACHE_COUNTER = []; |
|
724 | 3 | self::$STATIC_CACHE_EXPIRE = []; |
|
725 | } |
||
726 | |||
727 | 3 | return $this->adapter->removeAll(); |
|
728 | } |
||
729 | |||
730 | /** |
||
731 | * Check if cached-item exists. |
||
732 | * |
||
733 | * @param string $key |
||
734 | * |
||
735 | * @return bool |
||
736 | */ |
||
737 | 11 | public function existsItem(string $key): bool |
|
738 | { |
||
739 | 11 | if (!$this->adapter instanceof iAdapter) { |
|
740 | return false; |
||
741 | } |
||
742 | |||
743 | 11 | $storeKey = $this->calculateStoreKey($key); |
|
744 | |||
745 | // check static-cache |
||
746 | 11 | if ($this->checkForStaticCache($storeKey) === true) { |
|
747 | return true; |
||
748 | } |
||
749 | |||
750 | 11 | return $this->adapter->exists($storeKey); |
|
751 | } |
||
752 | |||
753 | /** |
||
754 | * @param string $storeKey |
||
755 | * |
||
756 | * @return bool |
||
757 | */ |
||
758 | 36 | protected function checkForStaticCache(string $storeKey): bool |
|
759 | { |
||
760 | 36 | return !empty(self::$STATIC_CACHE) |
|
761 | && |
||
762 | 36 | \array_key_exists($storeKey, self::$STATIC_CACHE) === true |
|
763 | && |
||
764 | 36 | \array_key_exists($storeKey, self::$STATIC_CACHE_EXPIRE) === true |
|
765 | && |
||
766 | 36 | \time() <= self::$STATIC_CACHE_EXPIRE[$storeKey]; |
|
767 | } |
||
768 | |||
769 | /** |
||
770 | * Get the current adapter class-name. |
||
771 | * |
||
772 | * @return string |
||
773 | */ |
||
774 | 3 | public function getUsedAdapterClassName(): string |
|
775 | { |
||
776 | 3 | if ($this->adapter) { |
|
777 | /** @noinspection GetClassUsageInspection */ |
||
778 | 3 | return \get_class($this->adapter); |
|
779 | } |
||
780 | |||
781 | return ''; |
||
782 | } |
||
783 | |||
784 | /** |
||
785 | * Get the current serializer class-name. |
||
786 | * |
||
787 | * @return string |
||
788 | */ |
||
789 | 3 | public function getUsedSerializerClassName(): string |
|
790 | { |
||
791 | 3 | if ($this->serializer) { |
|
792 | /** @noinspection GetClassUsageInspection */ |
||
793 | 3 | return \get_class($this->serializer); |
|
794 | } |
||
795 | |||
796 | return ''; |
||
797 | } |
||
798 | } |
||
799 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.