Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
18 | abstract class AbstractInMemoryHandler |
||
19 | { |
||
20 | /** |
||
21 | * NOTE: Instance of this must be InMemoryClearingProxyAdapter in order for cache clearing to affect in-memory cache. |
||
22 | * |
||
23 | * @var \eZ\Publish\Core\Persistence\Cache\Adapter\InMemoryClearingProxyAdapter |
||
24 | */ |
||
25 | protected $cache; |
||
26 | |||
27 | /** |
||
28 | * @var \eZ\Publish\Core\Persistence\Cache\PersistenceLogger |
||
29 | */ |
||
30 | protected $logger; |
||
31 | |||
32 | /** |
||
33 | * NOTE: On purpose private as it's only supposed to be interacted with in tandem with symfony cache here, |
||
34 | * hence the cache decorator and the reusable methods here. |
||
35 | * |
||
36 | * @var \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache |
||
37 | */ |
||
38 | private $inMemory; |
||
39 | |||
40 | /** |
||
41 | * Setups current handler with everything needed. |
||
42 | * |
||
43 | * @param \eZ\Publish\Core\Persistence\Cache\Adapter\InMemoryClearingProxyAdapter $cache |
||
44 | * @param \eZ\Publish\Core\Persistence\Cache\PersistenceLogger $logger |
||
45 | * @param \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache $inMemory |
||
46 | */ |
||
47 | public function __construct( |
||
48 | InMemoryClearingProxyAdapter $cache, |
||
49 | PersistenceLogger $logger, |
||
50 | InMemoryCache $inMemory |
||
51 | ) { |
||
52 | $this->cache = $cache; |
||
53 | $this->logger = $logger; |
||
54 | $this->inMemory = $inMemory; |
||
55 | } |
||
56 | |||
57 | /** |
||
58 | * Load one cache item from cache and loggs the hits / misses. |
||
59 | * |
||
60 | * Load items from in-memory cache, symfony cache pool or backend in that order. |
||
61 | * If not cached the returned objects will be placed in cache. |
||
62 | * |
||
63 | * @param int|string $id |
||
64 | * @param string $keyPrefix E.g "ez-content-" |
||
65 | * @param callable $backendLoader Function for loading missing objects, gets array with missing id's as argument, |
||
66 | * expects return value to be array with id as key. Missing items should be missing. |
||
67 | * @param callable $cacheTagger Gets cache object as argument, return array of cache tags. |
||
68 | * @param callable $cacheIndexes Gets cache object as argument, return array of cache keys. |
||
69 | * @param string $keySuffix Optional, e.g "-by-identifier" |
||
70 | * |
||
71 | * @return object |
||
72 | */ |
||
73 | final protected function getCacheValue( |
||
74 | $id, |
||
75 | string $keyPrefix, |
||
76 | callable $backendLoader, |
||
77 | callable $cacheTagger, |
||
78 | callable $cacheIndexes, |
||
79 | string $keySuffix = '', |
||
80 | array $arguments = [] |
||
81 | ) { |
||
82 | $key = $keyPrefix . $id . $keySuffix; |
||
83 | // In-memory |
||
84 | if ($object = $this->inMemory->get($key)) { |
||
85 | $this->logger->logCacheHit($arguments ?: [$id], 3, true); |
||
86 | |||
87 | return $object; |
||
88 | } |
||
89 | |||
90 | // Cache pool |
||
91 | $cacheItem = $this->cache->getItem($key); |
||
92 | View Code Duplication | if ($cacheItem->isHit()) { |
|
|
|||
93 | $this->logger->logCacheHit($arguments ?: [$id], 3); |
||
94 | $this->inMemory->setMulti([$object = $cacheItem->get()], $cacheIndexes); |
||
95 | |||
96 | return $object; |
||
97 | } |
||
98 | |||
99 | // Backend |
||
100 | $object = $backendLoader($id); |
||
101 | $this->inMemory->setMulti([$object], $cacheIndexes); |
||
102 | $this->logger->logCacheMiss($arguments ?: [$id], 3); |
||
103 | $this->cache->save( |
||
104 | $cacheItem |
||
105 | ->set($object) |
||
106 | ->tag($cacheTagger($object)) |
||
107 | ); |
||
108 | |||
109 | return $object; |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Load list of objects of some type and loggs the hits / misses. |
||
114 | * |
||
115 | * Load items from in-memory cache, symfony cache pool or backend in that order. |
||
116 | * If not cached the returned objects will be placed in cache. |
||
117 | * |
||
118 | * @param string $key |
||
119 | * @param callable $backendLoader Function for loading ALL objects, value is cached as-is. |
||
120 | * @param callable $cacheTagger Gets cache object as argument, return array of cache tags. |
||
121 | * @param callable $cacheIndexes Gets cache object as argument, return array of cache keys. |
||
122 | * @param callable $listTags Optional, global tags for the list cache. |
||
123 | * @param array $arguments Optional, arguments when parnt method takes arguments that key varies on. |
||
124 | * |
||
125 | * @return array |
||
126 | */ |
||
127 | final protected function getListCacheValue( |
||
174 | |||
175 | /** |
||
176 | * Load several cache items from cache and loggs the hits / misses. |
||
177 | * |
||
178 | * Load items from in-memory cache, symfony cache pool or backend in that order. |
||
179 | * If not cached the returned objects will be placed in cache. |
||
180 | * |
||
181 | * Cache items must be stored with a key in the following format "${keyPrefix}${id}", like "ez-content-info-${id}", |
||
182 | * in order for this method to be able to prefix key on id's and also extract key prefix afterwards. |
||
183 | * |
||
184 | * @param array $ids |
||
185 | * @param string $keyPrefix E.g "ez-content-" |
||
186 | * @param callable $backendLoader Function for loading missing objects, gets array with missing id's as argument, |
||
187 | * expects return value to be array with id as key. Missing items should be missing. |
||
188 | * @param callable $cacheTagger Gets cache object as argument, return array of cache tags. |
||
189 | * @param callable $cacheIndexes Gets cache object as argument, return array of cache keys. |
||
190 | * @param string $keySuffix Optional, e.g "-by-identifier" |
||
191 | * |
||
192 | * @return array |
||
193 | */ |
||
194 | final protected function getMultipleCacheValues( |
||
273 | |||
274 | /** |
||
275 | * Escape an argument for use in cache keys when needed. |
||
276 | * |
||
277 | * WARNING: Only use the result of this in cache keys, it won't work to use loading the item from backend on miss. |
||
278 | * |
||
279 | * @param string $identifier |
||
280 | * |
||
281 | * @return string |
||
282 | */ |
||
283 | View Code Duplication | final protected function escapeForCacheKey(string $identifier) |
|
291 | } |
||
292 |
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.