Passed
Pull Request — master (#30)
by Alexander
02:06
created

TagDependency   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 106
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 30
c 1
b 0
f 0
dl 0
loc 106
rs 10
wmc 14

7 Methods

Rating   Name   Duplication   Size   Complexity  
A touchKeys() 0 9 2
A buildCacheKey() 0 3 1
A __construct() 0 3 1
A generateDependencyData() 0 15 4
A invalidate() 0 7 2
A getTimestamps() 0 12 3
A isChanged() 0 4 1
1
<?php
2
namespace Yiisoft\CacheOld\DependencyOld;
3
4
use Psr\SimpleCache\InvalidArgumentException;
5
use Yiisoft\CacheOld\CacheInterface;
6
7
/**
8
 * TagDependency associates a cached value with one or multiple {@see TagDependency::$tags}.
9
 *
10
 * By calling {@see TagDependency::invalidate()}, you can invalidate all cached values that are associated with the specified tag name(s).
11
 *
12
 * ```php
13
 * // setting multiple cache keys to store data forever and tagging them with "user-123"
14
 * $cache->set('user_42_profile', '', 0, new TagDependency('user-123'));
15
 * $cache->set('user_42_stats', '', 0, new TagDependency('user-123'));
16
 *
17
 * // invalidating all keys tagged with "user-123"
18
 * TagDependency::invalidate($cache, 'user-123');
19
 * ```
20
 */
21
final class TagDependency extends Dependency
22
{
23
    /**
24
     * @var array a list of tag names for this dependency
25
     */
26
    private $tags;
27
28
    /**
29
     * @param string|array $tags a list of tag names for this dependency. For a single tag, you may specify it as a string
30
     */
31
    public function __construct($tags)
32
    {
33
        $this->tags = (array)$tags;
34
    }
35
36
    /**
37
     * Generates the data needed to determine if dependency has been changed.
38
     * This method does nothing in this class.
39
     * @param CacheInterface $cache the cache component that is currently evaluating this dependency
40
     * @return mixed the data needed to determine if dependency has been changed.
41
     * @throws InvalidArgumentException
42
     */
43
    protected function generateDependencyData(CacheInterface $cache): array
44
    {
45
        $timestamps = $this->getTimestamps($cache, $this->tags);
46
47
        $newKeys = [];
48
        foreach ($timestamps as $key => $timestamp) {
49
            if ($timestamp === false) {
50
                $newKeys[] = $key;
51
            }
52
        }
53
        if (!empty($newKeys)) {
54
            $timestamps = array_merge($timestamps, self::touchKeys($cache, $newKeys));
55
        }
56
57
        return $timestamps;
58
    }
59
60
    public function isChanged(CacheInterface $cache): bool
61
    {
62
        $timestamps = $this->getTimestamps($cache, $this->tags);
63
        return $timestamps !== $this->data;
64
    }
65
66
    /**
67
     * Invalidates all of the cached values that are associated with any of the specified {@see tags}.
68
     * @param CacheInterface $cache the cache component that caches the values
69
     * @param string|array $tags
70
     */
71
    public static function invalidate(CacheInterface $cache, $tags): void
72
    {
73
        $keys = [];
74
        foreach ((array) $tags as $tag) {
75
            $keys[] = self::buildCacheKey($tag);
76
        }
77
        self::touchKeys($cache, $keys);
78
    }
79
80
    /**
81
     * Generates the timestamp for the specified cache keys.
82
     * @param CacheInterface $cache
83
     * @param string[] $keys
84
     * @return array the timestamp indexed by cache keys
85
     */
86
    private static function touchKeys(CacheInterface $cache, array $keys): array
87
    {
88
        $values = [];
89
        $time = microtime();
90
        foreach ($keys as $key) {
91
            $values[$key] = $time;
92
        }
93
        $cache->setMultiple($values);
94
        return $values;
95
    }
96
97
    /**
98
     * Returns the timestamps for the specified tags.
99
     * @param CacheInterface $cache
100
     * @param string[] $tags
101
     * @return array the timestamps indexed by the specified tags.
102
     * @throws InvalidArgumentException
103
     */
104
    private function getTimestamps(CacheInterface $cache, array $tags): iterable
105
    {
106
        if (empty($tags)) {
107
            return [];
108
        }
109
110
        $keys = [];
111
        foreach ($tags as $tag) {
112
            $keys[] = self::buildCacheKey($tag);
113
        }
114
115
        return $cache->getMultiple($keys);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cache->getMultiple($keys) returns the type iterable which is incompatible with the documented return type array.
Loading history...
116
    }
117
118
    /**
119
     * Builds a normalized cache key from a given tag, making sure it is short enough and safe
120
     * for any particular cache storage.
121
     * @param string $tag tag name.
122
     * @return string cache key.
123
     */
124
    private static function buildCacheKey(string $tag): string
125
    {
126
        return md5(json_encode([__CLASS__, $tag]));
127
    }
128
}
129