Completed
Push — master ( 28797f...652832 )
by Alexander
01:44
created

TagDependency   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 106
Duplicated Lines 0 %

Importance

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

7 Methods

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