Passed
Pull Request — master (#536)
by Def
02:18
created

SchemaCache::addToTag()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 9
ccs 0
cts 0
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Cache;
6
7
use DateInterval;
8
use Psr\SimpleCache\CacheInterface;
9
use Yiisoft\Db\Exception\InvalidArgumentException;
10
use Yiisoft\Db\Exception\InvalidCallException;
11
12
/**
13
 * The SchemaCache class is used to cache database schema information.
14
 *
15
 * The Schema class retrieves information about the database schema from the database server and stores it in the cache
16
 * for faster access. When the Schema class needs to retrieve information about the database schema, it first checks the
17
 * cache using the SchemaCache class. If the information is not in the cache, the Schema class retrieves it from the
18
 * database server and stores it in the cache using the SchemaCache class.
19
 *
20
 * SchemaCache is used by {@see \Yiisoft\Db\Schema\Schema} to cache table metadata.
21 3029
 */
22
final class SchemaCache
23 3029
{
24 3029
    private bool $enabled = true;
25
    private int|null $duration = 3600;
26
    private array $exclude = [];
27
28
    public function __construct(private CacheInterface $psrCache)
29
    {
30
    }
31 1567
32
    /**
33 1567
     * Remove a value with the specified key from cache.
34 1567
     *
35
     * @param mixed $key A key identifying the value to be deleted from cache.
36 1635
     *
37
     * @throws InvalidArgumentException
38 1635
     * @throws \Psr\SimpleCache\InvalidArgumentException
39 1635
     */
40 1635
    public function remove(mixed $key): void
41
    {
42
        $stringKey = $this->normalize($key);
43
        $this->psrCache->delete($stringKey);
44
    }
45
46 1567
    public function getOrSet(
47
        mixed $key,
48 1567
        mixed $value = null,
49 1567
        DateInterval|int|null $ttl = null,
50 1567
        string $cacheTag = null
51
    ): mixed {
52
        $stringKey = $this->normalize($key);
53
        if ($this->psrCache->has($stringKey)) {
54
            return $this->psrCache->get($stringKey);
55
        }
56
57 1622
        $result = $this->psrCache->set(
58
            $stringKey,
59 1622
            $value,
60
            $ttl
61
        );
62
63
        if ($result) {
64
            $this->addToTag($stringKey, $cacheTag);
65
            return $value;
66
        }
67
68
        throw new InvalidCallException('Cache value not setted');
69 1622
    }
70
71 1622
    public function set(
72
        mixed $key,
73
        mixed $value,
74
        DateInterval|int $ttl = null,
75
        string $cacheTag = null
76
    ): void {
77
        $this->remove($key);
78
        $this->getOrSet($key, $value, $ttl, $cacheTag);
79 96
    }
80
81 96
    /**
82 96
     * @return int|null The number of seconds that table metadata can remain valid in cache.
83
     */
84
    public function getDuration(): int|null
85
    {
86
        return $this->duration;
87
    }
88
89 1640
    /**
90
     * @param string $value The table name.
91 1640
     *
92
     * @return bool Whether the table is excluded from caching.
93
     */
94
    public function isExcluded(string $value): bool
95
    {
96
        return in_array($value, $this->exclude, true);
97
    }
98
99
    /**
100
     * Invalidates all the cached values that are associated with any of the specified.
101
     *
102
     * @param string $cacheTag The cache tag used to identify the values to be invalidated.
103 39
     *
104
     * @throws \Psr\SimpleCache\InvalidArgumentException
105 39
     */
106 39
    public function invalidate(string $cacheTag): void
107
    {
108
        /** @var string[] $data */
109
        $data = $this->psrCache->get($cacheTag, []);
110
        foreach ($data as $key) {
111
            $this->psrCache->delete($key);
112
        }
113
    }
114
115
    /**
116
     * Return true if SchemaCache is active.
117
     */
118
    public function isEnabled(): bool
119
    {
120
        return $this->enabled;
121
    }
122
123
    /**
124
     * Whether to enable schema caching. Note that in order to enable truly schema caching, a valid cache component as
125
     * specified must be enabled and must be set true.
126
     *
127
     * @param bool $value Whether to enable schema caching.
128
     *
129
     * {@see setDuration()}
130
     * {@see setExclude()}
131
     */
132
    public function setEnable(bool $value): void
133
    {
134
        $this->enabled = $value;
135
    }
136
137
    /**
138
     * Number of seconds that table metadata can remain valid in cache. Use 'null' to indicate that the cached data will
139
     * never expire.
140
     *
141
     * @param int|null $value The number of seconds that table metadata can remain valid in cache.
142
     *
143
     * {@see setEnable()}
144
     */
145
    public function setDuration(int|null $value): void
146
    {
147
        $this->duration = $value;
148
    }
149
150
    /**
151
     * List of tables whose metadata should NOT be cached. Defaults to empty array. The table names may contain schema
152
     * prefix, if any. Do not quote the table names.
153
     *
154
     * @param array $value The table names.
155
     *
156
     * {@see setEnable()}
157
     */
158
    public function setExclude(array $value): void
159
    {
160
        $this->exclude = $value;
161
    }
162
163
    /**
164
     * Normalizes the cache key from a given key.
165
     *
166
     * If the given key is a string that does not contain characters `{}()/\@:` and no more than 64 characters,
167
     * then the key will be returned back as it is, integers will be converted to strings. Otherwise,
168
     * a normalized key is generated by serializing the given key and applying MD5 hashing.
169
     *
170
     * @see https://www.php-fig.org/psr/psr-16/#12-definitions
171
     *
172
     * @param mixed $key The key to be normalized.
173
     *
174
     * @throws InvalidArgumentException For invalid key.
175
     *
176
     * @return string The normalized cache key.
177
     */
178
    private function normalize(mixed $key): string
179
    {
180
        if (is_string($key) || is_int($key)) {
181
            $key = (string) $key;
182
            $length = mb_strlen($key, '8bit');
183
            return (strpbrk($key, '{}()/\@:') || $length < 1 || $length > 64) ? md5($key) : $key;
184
        }
185
186
        if (($key = json_encode($key)) === false) {
187
            throw new InvalidArgumentException('Invalid key. ' . json_last_error_msg());
188
        }
189
190
        return md5($key);
191
    }
192
193
    private function addToTag(string $key, string $tag = null): void
194
    {
195
        if (empty($tag)) {
196
            return;
197
        }
198
        /** @var string[] $data */
199
        $data = $this->psrCache->get($tag, []);
200
        $data[] = $key;
201
        $this->psrCache->set($tag, $data);
202
    }
203
}
204