Passed
Pull Request — master (#755)
by Georges
01:36
created

CacheItemPoolTrait   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 410
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 134
dl 0
loc 410
rs 8.8798
c 2
b 0
f 0
wmc 44

10 Methods

Rating   Name   Duplication   Size   Complexity  
B save() 0 56 7
A deleteItem() 0 31 4
A clear() 0 14 1
C getItem() 0 138 17
A setItem() 0 18 3
A deleteItems() 0 11 3
A hasItem() 0 3 1
A getItems() 0 8 2
A commit() 0 19 3
A saveDeferred() 0 18 3

How to fix   Complexity   

Complex Class

Complex classes like CacheItemPoolTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CacheItemPoolTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 *
5
 * This file is part of phpFastCache.
6
 *
7
 * @license MIT License (MIT)
8
 *
9
 * For full copyright and license information, please see the docs/CREDITS.txt file.
10
 *
11
 * @author Khoa Bui (khoaofgod)  <[email protected]> https://www.phpfastcache.com
12
 * @author Georges.L (Geolim4)  <[email protected]>
13
 *
14
 */
15
declare(strict_types=1);
16
17
namespace Phpfastcache\Core\Pool;
18
19
use DateTime;
20
use Phpfastcache\Config\ConfigurationOption;
21
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
22
use Phpfastcache\Entities\DriverIO;
23
use Phpfastcache\Entities\ItemBatch;
24
use Phpfastcache\Event\EventManagerDispatcherTrait;
25
use Phpfastcache\Exceptions\{PhpfastcacheCoreException, PhpfastcacheInvalidArgumentException, PhpfastcacheLogicException};
26
use Phpfastcache\Util\ClassNamespaceResolverTrait;
27
use Psr\Cache\CacheItemInterface;
28
use ReflectionClass;
29
use ReflectionObject;
30
use RuntimeException;
31
32
33
/**
34
 * Trait StandardPsr6StructureTrait
35
 * @package phpFastCache\Core
36
 * @property ConfigurationOption $config The config array
37
 * @method ConfigurationOption getConfig() Return the config object
38
 * @method DriverIO getIO() Return the IO object
39
 */
40
trait CacheItemPoolTrait
41
{
42
    use ClassNamespaceResolverTrait;
43
    use EventManagerDispatcherTrait;
44
    use TaggableCacheItemPoolTrait;
45
46
    /**
47
     * @var string
48
     */
49
    protected static $unsupportedKeyChars = '{}()/\@:';
50
51
    /**
52
     * @var array
53
     */
54
    protected $deferredList = [];
55
56
    /**
57
     * @var ExtendedCacheItemInterface[]
58
     */
59
    protected $itemInstances = [];
60
61
    /**CacheItemPoolTrait
62
     * @param CacheItemInterface $item
63
     * @return $this
64
     * @throws PhpfastcacheInvalidArgumentException
65
     */
66
    public function setItem(CacheItemInterface $item)
67
    {
68
        if ($this->getClassNamespace() . '\\Item' === \get_class($item)) {
69
            if(!$this->getConfig()->isUseStaticItemCaching()){
70
                throw new PhpfastcacheLogicException(
71
                    'The static item caching option (useStaticItemCaching) is disabled so you cannot attach an item.'
72
                );
73
            }
74
75
            $this->itemInstances[$item->getKey()] = $item;
76
77
            return $this;
78
        }
79
        throw new PhpfastcacheInvalidArgumentException(
80
            \sprintf(
81
                'Invalid Item Class "%s" for this driver "%s".',
82
                get_class($item),
83
                get_class($this)
84
            )
85
        );
86
    }
87
88
    /**
89
     * @param array $keys
90
     * @return array
91
     * @throws PhpfastcacheCoreException
92
     * @throws PhpfastcacheInvalidArgumentException
93
     * @throws PhpfastcacheLogicException
94
     */
95
    public function getItems(array $keys = [])
96
    {
97
        $collection = [];
98
        foreach ($keys as $key) {
99
            $collection[$key] = $this->getItem($key);
100
        }
101
102
        return $collection;
103
    }
104
105
    /**
106
     * @param string $key
107
     * @return ExtendedCacheItemInterface
108
     * @throws PhpfastcacheInvalidArgumentException
109
     * @throws PhpfastcacheLogicException
110
     * @throws PhpfastcacheCoreException
111
     */
112
    public function getItem($key)
113
    {
114
        if (\is_string($key)) {
0 ignored issues
show
introduced by
The condition is_string($key) is always true.
Loading history...
115
            $item = null;
116
117
            /**
118
             * Replace array_key_exists by isset
119
             * due to performance issue on huge
120
             * loop dispatching operations
121
             */
122
            if (!isset($this->itemInstances[$key]) || !$this->getConfig()->isUseStaticItemCaching()) {
123
                if (\preg_match('~([' . \preg_quote(self::$unsupportedKeyChars, '~') . ']+)~', $key, $matches)) {
124
                    throw new PhpfastcacheInvalidArgumentException(
125
                        'Unsupported key character detected: "' . $matches[1] . '". Please check: https://github.com/PHPSocialNetwork/phpfastcache/wiki/%5BV6%5D-Unsupported-characters-in-key-identifiers'
126
                    );
127
                }
128
129
                $cacheSlamsSpendSeconds = 0;
130
                $class = $this->getClassNamespace() . '\Item';
131
                /** @var $item ExtendedCacheItemInterface */
132
                $item = new $class($this, $key);
133
                $item->setEventManager($this->eventManager);
134
135
                getItemDriverRead:
136
                {
137
                    $driverArray = $this->driverRead($item);
0 ignored issues
show
Bug introduced by
It seems like driverRead() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

137
                    /** @scrutinizer ignore-call */ 
138
                    $driverArray = $this->driverRead($item);
Loading history...
138
139
                    if ($driverArray) {
140
                        if (!\is_array($driverArray)) {
141
                            throw new PhpfastcacheCoreException(
142
                                sprintf(
143
                                    'The driverRead method returned an unexpected variable type: %s',
144
                                    \gettype($driverArray)
145
                                )
146
                            );
147
                        }
148
                        $driverData = $this->driverUnwrapData($driverArray);
0 ignored issues
show
Bug introduced by
The method driverUnwrapData() does not exist on Phpfastcache\Core\Pool\CacheItemPoolTrait. Did you maybe mean driverUnwrapTags()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

148
                        /** @scrutinizer ignore-call */ 
149
                        $driverData = $this->driverUnwrapData($driverArray);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
149
150
                        if ($this->getConfig()['preventCacheSlams']) {
151
                            while ($driverData instanceof ItemBatch) {
152
                                if ($driverData->getItemDate()->getTimestamp() + $this->getConfig()->getCacheSlamsTimeout() < \time()) {
153
                                    /**
154
                                     * The timeout has been reached
155
                                     * Consider that the batch has
156
                                     * failed and serve an empty item
157
                                     * to avoid to get stuck with a
158
                                     * batch item stored in driver
159
                                     */
160
                                    goto getItemDriverExpired;
161
                                }
162
                                /**
163
                                 * @eventName CacheGetItem
164
                                 * @param $this ExtendedCacheItemPoolInterface
165
                                 * @param $driverData ItemBatch
166
                                 * @param $cacheSlamsSpendSeconds int
167
                                 */
168
                                $this->eventManager->dispatch('CacheGetItemInSlamBatch', $this, $driverData, $cacheSlamsSpendSeconds);
169
170
                                /**
171
                                 * Wait for a second before
172
                                 * attempting to get exit
173
                                 * the current batch process
174
                                 */
175
                                \sleep(1);
176
                                $cacheSlamsSpendSeconds++;
177
                                goto getItemDriverRead;
178
                            }
179
                        }
180
181
                        $item->set($driverData);
182
                        $item->expiresAt($this->driverUnwrapEdate($driverArray));
0 ignored issues
show
Bug introduced by
The method driverUnwrapEdate() does not exist on Phpfastcache\Core\Pool\CacheItemPoolTrait. Did you maybe mean driverUnwrapTags()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

182
                        $item->expiresAt($this->/** @scrutinizer ignore-call */ driverUnwrapEdate($driverArray));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
183
184
                        if ($this->getConfig()->isItemDetailedDate()) {
185
                            /**
186
                             * If the itemDetailedDate has been
187
                             * set after caching, we MUST inject
188
                             * a new DateTime object on the fly
189
                             */
190
                            $item->setCreationDate($this->driverUnwrapCdate($driverArray) ?: new DateTime());
0 ignored issues
show
Bug introduced by
The method driverUnwrapCdate() does not exist on Phpfastcache\Core\Pool\CacheItemPoolTrait. Did you maybe mean driverUnwrapTags()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

190
                            $item->setCreationDate($this->/** @scrutinizer ignore-call */ driverUnwrapCdate($driverArray) ?: new DateTime());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
191
                            $item->setModificationDate($this->driverUnwrapMdate($driverArray) ?: new DateTime());
0 ignored issues
show
Bug introduced by
The method driverUnwrapMdate() does not exist on Phpfastcache\Core\Pool\CacheItemPoolTrait. Did you maybe mean driverUnwrapTags()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

191
                            $item->setModificationDate($this->/** @scrutinizer ignore-call */ driverUnwrapMdate($driverArray) ?: new DateTime());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
192
                        }
193
194
                        $item->setTags($this->driverUnwrapTags($driverArray));
195
196
                        getItemDriverExpired:
197
                        if ($item->isExpired()) {
198
                            /**
199
                             * Using driverDelete() instead of delete()
200
                             * to avoid infinite loop caused by
201
                             * getItem() call in delete() method
202
                             * As we MUST return an item in any
203
                             * way, we do not de-register here
204
                             */
205
                            $this->driverDelete($item);
0 ignored issues
show
Bug introduced by
It seems like driverDelete() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

205
                            $this->/** @scrutinizer ignore-call */ 
206
                                   driverDelete($item);
Loading history...
206
207
                            /**
208
                             * Reset the Item
209
                             */
210
                            $item->set(null)
211
                                ->expiresAfter(abs((int)$this->getConfig()['defaultTtl']))
0 ignored issues
show
Bug introduced by
It seems like abs((int)$this->getConfig()['defaultTtl']) can also be of type double; however, parameter $time of Psr\Cache\CacheItemInterface::expiresAfter() does only seem to accept DateInterval|integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

211
                                ->expiresAfter(/** @scrutinizer ignore-type */ abs((int)$this->getConfig()['defaultTtl']))
Loading history...
212
                                ->setHit(false)
213
                                ->setTags([]);
214
                            if ($this->getConfig()->isItemDetailedDate()) {
215
                                /**
216
                                 * If the itemDetailedDate has been
217
                                 * set after caching, we MUST inject
218
                                 * a new DateTime object on the fly
219
                                 */
220
                                $item->setCreationDate(new DateTime());
221
                                $item->setModificationDate(new DateTime());
222
                            }
223
                        } else {
224
                            $item->setHit(true);
225
                        }
226
                    } else {
227
                        $item->expiresAfter(abs((int)$this->getConfig()['defaultTtl']));
228
                    }
229
                }
230
            }else{
231
                $item = $this->itemInstances[$key];
232
            }
233
234
235
            if($item !== null){
236
                /**
237
                 * @eventName CacheGetItem
238
                 * @param $this ExtendedCacheItemPoolInterface
239
                 * @param $this ExtendedCacheItemInterface
240
                 */
241
                $this->eventManager->dispatch('CacheGetItem', $this, $item);
242
243
                $item->isHit() ? $this->getIO()->incReadHit() : $this->getIO()->incReadMiss();
244
245
                return $item;
246
            }
247
            throw new PhpfastcacheInvalidArgumentException(\sprintf('Item %s was not build due to an unknown error', \gettype($key)));
248
        }
249
        throw new PhpfastcacheInvalidArgumentException(\sprintf('$key must be a string, got type "%s" instead.', \gettype($key)));
250
    }
251
252
    /**
253
     * @param string $key
254
     * @return bool
255
     * @throws PhpfastcacheInvalidArgumentException
256
     */
257
    public function hasItem($key)
258
    {
259
        return $this->getItem($key)->isHit();
260
    }
261
262
    /**
263
     * @return bool
264
     */
265
    public function clear()
266
    {
267
        /**
268
         * @eventName CacheClearItem
269
         * @param $this ExtendedCacheItemPoolInterface
270
         * @param $itemInstances ExtendedCacheItemInterface[]
271
         */
272
        $this->eventManager->dispatch('CacheClearItem', $this, $this->itemInstances);
273
274
        $this->getIO()->incWriteHit();
275
        // Faster than detachAllItems()
276
        $this->itemInstances = [];
277
278
        return $this->driverClear();
0 ignored issues
show
Bug introduced by
It seems like driverClear() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

278
        return $this->/** @scrutinizer ignore-call */ driverClear();
Loading history...
279
    }
280
281
    /**
282
     * @param array $keys
283
     * @return bool
284
     * @throws PhpfastcacheInvalidArgumentException
285
     */
286
    public function deleteItems(array $keys)
287
    {
288
        $return = null;
289
        foreach ($keys as $key) {
290
            $result = $this->deleteItem($key);
291
            if ($result !== false) {
292
                $return = $result;
293
            }
294
        }
295
296
        return (bool)$return;
297
    }
298
299
    /**
300
     * @param string $key
301
     * @return bool
302
     * @throws PhpfastcacheInvalidArgumentException
303
     */
304
    public function deleteItem($key)
305
    {
306
        $item = $this->getItem($key);
307
        if ($item->isHit() && $this->driverDelete($item)) {
308
            $item->setHit(false);
309
            $this->getIO()->incWriteHit();
310
311
            /**
312
             * @eventName CacheCommitItem
313
             * @param $this ExtendedCacheItemPoolInterface
314
             * @param $item ExtendedCacheItemInterface
315
             */
316
            $this->eventManager->dispatch('CacheDeleteItem', $this, $item);
317
318
            /**
319
             * De-register the item instance
320
             * then collect gc cycles
321
             */
322
            $this->deregisterItem($key);
0 ignored issues
show
Bug introduced by
It seems like deregisterItem() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

322
            $this->/** @scrutinizer ignore-call */ 
323
                   deregisterItem($key);
Loading history...
323
324
            /**
325
             * Perform a tag cleanup to avoid memory leaks
326
             */
327
            if (\strpos($key, self::DRIVER_TAGS_KEY_PREFIX) !== 0) {
0 ignored issues
show
Bug introduced by
The constant Phpfastcache\Core\Pool\C...:DRIVER_TAGS_KEY_PREFIX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
328
                $this->cleanItemTags($item);
329
            }
330
331
            return true;
332
        }
333
334
        return false;
335
    }
336
337
    /**
338
     * @param CacheItemInterface $item
339
     * @return CacheItemInterface
340
     * @throws RuntimeException
341
     */
342
    public function saveDeferred(CacheItemInterface $item)
343
    {
344
        if (!\array_key_exists($item->getKey(), $this->itemInstances)) {
345
            $this->itemInstances[$item->getKey()] = $item;
346
        } else {
347
            if (\spl_object_hash($item) !== \spl_object_hash($this->itemInstances[$item->getKey()])) {
348
                throw new RuntimeException('Spl object hash mismatches ! You probably tried to save a detached item which has been already retrieved from cache.');
349
            }
350
        }
351
352
        /**
353
         * @eventName CacheSaveDeferredItem
354
         * @param $this ExtendedCacheItemPoolInterface
355
         * @param $this ExtendedCacheItemInterface
356
         */
357
        $this->eventManager->dispatch('CacheSaveDeferredItem', $this, $item);
358
359
        return $this->deferredList[$item->getKey()] = $item;
360
    }
361
362
    /**
363
     * @return bool
364
     * @throws PhpfastcacheInvalidArgumentException
365
     */
366
    public function commit()
367
    {
368
        /**
369
         * @eventName CacheCommitItem
370
         * @param $this ExtendedCacheItemPoolInterface
371
         * @param $deferredList ExtendedCacheItemInterface[]
372
         */
373
        $this->eventManager->dispatch('CacheCommitItem', $this, $this->deferredList);
374
375
        $return = null;
376
        foreach ($this->deferredList as $key => $item) {
377
            $result = $this->save($item);
378
            if ($return !== false) {
379
                unset($this->deferredList[$key]);
380
                $return = $result;
381
            }
382
        }
383
384
        return (bool)$return;
385
    }
386
387
    /**
388
     * @param CacheItemInterface $item
389
     * @return bool
390
     * @throws PhpfastcacheInvalidArgumentException
391
     * @throws PhpfastcacheLogicException
392
     * @throws \ReflectionException
393
     */
394
    public function save(CacheItemInterface $item)
395
    {
396
        /**
397
         * @var ExtendedCacheItemInterface $item
398
         *
399
         * Replace array_key_exists by isset
400
         * due to performance issue on huge
401
         * loop dispatching operations
402
         */
403
        if (!isset($this->itemInstances[$item->getKey()])) {
404
            if($this->getConfig()->isUseStaticItemCaching()){
405
                $this->itemInstances[$item->getKey()] = $item;
406
            }
407
        } else {
408
            if (\spl_object_hash($item) !== \spl_object_hash($this->itemInstances[$item->getKey()])) {
409
                throw new RuntimeException('Spl object hash mismatches ! You probably tried to save a detached item which has been already retrieved from cache.');
410
            }
411
        }
412
413
        /**
414
         * @eventName CacheSaveItem
415
         * @param $this ExtendedCacheItemPoolInterface
416
         * @param $this ExtendedCacheItemInterface
417
         */
418
        $this->eventManager->dispatch('CacheSaveItem', $this, $item);
419
420
421
        if ($this->getConfig()->isPreventCacheSlams()) {
422
            /**
423
             * @var $itemBatch ExtendedCacheItemInterface
424
             */
425
            $class = new ReflectionClass((new ReflectionObject($this))->getNamespaceName() . '\Item');
426
            $itemBatch = $class->newInstanceArgs([$this, $item->getKey()]);
427
            $itemBatch->setEventManager($this->eventManager)
428
                ->set(new ItemBatch($item->getKey(), new DateTime()))
429
                ->expiresAfter($this->getConfig()->getCacheSlamsTimeout());
430
431
            /**
432
             * To avoid SPL mismatches
433
             * we have to re-attach the
434
             * original item to the pool
435
             */
436
            $this->driverWrite($itemBatch);
0 ignored issues
show
Bug introduced by
The method driverWrite() does not exist on Phpfastcache\Core\Pool\CacheItemPoolTrait. Did you maybe mean driverWriteTags()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

436
            $this->/** @scrutinizer ignore-call */ 
437
                   driverWrite($itemBatch);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
437
            $this->detachItem($itemBatch);
0 ignored issues
show
Bug introduced by
It seems like detachItem() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

437
            $this->/** @scrutinizer ignore-call */ 
438
                   detachItem($itemBatch);
Loading history...
438
            $this->attachItem($item);
0 ignored issues
show
Bug introduced by
It seems like attachItem() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

438
            $this->/** @scrutinizer ignore-call */ 
439
                   attachItem($item);
Loading history...
439
        }
440
441
442
        if ($this->driverWrite($item) && $this->driverWriteTags($item)) {
443
            $item->setHit(true);
444
            $this->getIO()->incWriteHit();
445
446
            return true;
447
        }
448
449
        return false;
450
    }
451
}
452