Completed
Push — inmemory-content-meta ( b4781d )
by André
20:22
created

AbstractInMemoryHandler::getMultipleCacheValues()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 13
nop 6
dl 0
loc 54
rs 8.0703
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\Core\Persistence\Cache;
8
9
use eZ\Publish\Core\Persistence\Cache\Adapter\InMemoryClearingProxyAdapter;
10
use eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache;
11
12
/**
13
 * Abstract handler for use in other SPI Handlers.
14
 *
15
 * @internal Can be used in external handlers, but be aware this should be regarded as experimental feature.
16
 *           As in, method signatures and behaviour might change in the future.
17
 */
18
abstract class AbstractInMemoryHandler
19
{
20
    use AbstractTrait {
21
        getMultipleCacheValues as getMultipleValuesWithoutInMemoryCache;
22
    }
23
24
    /**
25
     * NOTE: On purpose private as it's only supposed to be interacted with in tandem with symfony cache here,
26
     *       hence the cache decorator and the reusable methods here.
27
     *
28
     * @var \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache
29
     */
30
    private $inMemory;
31
32
    /**
33
     * Setups current handler with everything needed.
34
     *
35
     * @param \eZ\Publish\Core\Persistence\Cache\Adapter\InMemoryClearingProxyAdapter $cache
36
     *     Instance of this must be InMemoryClearingProxyAdapter in order for cache clearing to affect in-memory cache.
37
     * @param \eZ\Publish\Core\Persistence\Cache\PersistenceLogger $logger
38
     * @param \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache $inMemory
39
     */
40
    public function __construct(
41
        InMemoryClearingProxyAdapter $cache,
42
        PersistenceLogger $logger,
43
        InMemoryCache $inMemory
44
    ) {
45
        $this->cache = $cache;
46
        $this->logger = $logger;
47
        $this->inMemory = $inMemory;
48
    }
49
50
    /**
51
     * Load one cache item from cache and loggs the hits / misses.
52
     *
53
     * Load items from in-memory cache, symfony cache pool or backend in that order.
54
     * If not cached the returned objects will be placed in cache.
55
     *
56
     * @param int|string $id
57
     * @param string $keyPrefix E.g "ez-content-"
58
     * @param callable $backendLoader Function for loading missing objects, gets array with missing id's as argument,
59
     *                                expects return value to be array with id as key. Missing items should be missing.
60
     * @param callable $cacheTagger Gets cache object as argument, return array of cache tags.
61
     * @param callable $cacheIndexes Gets cache object as argument, return array of cache keys.
62
     * @param string $keySuffix Optional, e.g "-by-identifier"
63
     *
64
     * @return object
65
     */
66
    final protected function getCacheValue(
67
        $id,
68
        string $keyPrefix,
69
        callable $backendLoader,
70
        callable $cacheTagger,
71
        callable $cacheIndexes,
72
        string $keySuffix = ''
73
    ) {
74
        $key = $keyPrefix . $id . $keySuffix;
75
        // In-memory
76
        if ($object = $this->inMemory->get($key)) {
77
            $this->logger->logCacheHit([$id], 3, true);
78
79
            return $object;
80
        }
81
82
        // Cache pool
83
        $cacheItem = $this->cache->getItem($key);
84 View Code Duplication
        if ($cacheItem->isHit()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
85
            $this->logger->logCacheHit([$id], 3);
86
            $this->inMemory->setMulti([$object = $cacheItem->get()], $cacheIndexes);
87
88
            return $object;
89
        }
90
91
        // Backend
92
        $object = $backendLoader($id);
93
        $this->inMemory->setMulti([$object], $cacheIndexes);
94
        $this->logger->logCacheMiss([$id], 3);
95
        $this->cache->save(
96
            $cacheItem
97
                ->set($object)
98
                ->tag($cacheTagger($object))
99
        );
100
101
        return $object;
102
    }
103
104
    /**
105
     * Load list of objects of some type and loggs the hits / misses.
106
     *
107
     * Load items from in-memory cache, symfony cache pool or backend in that order.
108
     * If not cached the returned objects will be placed in cache.
109
     *
110
     * @param string $key
111
     * @param callable $backendLoader Function for loading ALL objects, value is cached as-is.
112
     * @param callable $cacheTagger Gets cache object as argument, return array of cache tags.
113
     * @param callable $cacheIndexes Gets cache object as argument, return array of cache keys.
114
     * @param callable $listTags Optional, global tags for the list cache.
115
     * @param array $arguments Optional, arguments when parnt method takes arguments that key varies on.
116
     *
117
     * @return array
118
     */
119
    final protected function getListCacheValue(
120
        string $key,
121
        callable $backendLoader,
122
        callable $cacheTagger,
123
        callable $cacheIndexes,
124
        callable $listTags = null,
125
        array $arguments = []
126
    ) {
127
        // In-memory
128
        if ($objects = $this->inMemory->get($key)) {
129
            $this->logger->logCacheHit($arguments, 3, true);
130
131
            return $objects;
132
        }
133
134
        // Cache pool
135
        $cacheItem = $this->cache->getItem($key);
136 View Code Duplication
        if ($cacheItem->isHit()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
137
            $this->logger->logCacheHit($arguments, 3);
138
            $this->inMemory->setMulti($objects = $cacheItem->get(), $cacheIndexes, $key);
139
140
            return $objects;
141
        }
142
143
        // Backend
144
        $objects = $backendLoader();
145
        $this->inMemory->setMulti($objects, $cacheIndexes, $key);
146
        $this->logger->logCacheMiss($arguments, 3);
147
148
        if ($listTags !== null) {
149
            $tagSet = [$listTags()];
150
        } else {
151
            $tagSet = [[]];
152
        }
153
154
        foreach ($objects as $object) {
155
            $tagSet[] = $cacheTagger($object);
156
        }
157
158
        $this->cache->save(
159
            $cacheItem
160
                ->set($objects)
161
                ->tag(array_unique(array_merge(...$tagSet)))
162
        );
163
164
        return $objects;
165
    }
166
167
    /**
168
     * Load several cache items from cache and loggs the hits / misses.
169
     *
170
     * Load items from in-memory cache, symfony cache pool or backend in that order.
171
     * If not cached the returned objects will be placed in cache.
172
     *
173
     * Cache items must be stored with a key in the following format "${keyPrefix}${id}", like "ez-content-info-${id}",
174
     * in order for this method to be able to prefix key on id's and also extract key prefix afterwards.
175
     *
176
     * @param array $ids
177
     * @param string $keyPrefix E.g "ez-content-"
178
     * @param callable $backendLoader Function for loading missing objects, gets array with missing id's as argument,
179
     *                                expects return value to be array with id as key. Missing items should be missing.
180
     * @param callable $cacheTagger Gets cache object as argument, return array of cache tags.
181
     * @param callable $cacheIndexes Gets cache object as argument, return array of cache keys.
182
     * @param string $keySuffix Optional, e.g "-by-identifier"
183
     *
184
     * @return array
185
     */
186
    final protected function getMultipleCacheValues(
187
        array $ids,
188
        string $keyPrefix,
189
        callable $backendLoader,
190
        callable $cacheTagger,
191
        callable $cacheIndexes,
192
        string $keySuffix = ''
193
    ): array {
194
        if (empty($ids)) {
195
            return [];
196
        }
197
198
        // Generate unique cache keys and check if in-memory
199
        $list = [];
200
        $misses = [];
201
        foreach (\array_unique($ids) as $id) {
202
            if ($object = $this->inMemory->get($keyPrefix . $id . $keySuffix)) {
203
                $list[$id] = $object;
204
            } else {
205
                $list[$id] = null;
206
                $misses[] = $id;
207
            }
208
        }
209
210
        // No in-memory misses
211
        if (empty($misses)) {
212
            $this->logger->logCacheHit($ids, 3, true);
213
214
            return $list;
215
        }
216
217
        $loaded = $this->getMultipleValuesWithoutInMemoryCache(
218
            $misses,
219
            $keyPrefix,
220
            $backendLoader,
221
            $cacheTagger,
222
            $keySuffix
223
        );
224
225
        foreach ($misses as $id) {
226
            if (isset($loaded[$id])) {
227
                $list[$id] = $loaded[$id];
228
            } else {
229
                // not found
230
                unset($list[$id]);
231
            }
232
        }
233
234
        // Save cache in-memory
235
        $this->inMemory->setMulti($loaded, $cacheIndexes);
236
        unset($loaded);
237
238
        return $list;
239
    }
240
}
241