Completed
Push — master ( be9660...707803 )
by Georges
16s queued 13s
created

TaggableCacheItemPoolTrait   F

Complexity

Total Complexity 63

Size/Duplication

Total Lines 390
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 133
c 1
b 0
f 0
dl 0
loc 390
rs 3.36
wmc 63

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getTagKey() 0 3 1
A appendItemsByTag() 0 12 3
A deleteItemsByTags() 0 11 3
A cleanItemTags() 0 3 1
A getTagKeys() 0 7 1
A incrementItemsByTag() 0 12 4
B getItemsByTags() 0 31 10
A deleteItemsByTag() 0 15 4
A prependItemsByTags() 0 11 3
A prependItemsByTag() 0 12 3
A driverUnwrapTags() 0 3 1
A getItemsByTag() 0 11 4
A decrementItemsByTags() 0 11 3
A getItemsByTagsAsJsonString() 0 7 1
A incrementItemsByTags() 0 11 3
A decrementItemsByTag() 0 12 4
B driverWriteTags() 0 74 8
A fetchItemsByTagFromBackend() 0 28 3
A appendItemsByTags() 0 11 3

How to fix   Complexity   

Complex Class

Complex classes like TaggableCacheItemPoolTrait 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 TaggableCacheItemPoolTrait, 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\Core\Item\ExtendedCacheItemInterface;
21
use Phpfastcache\Exceptions\{PhpfastcacheInvalidArgumentException, PhpfastcacheLogicException};
22
use Psr\Cache\{CacheItemInterface};
23
24
/**
25
 * Trait TaggableCacheItemPoolTrait
26
 * @package Phpfastcache\Core\Pool
27
 * @method ExtendedCacheItemInterface getItem(string $key) Return the config object
28
 * @method ExtendedCacheItemInterface[] getItems(array $keys) Return the config object
29
 */
30
trait TaggableCacheItemPoolTrait
31
{
32
    /**
33
     * @inheritdoc
34
     */
35
    public function getItemsByTagsAsJsonString(array $tagNames, int $option = 0, int $depth = 512, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): string
36
    {
37
        $callback = static function (CacheItemInterface $item) {
38
            return $item->get();
39
        };
40
41
        return \json_encode(\array_map($callback, \array_values($this->getItemsByTags($tagNames, $strategy))), $option, $depth);
42
    }
43
44
    /**
45
     * @inheritdoc
46
     */
47
    public function getItemsByTags(array $tagNames, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): array
48
    {
49
        $items = [];
50
        foreach (\array_unique($tagNames) as $tagName) {
51
            if (\is_string($tagName)) {
52
                $items[] = $this->fetchItemsByTagFromBackend($tagName);
53
            } else {
54
                throw new PhpfastcacheInvalidArgumentException('$tagName must be a a string');
55
            }
56
        }
57
58
        $items = \array_merge([], ...$items);
59
60
        switch ($strategy) {
61
            case TaggableCacheItemPoolInterface::TAG_STRATEGY_ALL:
62
                foreach ($items as $key => $item) {
63
                    if (\array_diff($tagNames, $item->getTags())) {
64
                        unset($items[$key]);
65
                    }
66
                }
67
                break;
68
69
            case TaggableCacheItemPoolInterface::TAG_STRATEGY_ONLY:
70
                foreach ($items as $key => $item) {
71
                    if (\array_diff($tagNames, $item->getTags()) || \array_diff($item->getTags(), $tagNames)) {
72
                        unset($items[$key]);
73
                    }
74
                }
75
                break;
76
        }
77
        return $items;
78
    }
79
80
    /**
81
     * @param string $tagName
82
     * @return array
83
     * @throws PhpfastcacheInvalidArgumentException
84
     */
85
    protected function fetchItemsByTagFromBackend(string $tagName): array
86
    {
87
        if (\is_string($tagName)) {
0 ignored issues
show
introduced by
The condition is_string($tagName) is always true.
Loading history...
88
            $driverResponse = $this->getItem($this->getTagKey($tagName));
89
            if ($driverResponse->isHit()) {
90
                $tagsItems = (array)$driverResponse->get();
91
92
                /**
93
                 * getItems() may provides expired item(s)
94
                 * themselves provided by a cache of item
95
                 * keys based stored the tag item.
96
                 * Therefore we pass a filter callback
97
                 * to remove the expired Item(s) provided by
98
                 * the item keys passed through getItems()
99
                 *
100
                 * #headache
101
                 */
102
                return \array_filter(
103
                    $this->getItems(\array_unique(\array_keys($tagsItems))),
104
                    static function (ExtendedCacheItemInterface $item) {
105
                        return $item->isHit();
106
                    }
107
                );
108
            }
109
            return [];
110
        }
111
112
        throw new PhpfastcacheInvalidArgumentException('$tagName must be a string');
113
    }
114
115
    /**
116
     * @param string $key
117
     * @return string
118
     */
119
    protected function getTagKey(string $key): string
120
    {
121
        return self::DRIVER_TAGS_KEY_PREFIX . $key;
0 ignored issues
show
Bug introduced by
The constant Phpfastcache\Core\Pool\T...:DRIVER_TAGS_KEY_PREFIX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
122
    }
123
124
    /**
125
     * @inheritdoc
126
     */
127
    public function deleteItemsByTags(array $tagNames, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
128
    {
129
        $return = null;
130
        foreach ($tagNames as $tagName) {
131
            $result = $this->deleteItemsByTag($tagName, $strategy);
132
            if ($return !== false) {
133
                $return = $result;
134
            }
135
        }
136
137
        return (bool)$return;
138
    }
139
140
    /**
141
     * @inheritdoc
142
     */
143
    public function deleteItemsByTag(string $tagName, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
144
    {
145
        if (\is_string($tagName)) {
0 ignored issues
show
introduced by
The condition is_string($tagName) is always true.
Loading history...
146
            $return = null;
147
            foreach ($this->getItemsByTag($tagName, $strategy) as $item) {
148
                $result = $this->deleteItem($item->getKey());
0 ignored issues
show
Bug introduced by
The method deleteItem() does not exist on Phpfastcache\Core\Pool\TaggableCacheItemPoolTrait. Did you maybe mean deleteItemsByTag()? ( 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
                $result = $this->deleteItem($item->getKey());

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
                if ($return !== false) {
150
                    $return = $result;
151
                }
152
            }
153
154
            return (bool)$return;
155
        }
156
157
        throw new PhpfastcacheInvalidArgumentException('$tagName must be a string');
158
    }
159
160
    /**
161
     * @inheritdoc
162
     */
163
    public function getItemsByTag(string $tagName, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): array
164
    {
165
        $items = $this->fetchItemsByTagFromBackend($tagName);
166
        if ($strategy === TaggableCacheItemPoolInterface::TAG_STRATEGY_ONLY) {
167
            foreach ($items as $key => $item) {
168
                if (\array_diff($item->getTags(), $tagName)) {
0 ignored issues
show
Bug introduced by
$tagName of type string is incompatible with the type array expected by parameter $array2 of array_diff(). ( Ignorable by Annotation )

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

168
                if (\array_diff($item->getTags(), /** @scrutinizer ignore-type */ $tagName)) {
Loading history...
169
                    unset($items[$key]);
170
                }
171
            }
172
        }
173
        return $items;
174
    }
175
176
    /**
177
     * @inheritdoc
178
     */
179
    public function incrementItemsByTags(array $tagNames, int $step = 1, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
180
    {
181
        $return = null;
182
        foreach ($tagNames as $tagName) {
183
            $result = $this->incrementItemsByTag($tagName, $step, $strategy);
184
            if ($return !== false) {
185
                $return = $result;
186
            }
187
        }
188
189
        return (bool)$return;
190
    }
191
192
    /**
193
     * @inheritdoc
194
     */
195
    public function incrementItemsByTag(string $tagName, int $step = 1, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
196
    {
197
        if (\is_string($tagName) && \is_int($step)) {
0 ignored issues
show
introduced by
The condition is_int($step) is always true.
Loading history...
198
            foreach ($this->getItemsByTag($tagName, $strategy) as $item) {
199
                $item->increment($step);
200
                $this->saveDeferred($item);
0 ignored issues
show
Bug introduced by
It seems like saveDeferred() 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

200
                $this->/** @scrutinizer ignore-call */ 
201
                       saveDeferred($item);
Loading history...
201
            }
202
203
            return (bool)$this->commit();
0 ignored issues
show
Bug introduced by
It seems like commit() 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

203
            return (bool)$this->/** @scrutinizer ignore-call */ commit();
Loading history...
204
        }
205
206
        throw new PhpfastcacheInvalidArgumentException('$tagName must be a string and $step an integer');
207
    }
208
209
    /**
210
     * @inheritdoc
211
     */
212
    public function decrementItemsByTags(array $tagNames, int $step = 1, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
213
    {
214
        $return = null;
215
        foreach ($tagNames as $tagName) {
216
            $result = $this->decrementItemsByTag($tagName, $step, $strategy);
217
            if ($return !== false) {
218
                $return = $result;
219
            }
220
        }
221
222
        return (bool)$return;
223
    }
224
225
    /**
226
     * @inheritdoc
227
     */
228
    public function decrementItemsByTag(string $tagName, int $step = 1, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
229
    {
230
        if (\is_string($tagName) && \is_int($step)) {
0 ignored issues
show
introduced by
The condition is_int($step) is always true.
Loading history...
231
            foreach ($this->getItemsByTag($tagName, $strategy) as $item) {
232
                $item->decrement($step);
233
                $this->saveDeferred($item);
234
            }
235
236
            return (bool)$this->commit();
237
        }
238
239
        throw new PhpfastcacheInvalidArgumentException('$tagName must be a string and $step an integer');
240
    }
241
242
    /**
243
     * @inheritdoc
244
     */
245
    public function appendItemsByTags(array $tagNames, $data, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
246
    {
247
        $return = null;
248
        foreach ($tagNames as $tagName) {
249
            $result = $this->appendItemsByTag($tagName, $data, $strategy);
250
            if ($return !== false) {
251
                $return = $result;
252
            }
253
        }
254
255
        return (bool)$return;
256
    }
257
258
    /**
259
     * @inheritdoc
260
     */
261
    public function appendItemsByTag(string $tagName, $data, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
262
    {
263
        if (\is_string($tagName)) {
0 ignored issues
show
introduced by
The condition is_string($tagName) is always true.
Loading history...
264
            foreach ($this->getItemsByTag($tagName, $strategy) as $item) {
265
                $item->append($data);
266
                $this->saveDeferred($item);
267
            }
268
269
            return (bool)$this->commit();
270
        }
271
272
        throw new PhpfastcacheInvalidArgumentException('$tagName must be a string');
273
    }
274
275
    /**
276
     * @inheritdoc
277
     */
278
    public function prependItemsByTags(array $tagNames, $data, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
279
    {
280
        $return = null;
281
        foreach ($tagNames as $tagName) {
282
            $result = $this->prependItemsByTag($tagName, $data, $strategy);
283
            if ($return !== false) {
284
                $return = $result;
285
            }
286
        }
287
288
        return (bool)$return;
289
    }
290
291
    /**
292
     * @inheritdoc
293
     */
294
    public function prependItemsByTag(string $tagName, $data, int $strategy = TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE): bool
295
    {
296
        if (\is_string($tagName)) {
0 ignored issues
show
introduced by
The condition is_string($tagName) is always true.
Loading history...
297
            foreach ($this->getItemsByTag($tagName, $strategy) as $item) {
298
                $item->prepend($data);
299
                $this->saveDeferred($item);
300
            }
301
302
            return (bool)$this->commit();
303
        }
304
305
        throw new PhpfastcacheInvalidArgumentException('$tagName must be a string');
306
    }
307
308
    /**
309
     * @param array $wrapper
310
     * @return mixed
311
     */
312
    protected function driverUnwrapTags(array $wrapper)
313
    {
314
        return $wrapper[self::DRIVER_TAGS_WRAPPER_INDEX];
0 ignored issues
show
Bug introduced by
The constant Phpfastcache\Core\Pool\T...IVER_TAGS_WRAPPER_INDEX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
315
    }
316
317
    /**
318
     * @param ExtendedCacheItemInterface $item
319
     * @throws PhpfastcacheInvalidArgumentException
320
     * @throws PhpfastcacheLogicException
321
     */
322
    protected function cleanItemTags(ExtendedCacheItemInterface $item)
323
    {
324
        $this->driverWriteTags($item->removeTags($item->getTags()));
325
    }
326
327
    /**
328
     * @param ExtendedCacheItemInterface $item
329
     * @return bool
330
     * @throws PhpfastcacheInvalidArgumentException
331
     * @throws PhpfastcacheLogicException
332
     */
333
    protected function driverWriteTags(ExtendedCacheItemInterface $item): bool
334
    {
335
        /**
336
         * Do not attempt to write tags
337
         * on tags item, it can leads
338
         * to an infinite recursive calls
339
         */
340
        if (\strpos($item->getKey(), self::DRIVER_TAGS_KEY_PREFIX) === 0) {
0 ignored issues
show
Bug introduced by
The constant Phpfastcache\Core\Pool\T...:DRIVER_TAGS_KEY_PREFIX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
341
            throw new PhpfastcacheLogicException('Trying to set tag(s) to an Tag item index: ' . $item->getKey());
342
        }
343
344
        if (!$item->getTags() && !$item->getRemovedTags()) {
345
            return true;
346
        }
347
348
        /**
349
         * @var $tagsItems ExtendedCacheItemInterface[]
350
         */
351
        $tagsItems = $this->getItems($this->getTagKeys($item->getTags()));
352
353
        foreach ($tagsItems as $tagsItem) {
354
            $data = $tagsItem->get();
355
            $expTimestamp = $item->getExpirationDate()->getTimestamp();
356
357
            /**
358
             * Using the key will
359
             * avoid to use array_unique
360
             * that has slow performances
361
             */
362
363
            $tagsItem->set(\array_merge((array)$data, [$item->getKey() => $expTimestamp]));
364
365
            /**
366
             * Set the expiration date
367
             * of the $tagsItem based
368
             * on the older $item
369
             * expiration date
370
             */
371
            if ($expTimestamp > $tagsItem->getExpirationDate()->getTimestamp()) {
372
                $tagsItem->expiresAt($item->getExpirationDate());
373
            }
374
            $this->driverWrite($tagsItem);
0 ignored issues
show
Bug introduced by
The method driverWrite() does not exist on Phpfastcache\Core\Pool\TaggableCacheItemPoolTrait. 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

374
            $this->/** @scrutinizer ignore-call */ 
375
                   driverWrite($tagsItem);

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...
375
            $tagsItem->setHit(true);
376
        }
377
378
        /**
379
         * Also update removed tags to
380
         * keep the index up to date
381
         */
382
        $tagsItems = $this->getItems($this->getTagKeys($item->getRemovedTags()));
383
384
        foreach ($tagsItems as $tagsItem) {
385
            $data = (array)$tagsItem->get();
386
387
            unset($data[$item->getKey()]);
388
            $tagsItem->set($data);
389
390
            /**
391
             * Recalculate the expiration date
392
             *
393
             * If the $tagsItem does not have
394
             * any cache item references left
395
             * then remove it from tagsItems index
396
             */
397
            if (\count($data)) {
398
                $tagsItem->expiresAt((new DateTime())->setTimestamp(max($data)));
399
                $this->driverWrite($tagsItem);
400
                $tagsItem->setHit(true);
401
            } else {
402
                $this->deleteItem($tagsItem->getKey());
403
            }
404
        }
405
406
        return true;
407
    }
408
409
    /**
410
     * @param array $keys
411
     * @return array
412
     */
413
    protected function getTagKeys(array $keys): array
414
    {
415
        return \array_map(
416
            function (string $key) {
417
                return $this->getTagKey($key);
418
            },
419
            $keys
420
        );
421
    }
422
}
423