Completed
Pull Request — master (#340)
by
unknown
31:40
created

CacheProvider::fetchAtomic()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Doctrine\Common\Cache;
4
5
use function array_combine;
6
use function array_key_exists;
7
use function array_map;
8
use function sprintf;
9
10
/**
11
 * Base class for cache provider implementations.
12
 */
13
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiOperationCache, AtomicFetchCache
14
{
15
    public const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
16
17
    /**
18
     * The namespace to prefix all cache ids with.
19
     *
20
     * @var string
21
     */
22
    private $namespace = '';
23
24
    /**
25
     * The namespace version.
26
     *
27
     * @var int|null
28
     */
29
    private $namespaceVersion;
30
31
    /**
32
     * Sets the namespace to prefix all cache ids with.
33
     *
34
     * @param string $namespace
35
     *
36
     * @return void
37
     */
38 23
    public function setNamespace($namespace)
39
    {
40 23
        $this->namespace        = (string) $namespace;
41 23
        $this->namespaceVersion = null;
42 23
    }
43
44
    /**
45
     * Retrieves the namespace that prefixes all cache ids.
46
     *
47
     * @return string
48
     */
49 1
    public function getNamespace()
50
    {
51 1
        return $this->namespace;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57 688
    public function fetch($id)
58
    {
59 688
        return $this->doFetch($this->getNamespacedId($id));
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65 36
    public function fetchAtomic(string $id, callable $generator, int $ttl = 0)
66
    {
67 36
        return $this->doFetchAtomic($id, $generator, $ttl);
68 11
    }
69
70
    /**
71
     * {@inheritdoc}
72 25
     */
73 25
    public function fetchMultiple(array $keys)
74 25
    {
75
        if (empty($keys)) {
76
            return [];
77
        }
78 25
79 25
        // note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys
80 11
        $namespacedKeys = array_combine($keys, array_map([$this, 'getNamespacedId'], $keys));
81
        $items          = $this->doFetchMultiple($namespacedKeys);
0 ignored issues
show
Bug introduced by
It seems like $namespacedKeys can also be of type false; however, parameter $keys of Doctrine\Common\Cache\Ca...ider::doFetchMultiple() does only seem to accept array, 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

81
        $items          = $this->doFetchMultiple(/** @scrutinizer ignore-type */ $namespacedKeys);
Loading history...
82
        $foundItems     = [];
83 25
84
        // no internal array function supports this sort of mapping: needs to be iterative
85
        // this filters and combines keys in one pass
86 25
        foreach ($namespacedKeys as $requestedKey => $namespacedKey) {
87
            if (! isset($items[$namespacedKey]) && ! array_key_exists($namespacedKey, $items)) {
88
                continue;
89
            }
90
91
            $foundItems[$requestedKey] = $items[$namespacedKey];
92 17
        }
93
94 17
        return $foundItems;
95 17
    }
96 17
97
    /**
98
     * {@inheritdoc}
99 17
     */
100
    public function saveMultiple(array $keysAndValues, $lifetime = 0)
101
    {
102
        $namespacedKeysAndValues = [];
103
        foreach ($keysAndValues as $key => $value) {
104
            $namespacedKeysAndValues[$this->getNamespacedId($key)] = $value;
105 771
        }
106
107 771
        return $this->doSaveMultiple($namespacedKeysAndValues, $lifetime);
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113 808
    public function contains($id)
114
    {
115 808
        return $this->doContains($this->getNamespacedId($id));
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121 13
    public function save($id, $data, $lifeTime = 0)
122
    {
123 13
        return $this->doSave($this->getNamespacedId($id), $data, $lifeTime);
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129 480
    public function deleteMultiple(array $keys)
130
    {
131 480
        return $this->doDeleteMultiple(array_map([$this, 'getNamespacedId'], $keys));
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137 13
    public function delete($id)
138
    {
139 13
        return $this->doDelete($this->getNamespacedId($id));
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 24
    public function getStats()
146
    {
147 24
        return $this->doGetStats();
148
    }
149
150
    /**
151
     * {@inheritDoc}
152
     */
153 71
    public function flushAll()
154
    {
155 71
        return $this->doFlush();
156 71
    }
157
158 71
    /**
159 70
     * {@inheritDoc}
160
     */
161 70
    public function deleteAll()
162
    {
163
        $namespaceCacheKey = $this->getNamespaceCacheKey();
164 1
        $namespaceVersion  = $this->getNamespaceVersion() + 1;
165
166
        if ($this->doSave($namespaceCacheKey, $namespaceVersion)) {
167
            $this->namespaceVersion = $namespaceVersion;
168
169
            return true;
170
        }
171
172
        return false;
173
    }
174 853
175
    /**
176 853
     * Prefixes the passed id with the configured namespace value.
177
     *
178 853
     * @param string $id The id to namespace.
179
     *
180
     * @return string The namespaced id.
181
     */
182
    private function getNamespacedId(string $id) : string
183
    {
184 854
        $namespaceVersion = $this->getNamespaceVersion();
185
186 854
        return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion);
187
    }
188
189
    /**
190
     * Returns the namespace cache key.
191
     */
192 854
    private function getNamespaceCacheKey() : string
193
    {
194 854
        return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace);
195 836
    }
196
197
    /**
198 854
     * Returns the namespace version.
199 854
     */
200
    private function getNamespaceVersion() : int
201 854
    {
202
        if ($this->namespaceVersion !== null) {
203
            return $this->namespaceVersion;
204
        }
205
206
        $namespaceCacheKey      = $this->getNamespaceCacheKey();
207
        $this->namespaceVersion = (int) $this->doFetch($namespaceCacheKey) ?: 1;
208
209
        return $this->namespaceVersion;
210
    }
211 16
212
    /**
213 16
     * Note: this implementation is NOT atomic! Inheriting classes should provide an atomic implementation.
214
     *
215 16
     * @param string $id
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
216 16
     * @param callable $generator
217 16
     * @param int $ttl
0 ignored issues
show
Coding Style introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
218 8
     *
219
     * @return false|mixed
220
     */
221 16
    protected function doFetchAtomic(string $id, callable $generator, int $ttl)
222
    {
223
        if ($this->contains($id)) {
224 16
            return $this->fetch($id);
225
        }
226
227
        $data = $generator($id);
228
        $this->save($id, $data, $ttl);
229
230
        return $data;
231
    }
232
233
    /**
234
     * Default implementation of doFetchMultiple. Each driver that supports multi-get should owerwrite it.
235
     *
236
     * @param array $keys Array of keys to retrieve from cache
237
     *
238
     * @return array Array of values retrieved for the given keys.
239
     */
240
    protected function doFetchMultiple(array $keys)
241
    {
242
        $returnValues = [];
243
244
        foreach ($keys as $key) {
245
            $item = $this->doFetch($key);
246
            if ($item === false && ! $this->doContains($key)) {
247
                continue;
248
            }
249
250
            $returnValues[$key] = $item;
251
        }
252
253
        return $returnValues;
254 10
    }
255
256 10
    /**
257
     * Fetches an entry from the cache.
258 10
     *
259 10
     * @param string $id The id of the cache entry to fetch.
260 10
     *
261
     * @return mixed|false The cached data or FALSE, if no cache entry exists for the given id.
262
     */
263 2
    abstract protected function doFetch($id);
264
265
    /**
266 10
     * Tests if an entry exists in the cache.
267
     *
268
     * @param string $id The cache id of the entry to check for.
269
     *
270
     * @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise.
271
     */
272
    abstract protected function doContains($id);
273
274
    /**
275
     * Default implementation of doSaveMultiple. Each driver that supports multi-put should override it.
276
     *
277
     * @param array $keysAndValues Array of keys and values to save in cache
278
     * @param int   $lifetime      The lifetime. If != 0, sets a specific lifetime for these
279
     *                             cache entries (0 => infinite lifeTime).
280
     *
281
     * @return bool TRUE if the operation was successful, FALSE if it wasn't.
282
     */
283
    protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
284
    {
285
        $success = true;
286
287
        foreach ($keysAndValues as $key => $value) {
288 8
            if ($this->doSave($key, $value, $lifetime)) {
289
                continue;
290 8
            }
291
292 8
            $success = false;
293 8
        }
294 8
295
        return $success;
296
    }
297 1
298
    /**
299
     * Puts data into the cache.
300 8
     *
301
     * @param string $id       The cache id.
302
     * @param string $data     The cache entry/data.
303
     * @param int    $lifeTime The lifetime. If != 0, sets a specific lifetime for this
304
     *                           cache entry (0 => infinite lifeTime).
305
     *
306
     * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
307
     */
308
    abstract protected function doSave($id, $data, $lifeTime = 0);
309
310
    /**
311
     * Default implementation of doDeleteMultiple. Each driver that supports multi-delete should override it.
312
     *
313
     * @param array $keys Array of keys to delete from cache
314
     *
315
     * @return bool TRUE if the operation was successful, FALSE if it wasn't
316
     */
317
    protected function doDeleteMultiple(array $keys)
318
    {
319
        $success = true;
320
321
        foreach ($keys as $key) {
322
            if ($this->doDelete($key)) {
323
                continue;
324
            }
325
326
            $success = false;
327
        }
328
329
        return $success;
330
    }
331
332
    /**
333
     * Deletes a cache entry.
334
     *
335
     * @param string $id The cache id.
336
     *
337
     * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise.
338
     */
339
    abstract protected function doDelete($id);
340
341
    /**
342
     * Flushes all cache entries.
343
     *
344
     * @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise.
345
     */
346
    abstract protected function doFlush();
347
348
    /**
349
     * Retrieves cached information from the data store.
350
     *
351
     * @return array|null An associative array with server's statistics if available, NULL otherwise.
352
     */
353
    abstract protected function doGetStats();
354
}
355