Completed
Push — master ( 4e7427...4a94bb )
by Georges
12s
created

CacheItemPoolTrait::deleteItem()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 0
loc 31
rs 9.424
c 0
b 0
f 0
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]> https://www.phpfastcache.com
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 *
13
 */
14
declare(strict_types=1);
15
16
namespace Phpfastcache\Core\Pool;
17
18
use Phpfastcache\CacheManager;
19
use Phpfastcache\Config\ConfigurationOption;
20
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
21
use Phpfastcache\Entities\ItemBatch;
22
use Phpfastcache\Event\EventInterface;
23
use Phpfastcache\EventManager;
24
use Phpfastcache\Exceptions\{
25
    PhpfastcacheCoreException, PhpfastcacheInvalidArgumentException, PhpfastcacheLogicException
26
};
27
use Phpfastcache\Util\ClassNamespaceResolverTrait;
28
use Psr\Cache\CacheItemInterface;
29
30
/**
31
 * Trait StandardPsr6StructureTrait
32
 * @package phpFastCache\Core
33
 * @property ConfigurationOption $config The config array
34
 * @method ConfigurationOption getConfig() Return the config object
35
 */
36
trait CacheItemPoolTrait
37
{
38
    use ClassNamespaceResolverTrait;
39
40
    /**
41
     * @var string
42
     */
43
    protected static $unsupportedKeyChars = '{}()/\@:';
44
45
    /**
46
     * @var array
47
     */
48
    protected $deferredList = [];
49
50
    /**
51
     * @var ExtendedCacheItemInterface[]
52
     */
53
    protected $itemInstances = [];
54
55
    /**
56
     * @var EventInterface
57
     */
58
    protected $eventManager;
59
60
    /**
61
     * @param string $key
62
     * @return \Phpfastcache\Core\Item\ExtendedCacheItemInterface
63
     * @throws PhpfastcacheInvalidArgumentException
64
     * @throws PhpfastcacheLogicException
65
     * @throws PhpfastcacheCoreException
66
     */
67
    public function getItem($key)
68
    {
69
        if (\is_string($key)) {
0 ignored issues
show
introduced by
The condition is_string($key) is always true.
Loading history...
70
            /**
71
             * Replace array_key_exists by isset
72
             * due to performance issue on huge
73
             * loop dispatching operations
74
             */
75
            if (!isset($this->itemInstances[$key])) {
76
                if (\preg_match('~([' . \preg_quote(self::$unsupportedKeyChars, '~') . ']+)~', $key, $matches)) {
77
                    throw new PhpfastcacheInvalidArgumentException('Unsupported key character detected: "' . $matches[1] . '". Please check: https://github.com/PHPSocialNetwork/phpfastcache/wiki/%5BV6%5D-Unsupported-characters-in-key-identifiers');
78
                }
79
80
                CacheManager::$ReadHits++;
81
                $cacheSlamsSpendSeconds = 0;
82
                $class = $this->getClassNamespace() . '\Item';
83
                /** @var $item ExtendedCacheItemInterface */
84
                $item = new $class($this, $key);
85
                $item->setEventManager($this->eventManager);
86
87
                getItemDriverRead:
88
                {
89
                    $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

89
                    /** @scrutinizer ignore-call */ 
90
                    $driverArray = $this->driverRead($item);
Loading history...
90
91
                    if ($driverArray) {
92
                        if (!\is_array($driverArray)) {
93
                            throw new PhpfastcacheCoreException(\sprintf('The driverRead method returned an unexpected variable type: %s',
94
                                \gettype($driverArray)));
95
                        }
96
                        $driverData = $this->driverUnwrapData($driverArray);
0 ignored issues
show
Bug introduced by
It seems like driverUnwrapData() 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

96
                        /** @scrutinizer ignore-call */ 
97
                        $driverData = $this->driverUnwrapData($driverArray);
Loading history...
97
98
                        if ($this->getConfig()['preventCacheSlams']) {
99
                            while ($driverData instanceof ItemBatch) {
100
                                if ($driverData->getItemDate()->getTimestamp() + $this->getConfig()->getCacheSlamsTimeout() < \time()) {
101
                                    /**
102
                                     * The timeout has been reached
103
                                     * Consider that the batch has
104
                                     * failed and serve an empty item
105
                                     * to avoid to get stuck with a
106
                                     * batch item stored in driver
107
                                     */
108
                                    goto getItemDriverExpired;
109
                                }
110
                                /**
111
                                 * @eventName CacheGetItem
112
                                 * @param $this ExtendedCacheItemPoolInterface
113
                                 * @param $driverData ItemBatch
114
                                 * @param $cacheSlamsSpendSeconds int
115
                                 */
116
                                $this->eventManager->dispatch('CacheGetItemInSlamBatch', $this, $driverData, $cacheSlamsSpendSeconds);
117
118
                                /**
119
                                 * Wait for a second before
120
                                 * attempting to get exit
121
                                 * the current batch process
122
                                 */
123
                                \sleep(1);
124
                                $cacheSlamsSpendSeconds++;
125
                                goto getItemDriverRead;
126
                            }
127
                        }
128
129
                        $item->set($driverData);
130
                        $item->expiresAt($this->driverUnwrapEdate($driverArray));
0 ignored issues
show
Bug introduced by
It seems like driverUnwrapEdate() 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

130
                        $item->expiresAt($this->/** @scrutinizer ignore-call */ driverUnwrapEdate($driverArray));
Loading history...
131
132
                        if ($this->getConfig()->isItemDetailedDate()) {
133
                            /**
134
                             * If the itemDetailedDate has been
135
                             * set after caching, we MUST inject
136
                             * a new DateTime object on the fly
137
                             */
138
                            $item->setCreationDate($this->driverUnwrapCdate($driverArray) ?: new \DateTime());
0 ignored issues
show
Bug introduced by
It seems like driverUnwrapCdate() 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

138
                            $item->setCreationDate($this->/** @scrutinizer ignore-call */ driverUnwrapCdate($driverArray) ?: new \DateTime());
Loading history...
139
                            $item->setModificationDate($this->driverUnwrapMdate($driverArray) ?: new \DateTime());
0 ignored issues
show
Bug introduced by
It seems like driverUnwrapMdate() 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

139
                            $item->setModificationDate($this->/** @scrutinizer ignore-call */ driverUnwrapMdate($driverArray) ?: new \DateTime());
Loading history...
140
                        }
141
142
                        $item->setTags($this->driverUnwrapTags($driverArray));
0 ignored issues
show
Bug introduced by
It seems like driverUnwrapTags() 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

142
                        $item->setTags($this->/** @scrutinizer ignore-call */ driverUnwrapTags($driverArray));
Loading history...
143
144
                        getItemDriverExpired:
145
                        if ($item->isExpired()) {
146
                            /**
147
                             * Using driverDelete() instead of delete()
148
                             * to avoid infinite loop caused by
149
                             * getItem() call in delete() method
150
                             * As we MUST return an item in any
151
                             * way, we do not de-register here
152
                             */
153
                            $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

153
                            $this->/** @scrutinizer ignore-call */ 
154
                                   driverDelete($item);
Loading history...
154
155
                            /**
156
                             * Reset the Item
157
                             */
158
                            $item->set(null)
159
                                ->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 null|integer|DateInterval, 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

159
                                ->expiresAfter(/** @scrutinizer ignore-type */ \abs((int)$this->getConfig()['defaultTtl']))
Loading history...
160
                                ->setHit(false)
161
                                ->setTags([]);
162
                            if ($this->getConfig()->isItemDetailedDate()) {
163
164
                                /**
165
                                 * If the itemDetailedDate has been
166
                                 * set after caching, we MUST inject
167
                                 * a new DateTime object on the fly
168
                                 */
169
                                $item->setCreationDate(new \DateTime());
170
                                $item->setModificationDate(new \DateTime());
171
                            }
172
                        } else {
173
                            $item->setHit(true);
174
                        }
175
                    } else {
176
                        $item->expiresAfter(\abs((int)$this->getConfig()['defaultTtl']));
177
                    }
178
                }
179
            }
180
        } else {
181
            throw new PhpfastcacheInvalidArgumentException(\sprintf('$key must be a string, got type "%s" instead.', \gettype($key)));
182
        }
183
184
        /**
185
         * @eventName CacheGetItem
186
         * @param $this ExtendedCacheItemPoolInterface
187
         * @param $this ExtendedCacheItemInterface
188
         */
189
        $this->eventManager->dispatch('CacheGetItem', $this, $this->itemInstances[$key]);
190
191
        return $this->itemInstances[$key];
192
    }
193
194
    /**
195
     * @param \Psr\Cache\CacheItemInterface $item
196
     * @return $this
197
     * @throws PhpfastcacheInvalidArgumentException
198
     */
199
    public function setItem(CacheItemInterface $item)
200
    {
201
        if ($this->getClassNamespace() . '\\Item' === \get_class($item)) {
202
            $this->itemInstances[$item->getKey()] = $item;
203
204
            return $this;
205
        }
206
207
        throw new PhpfastcacheInvalidArgumentException(\sprintf('Invalid Item Class "%s" for this driver.', \get_class($item)));
208
    }
209
210
    /**
211
     * @param array $keys
212
     * @return CacheItemInterface[]
213
     * @throws PhpfastcacheInvalidArgumentException
214
     */
215
    public function getItems(array $keys = [])
216
    {
217
        $collection = [];
218
        foreach ($keys as $key) {
219
            $collection[$key] = $this->getItem($key);
220
        }
221
222
        return $collection;
223
    }
224
225
    /**
226
     * @param string $key
227
     * @return bool
228
     * @throws PhpfastcacheInvalidArgumentException
229
     */
230
    public function hasItem($key)
231
    {
232
        return $this->getItem($key)->isHit();
233
    }
234
235
    /**
236
     * @return bool
237
     */
238
    public function clear()
239
    {
240
        /**
241
         * @eventName CacheClearItem
242
         * @param $this ExtendedCacheItemPoolInterface
243
         * @param $deferredList ExtendedCacheItemInterface[]
244
         */
245
        $this->eventManager->dispatch('CacheClearItem', $this, $this->itemInstances);
246
247
        CacheManager::$WriteHits++;
248
        $this->itemInstances = [];
249
250
        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

250
        return $this->/** @scrutinizer ignore-call */ driverClear();
Loading history...
251
    }
252
253
    /**
254
     * @param string $key
255
     * @return bool
256
     * @throws PhpfastcacheInvalidArgumentException
257
     */
258
    public function deleteItem($key)
259
    {
260
        $item = $this->getItem($key);
261
        if ($item->isHit() && $this->driverDelete($item)) {
262
            $item->setHit(false);
263
            CacheManager::$WriteHits++;
264
265
            /**
266
             * @eventName CacheCommitItem
267
             * @param $this ExtendedCacheItemPoolInterface
268
             * @param $item ExtendedCacheItemInterface
269
             */
270
            $this->eventManager->dispatch('CacheDeleteItem', $this, $item);
271
272
            /**
273
             * De-register the item instance
274
             * then collect gc cycles
275
             */
276
            $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

276
            $this->/** @scrutinizer ignore-call */ 
277
                   deregisterItem($key);
Loading history...
277
278
            /**
279
             * Perform a tag cleanup to avoid memory leaks
280
             */
281
            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...
282
                $this->cleanItemTags($item);
0 ignored issues
show
Bug introduced by
It seems like cleanItemTags() 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

282
                $this->/** @scrutinizer ignore-call */ 
283
                       cleanItemTags($item);
Loading history...
283
            }
284
285
            return true;
286
        }
287
288
        return false;
289
    }
290
291
    /**
292
     * @param array $keys
293
     * @return bool
294
     * @throws PhpfastcacheInvalidArgumentException
295
     */
296
    public function deleteItems(array $keys)
297
    {
298
        $return = null;
299
        foreach ($keys as $key) {
300
            $result = $this->deleteItem($key);
301
            if ($result !== false) {
302
                $return = $result;
303
            }
304
        }
305
306
        return (bool)$return;
307
    }
308
309
    /**
310
     * @param \Psr\Cache\CacheItemInterface $item
311
     * @return mixed
312
     * @throws PhpfastcacheInvalidArgumentException
313
     * @throws \RuntimeException
314
     */
315
    public function save(CacheItemInterface $item)
316
    {
317
        /**
318
         * @var ExtendedCacheItemInterface $item
319
         *
320
         * Replace array_key_exists by isset
321
         * due to performance issue on huge
322
         * loop dispatching operations
323
         */
324
        if (!isset($this->itemInstances[$item->getKey()])) {
325
            $this->itemInstances[$item->getKey()] = $item;
326
        } else {
327
            if (\spl_object_hash($item) !== \spl_object_hash($this->itemInstances[$item->getKey()])) {
328
                throw new \RuntimeException('Spl object hash mismatches ! You probably tried to save a detached item which has been already retrieved from cache.');
329
            }
330
        }
331
332
        /**
333
         * @eventName CacheSaveItem
334
         * @param $this ExtendedCacheItemPoolInterface
335
         * @param $this ExtendedCacheItemInterface
336
         */
337
        $this->eventManager->dispatch('CacheSaveItem', $this, $item);
338
339
340
        if ($this->getConfig()->isPreventCacheSlams()) {
341
            /**
342
             * @var $itemBatch ExtendedCacheItemInterface
343
             */
344
            $class = new \ReflectionClass((new \ReflectionObject($this))->getNamespaceName() . '\Item');
345
            $itemBatch = $class->newInstanceArgs([$this, $item->getKey()]);
346
            $itemBatch->setEventManager($this->eventManager)
347
                ->set(new ItemBatch($item->getKey(), new \DateTime()))
348
                ->expiresAfter($this->getConfig()->getCacheSlamsTimeout());
349
350
            /**
351
             * To avoid SPL mismatches
352
             * we have to re-attach the
353
             * original item to the pool
354
             */
355
            $this->driverWrite($itemBatch);
0 ignored issues
show
Bug introduced by
It seems like driverWrite() 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

355
            $this->/** @scrutinizer ignore-call */ 
356
                   driverWrite($itemBatch);
Loading history...
356
            $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

356
            $this->/** @scrutinizer ignore-call */ 
357
                   detachItem($itemBatch);
Loading history...
357
            $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

357
            $this->/** @scrutinizer ignore-call */ 
358
                   attachItem($item);
Loading history...
358
        }
359
360
361
        if ($this->driverWrite($item) && $this->driverWriteTags($item)) {
0 ignored issues
show
Bug introduced by
It seems like driverWriteTags() 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

361
        if ($this->driverWrite($item) && $this->/** @scrutinizer ignore-call */ driverWriteTags($item)) {
Loading history...
362
            $item->setHit(true);
363
            CacheManager::$WriteHits++;
364
365
            return true;
366
        }
367
368
        return false;
369
    }
370
371
372
    /**
373
     * @param \Psr\Cache\CacheItemInterface $item
374
     * @return \Psr\Cache\CacheItemInterface
375
     * @throws \RuntimeException
376
     */
377
    public function saveDeferred(CacheItemInterface $item)
378
    {
379
        if (!\array_key_exists($item->getKey(), $this->itemInstances)) {
380
            $this->itemInstances[$item->getKey()] = $item;
381
        } else {
382
            if (\spl_object_hash($item) !== \spl_object_hash($this->itemInstances[$item->getKey()])) {
383
                throw new \RuntimeException('Spl object hash mismatches ! You probably tried to save a detached item which has been already retrieved from cache.');
384
            }
385
        }
386
387
        /**
388
         * @eventName CacheSaveDeferredItem
389
         * @param $this ExtendedCacheItemPoolInterface
390
         * @param $this ExtendedCacheItemInterface
391
         */
392
        $this->eventManager->dispatch('CacheSaveDeferredItem', $this, $item);
393
394
        return $this->deferredList[$item->getKey()] = $item;
395
    }
396
397
    /**
398
     * @return mixed|null
399
     * @throws PhpfastcacheInvalidArgumentException
400
     */
401
    public function commit()
402
    {
403
        /**
404
         * @eventName CacheCommitItem
405
         * @param $this ExtendedCacheItemPoolInterface
406
         * @param $deferredList ExtendedCacheItemInterface[]
407
         */
408
        $this->eventManager->dispatch('CacheCommitItem', $this, $this->deferredList);
409
410
        $return = null;
411
        foreach ($this->deferredList as $key => $item) {
412
            $result = $this->save($item);
413
            if ($return !== false) {
414
                unset($this->deferredList[$key]);
415
                $return = $result;
416
            }
417
        }
418
419
        return (bool)$return;
420
    }
421
}