1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* TechDivision\Import\Cache\GenericCacheAdapter |
5
|
|
|
* |
6
|
|
|
* NOTICE OF LICENSE |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the Open Software License (OSL 3.0) |
9
|
|
|
* that is available through the world-wide-web at this URL: |
10
|
|
|
* http://opensource.org/licenses/osl-3.0.php |
11
|
|
|
* |
12
|
|
|
* PHP version 5 |
13
|
|
|
* |
14
|
|
|
* @author Tim Wagner <[email protected]> |
15
|
|
|
* @copyright 2019 TechDivision GmbH <[email protected]> |
16
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
17
|
|
|
* @link https://github.com/techdivision/import |
18
|
|
|
* @link http://www.techdivision.com |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace TechDivision\Import\Cache; |
22
|
|
|
|
23
|
|
|
use Cache\TagInterop\TaggableCacheItemInterface; |
24
|
|
|
use Psr\Cache\CacheItemPoolInterface; |
25
|
|
|
use Symfony\Contracts\Cache\ItemInterface; |
26
|
|
|
use TechDivision\Import\Utils\CacheKeys; |
27
|
|
|
use TechDivision\Import\Utils\CacheKeyUtilInterface; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Generic cache adapter that wrappes any PSR-6 compatible cache implementation and can be |
31
|
|
|
* used in a distributed environment. |
32
|
|
|
* |
33
|
|
|
* If you're searching for a maximum performance consider using the LocalCacheAdapter |
34
|
|
|
* implementation. |
35
|
|
|
* |
36
|
|
|
* ATTENTION: Please be aware, that this cache adapter is NOT multiprocess or -threadsafe! |
37
|
|
|
* |
38
|
|
|
* @author Tim Wagner <[email protected]> |
39
|
|
|
* @copyright 2019 TechDivision GmbH <[email protected]> |
40
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
41
|
|
|
* @link https://github.com/techdivision/import |
42
|
|
|
* @link http://www.techdivision.com |
43
|
|
|
* @see \TechDivision\Import\Cache\LocalCacheAdapter |
44
|
|
|
*/ |
45
|
|
|
class GenericCacheAdapter implements CacheAdapterInterface |
46
|
|
|
{ |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Trait that provides custom cache adapter functionality. |
50
|
|
|
* |
51
|
|
|
* @var TechDivision\Import\Cache\CacheAdapterTrait |
52
|
|
|
*/ |
53
|
|
|
use CacheAdapterTrait; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* The cache for the query results. |
57
|
|
|
* |
58
|
|
|
* @var \Psr\Cache\CacheItemPoolInterface |
59
|
|
|
*/ |
60
|
|
|
protected $cache; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* The cache key utility instance. |
64
|
|
|
* |
65
|
|
|
* @var \TechDivision\Import\Utils\CacheKeyUtilInterface |
66
|
|
|
*/ |
67
|
|
|
protected $cacheKeyUtil; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Initialize the cache handler with the passed cache and configuration instances. |
71
|
|
|
* . |
72
|
|
|
* @param \Psr\Cache\CacheItemPoolInterface $cache The cache instance |
73
|
|
|
* @param \TechDivision\Import\Utils\CacheKeyUtilInterface $cacheKeyUtil The cache key utility instance |
74
|
|
|
*/ |
75
|
|
|
public function __construct( |
76
|
|
|
CacheItemPoolInterface $cache, |
77
|
|
|
CacheKeyUtilInterface $cacheKeyUtil |
78
|
|
|
) { |
79
|
|
|
|
80
|
|
|
// set the cache and the cache key utility instance |
81
|
|
|
$this->cache = $cache; |
82
|
|
|
$this->cacheKeyUtil = $cacheKeyUtil; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Resolve's the cache key. |
87
|
|
|
* |
88
|
|
|
* @param string $from The cache key to resolve |
89
|
|
|
* |
90
|
|
|
* @return string The resolved reference |
91
|
|
|
*/ |
92
|
|
|
protected function resolveReference($from) |
93
|
|
|
{ |
94
|
|
|
|
95
|
|
|
// try to load the load the references |
96
|
|
|
if ($this->cache->hasItem(CacheKeys::REFERENCES)) { |
97
|
|
|
// load the array with references from the cache |
98
|
|
|
$references = $this->cache->getItem(CacheKeys::REFERENCES)->get(); |
99
|
|
|
|
100
|
|
|
// query whether a reference is available |
101
|
|
|
if (isset($references[$from])) { |
102
|
|
|
return $references[$from]; |
103
|
|
|
} |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
// return the passed reference |
107
|
|
|
return $from; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Creates a unique cache key from the passed data. |
112
|
|
|
* |
113
|
|
|
* @param mixed $data The date to create the cache key from |
114
|
|
|
* @param boolean $usePrefix Flag to signal using the prefix or not |
115
|
|
|
* |
116
|
|
|
* @return string The generated cache key |
117
|
|
|
*/ |
118
|
|
|
public function cacheKey($data, $usePrefix = true) |
119
|
|
|
{ |
120
|
|
|
return $this->cacheKeyUtil->cacheKey($data, $usePrefix); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Query whether or not a cache value for the passed cache key is available. |
125
|
|
|
* |
126
|
|
|
* @param string $key The cache key to query for |
127
|
|
|
* |
128
|
|
|
* @return boolean TRUE if the a value is available, else FALSE |
129
|
|
|
*/ |
130
|
|
|
public function isCached($key) |
131
|
|
|
{ |
132
|
|
|
|
133
|
|
|
// query whether or not the item has been cached, and if yes if the cache is valid |
134
|
|
|
if ($this->cache->hasItem($resolvedKey = $this->resolveReference($this->cacheKey($key)))) { |
135
|
|
|
return $this->cache->getItem($resolvedKey)->isHit(); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
// return FALSE in all other cases |
139
|
|
|
return false; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Inversion of the isCached() method. |
144
|
|
|
* |
145
|
|
|
* @param string $key The cache key to query for |
146
|
|
|
* |
147
|
|
|
* @return boolean TRUE if the value is not available, else FALSE |
148
|
|
|
*/ |
149
|
|
|
public function notCached($key) |
150
|
|
|
{ |
151
|
|
|
return !$this->isCached($key); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Add's a cache reference from one key to another. |
156
|
|
|
* |
157
|
|
|
* @param string $from The key to reference from |
158
|
|
|
* @param string $to The key to reference to |
159
|
|
|
* |
160
|
|
|
* @return void |
161
|
|
|
*/ |
162
|
|
|
public function addReference($from, $to) |
163
|
|
|
{ |
164
|
|
|
|
165
|
|
|
// initialize the array with references |
166
|
|
|
$references = array(); |
167
|
|
|
|
168
|
|
|
// try to load the references from the cache |
169
|
|
|
if ($this->isCached(CacheKeys::REFERENCES)) { |
170
|
|
|
$references = $this->fromCache(CacheKeys::REFERENCES); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
// add the reference to the array |
174
|
|
|
$references[$this->cacheKey($from)] = $this->cacheKey($to); |
175
|
|
|
|
176
|
|
|
// add the references back to the cache |
177
|
|
|
$this->toCache(CacheKeys::REFERENCES, $references); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Add the passed item to the cache. |
182
|
|
|
* |
183
|
|
|
* @param string $key The cache key to use |
184
|
|
|
* @param mixed $value The value that has to be cached |
185
|
|
|
* @param array $references An array with references to add |
186
|
|
|
* @param array $tags An array with tags to add |
187
|
|
|
* @param boolean $override Flag that allows to override an exising cache entry |
188
|
|
|
* @param integer $time The TTL in seconds for the passed item |
189
|
|
|
* |
190
|
|
|
* @return void |
191
|
|
|
*/ |
192
|
|
|
public function toCache($key, $value, array $references = array(), array $tags = array(), $override = true, $time = null) |
193
|
|
|
{ |
194
|
|
|
|
195
|
|
|
// create the unique cache key |
196
|
|
|
$uniqueKey = $this->cacheKey($key); |
197
|
|
|
|
198
|
|
|
// query whether or not the key has already been used |
199
|
|
|
if ($this->isCached($uniqueKey) && $override === false) { |
200
|
|
|
throw new \Exception( |
201
|
|
|
sprintf( |
202
|
|
|
'Try to override data with key "%s"', |
203
|
|
|
$uniqueKey |
204
|
|
|
) |
205
|
|
|
); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
// prepend the tags with the cache key |
209
|
|
|
array_walk($tags, function (&$tag) { |
210
|
|
|
$tag = $this->cacheKey($tag); |
211
|
|
|
}); |
212
|
|
|
|
213
|
|
|
// initialize the cache item |
214
|
|
|
$cacheItem = $this->cache->getItem($uniqueKey); |
215
|
|
|
$cacheItem->set($value)->expiresAfter($time); |
216
|
|
|
|
217
|
|
|
if ($cacheItem instanceof TaggableCacheItemInterface) { |
218
|
|
|
$cacheItem->setTags($tags); |
219
|
|
|
} elseif ($cacheItem instanceof ItemInterface) { |
|
|
|
|
220
|
|
|
$cacheItem->tag($tags); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
// set the attribute in the registry |
224
|
|
|
$this->cache->save($cacheItem); |
225
|
|
|
|
226
|
|
|
// also register the references if given |
227
|
|
|
foreach ($references as $from => $to) { |
228
|
|
|
$this->addReference($from, $to); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Returns a new cache item for the passed key |
234
|
|
|
* |
235
|
|
|
* @param string $key The cache key to return the item for |
236
|
|
|
* |
237
|
|
|
* @return mixed The value for the passed key |
238
|
|
|
*/ |
239
|
|
|
public function fromCache($key) |
240
|
|
|
{ |
241
|
|
|
return $this->cache->getItem($this->resolveReference($this->cacheKey($key)))->get(); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* Flush the cache and remove the references. |
246
|
|
|
* |
247
|
|
|
* @return void |
248
|
|
|
*/ |
249
|
|
|
public function flushCache() |
250
|
|
|
{ |
251
|
|
|
$this->cache->clear(); |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* Invalidate the cache entries for the passed tags. |
256
|
|
|
* |
257
|
|
|
* @param array $tags The tags to invalidate the cache for |
258
|
|
|
* |
259
|
|
|
* @return void |
260
|
|
|
*/ |
261
|
|
|
public function invalidateTags(array $tags) |
262
|
|
|
{ |
263
|
|
|
|
264
|
|
|
// prepend the tags with the cache key |
265
|
|
|
array_walk($tags, function (&$tag) { |
266
|
|
|
$tag = $this->cacheKey($tag); |
267
|
|
|
}); |
268
|
|
|
|
269
|
|
|
// query whether or not references are available |
270
|
|
|
if ($this->isCached(CacheKeys::REFERENCES)) { |
271
|
|
|
// load the array with references from the cache |
272
|
|
|
$references = $this->fromCache(CacheKeys::REFERENCES); |
273
|
|
|
|
274
|
|
|
// remove all the references of items that has one of the passed tags |
275
|
|
|
foreach ($tags as $tag) { |
276
|
|
|
foreach ($references as $from => $to) { |
277
|
|
|
// load the cache item for the referenced key |
278
|
|
|
$cacheItem = $this->cache->getItem($to); |
279
|
|
|
// query whether or not the cache item has the tag, if yes remove the reference |
280
|
|
|
if (in_array($tag, $cacheItem->getPreviousTags())) { |
281
|
|
|
unset($references[$from]); |
282
|
|
|
} |
283
|
|
|
} |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
// query whether or not the references exists |
287
|
|
|
if (sizeof($references) > 0) { |
288
|
|
|
// set the array with references to the cache |
289
|
|
|
$this->toCache(CacheKeys::REFERENCES, $references); |
290
|
|
|
} else { |
291
|
|
|
$this->removeCache(CacheKeys::REFERENCES); |
292
|
|
|
} |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
// finally, invalidate the items with the passed tags |
296
|
|
|
$this->cache->invalidateTags($tags); |
|
|
|
|
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Remove the item with the passed key and all its references from the cache. |
301
|
|
|
* |
302
|
|
|
* @param string $key The key of the cache item to Remove |
303
|
|
|
* |
304
|
|
|
* @return void |
305
|
|
|
*/ |
306
|
|
|
public function removeCache($key) |
307
|
|
|
{ |
308
|
|
|
|
309
|
|
|
// delete the item with the passed key |
310
|
|
|
$this->cache->deleteItem($this->resolveReference($uniqueKey = $this->cacheKey($key))); |
311
|
|
|
|
312
|
|
|
// query whether or not references are available |
313
|
|
|
if ($this->isCached(CacheKeys::REFERENCES)) { |
314
|
|
|
// load the array with references from the cache |
315
|
|
|
$references = $this->fromCache(CacheKeys::REFERENCES); |
316
|
|
|
// query whether or not the references exists |
317
|
|
|
if (isset($references[$uniqueKey])) { |
318
|
|
|
// remove the reference |
319
|
|
|
unset($references[$uniqueKey]); |
320
|
|
|
// set the array with references to the cache |
321
|
|
|
$this->toCache(CacheKeys::REFERENCES, $references); |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
} |
325
|
|
|
} |
326
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.