1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @link http://www.yiiframework.com/ |
4
|
|
|
* @copyright Copyright (c) 2008 Yii Software LLC |
5
|
|
|
* @license http://www.yiiframework.com/license/ |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace yii\caching; |
9
|
|
|
|
10
|
|
|
use Yii; |
11
|
|
|
use yii\base\Component; |
12
|
|
|
use yii\di\Instance; |
13
|
|
|
use yii\helpers\StringHelper; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Cache provides support for the data caching, including cache key composition and dependencies. |
17
|
|
|
* The actual data caching is performed via [[handler]], which should be configured to be [[\Psr\SimpleCache\CacheInterface]] |
18
|
|
|
* instance. |
19
|
|
|
* |
20
|
|
|
* Application configuration example: |
21
|
|
|
* |
22
|
|
|
* ```php |
23
|
|
|
* return [ |
24
|
|
|
* 'components' => [ |
25
|
|
|
* 'cache' => [ |
26
|
|
|
* 'class' => yii\caching\Cache::class, |
27
|
|
|
* 'handler' => [ |
28
|
|
|
* 'class' => yii\caching\FileCache::class, |
29
|
|
|
* 'cachePath' => '@runtime/cache', |
30
|
|
|
* ], |
31
|
|
|
* ], |
32
|
|
|
* // ... |
33
|
|
|
* ], |
34
|
|
|
* // ... |
35
|
|
|
* ]; |
36
|
|
|
* ``` |
37
|
|
|
* |
38
|
|
|
* A data item can be stored in the cache by calling [[set()]] and be retrieved back |
39
|
|
|
* later (in the same or different request) by [[get()]]. In both operations, |
40
|
|
|
* a key identifying the data item is required. An expiration time and/or a [[Dependency|dependency]] |
41
|
|
|
* can also be specified when calling [[set()]]. If the data item expires or the dependency |
42
|
|
|
* changes at the time of calling [[get()]], the cache will return no data. |
43
|
|
|
* |
44
|
|
|
* A typical usage pattern of cache is like the following: |
45
|
|
|
* |
46
|
|
|
* ```php |
47
|
|
|
* $key = 'demo'; |
48
|
|
|
* $data = $cache->get($key); |
49
|
|
|
* if ($data === null) { |
50
|
|
|
* // ...generate $data here... |
51
|
|
|
* $cache->set($key, $data, $duration, $dependency); |
52
|
|
|
* } |
53
|
|
|
* ``` |
54
|
|
|
* |
55
|
|
|
* Because Cache implements the [[\ArrayAccess]] interface, it can be used like an array. For example, |
56
|
|
|
* |
57
|
|
|
* ```php |
58
|
|
|
* $cache['foo'] = 'some data'; |
59
|
|
|
* echo $cache['foo']; |
60
|
|
|
* ``` |
61
|
|
|
* |
62
|
|
|
* For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview) |
63
|
|
|
* and [PSR-16 specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md). |
64
|
|
|
* |
65
|
|
|
* @author Qiang Xue <[email protected]> |
66
|
|
|
* @since 2.0 |
67
|
|
|
*/ |
68
|
|
|
class Cache extends Component implements CacheInterface |
69
|
|
|
{ |
70
|
|
|
/** |
71
|
|
|
* @var \Psr\SimpleCache\CacheInterface|array|\Closure|string actual cache handler or its DI compatible configuration. |
72
|
|
|
* After the Cache object is created, if you want to change this property, you should only assign it |
73
|
|
|
* with a [[\Psr\SimpleCache\CacheInterface]] instance. |
74
|
|
|
* @since 2.1.0 |
75
|
|
|
*/ |
76
|
|
|
public $handler; |
77
|
|
|
|
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* {@inheritdoc} |
81
|
|
|
*/ |
82
|
199 |
|
public function init() |
83
|
|
|
{ |
84
|
199 |
|
parent::init(); |
85
|
199 |
|
$this->handler = Instance::ensure($this->handler instanceof \Closure ? call_user_func($this->handler) : $this->handler, \Psr\SimpleCache\CacheInterface::class); |
86
|
199 |
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Builds a normalized cache key from a given key. |
90
|
|
|
* |
91
|
|
|
* If the given key is a string containing alphanumeric characters only and no more than 32 characters, |
92
|
|
|
* then the key will be returned back as it is. Otherwise, a normalized key is generated by serializing |
93
|
|
|
* the given key and applying MD5 hashing. |
94
|
|
|
* |
95
|
|
|
* @param mixed $key the key to be normalized |
96
|
|
|
* @return string the generated cache key |
97
|
|
|
*/ |
98
|
198 |
|
protected function buildKey($key) |
99
|
|
|
{ |
100
|
198 |
|
if (is_string($key)) { |
101
|
168 |
|
return ctype_alnum($key) && StringHelper::byteLength($key) <= 32 ? $key : md5($key); |
102
|
|
|
} |
103
|
30 |
|
return md5(json_encode($key)); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* {@inheritdoc} |
108
|
|
|
*/ |
109
|
105 |
|
public function get($key, $default = null) |
110
|
|
|
{ |
111
|
105 |
|
$key = $this->buildKey($key); |
112
|
105 |
|
$value = $this->handler->get($key); |
|
|
|
|
113
|
|
|
|
114
|
105 |
|
if ($value === null) { |
115
|
74 |
|
return $default; |
116
|
|
|
} |
117
|
|
|
|
118
|
86 |
|
if (is_array($value) && isset($value[1]) && $value[1] instanceof Dependency) { |
119
|
7 |
|
if ($value[1]->isChanged($this)) { |
120
|
7 |
|
return $default; |
121
|
|
|
} |
122
|
7 |
|
return $value[0]; |
123
|
|
|
} |
124
|
|
|
|
125
|
79 |
|
return $value; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* {@inheritdoc} |
130
|
|
|
*/ |
131
|
8 |
|
public function has($key) |
132
|
|
|
{ |
133
|
8 |
|
$key = $this->buildKey($key); |
134
|
8 |
|
return $this->handler->has($key); |
|
|
|
|
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Retrieves multiple values from cache with the specified keys. |
139
|
|
|
* Some caches (such as memcache, apc) allow retrieving multiple cached values at the same time, |
140
|
|
|
* which may improve the performance. In case a cache does not support this feature natively, |
141
|
|
|
* this method will try to simulate it. |
142
|
|
|
* @param string[] $keys list of string keys identifying the cached values |
143
|
|
|
* @param mixed $default Default value to return for keys that do not exist. |
144
|
|
|
* @return array list of cached values corresponding to the specified keys. The array |
145
|
|
|
* is returned in terms of (key, value) pairs. |
146
|
|
|
* If a value is not cached or expired, the corresponding array value will be false. |
147
|
|
|
* @since 2.0.7 |
148
|
|
|
*/ |
149
|
11 |
|
public function getMultiple($keys, $default = null) |
150
|
|
|
{ |
151
|
11 |
|
$keyMap = []; |
152
|
11 |
|
foreach ($keys as $key) { |
153
|
11 |
|
$keyMap[$key] = $this->buildKey($key); |
154
|
|
|
} |
155
|
11 |
|
$values = $this->handler->getMultiple(array_values($keyMap)); |
|
|
|
|
156
|
11 |
|
$results = []; |
157
|
11 |
|
foreach ($keyMap as $key => $newKey) { |
158
|
11 |
|
$results[$key] = $default; |
159
|
11 |
|
if (isset($values[$newKey])) { |
160
|
11 |
|
$value = $values[$newKey]; |
161
|
11 |
|
if (is_array($value) && isset($value[1]) && $value[1] instanceof Dependency) { |
162
|
|
|
if ($value[1]->isChanged($this)) { |
163
|
|
|
continue; |
164
|
|
|
} else { |
165
|
|
|
$value = $value[0]; |
166
|
|
|
} |
167
|
|
|
} |
168
|
11 |
|
$results[$key] = $value; |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
|
172
|
11 |
|
return $results; |
|
|
|
|
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Stores a value identified by a key into cache. |
177
|
|
|
* If the cache already contains such a key, the existing value and |
178
|
|
|
* expiration time will be replaced with the new ones, respectively. |
179
|
|
|
* |
180
|
|
|
* @param mixed $key a key identifying the value to be cached. This can be a simple string or |
181
|
|
|
* a complex data structure consisting of factors representing the key. |
182
|
|
|
* @param mixed $value the value to be cached |
183
|
|
|
* @param null|int|\DateInterval $ttl the TTL value of this item. If not set, default value is used. |
184
|
|
|
* @param Dependency $dependency dependency of the cached item. If the dependency changes, |
185
|
|
|
* the corresponding value in the cache will be invalidated when it is fetched via [[get()]]. |
186
|
|
|
* This parameter is ignored if [[serializer]] is false. |
187
|
|
|
* @return bool whether the value is successfully stored into cache |
188
|
|
|
*/ |
189
|
96 |
|
public function set($key, $value, $ttl = null, $dependency = null) |
190
|
|
|
{ |
191
|
96 |
|
if ($dependency !== null) { |
192
|
7 |
|
$dependency->evaluateDependency($this); |
193
|
7 |
|
$value = [$value, $dependency]; |
194
|
|
|
} |
195
|
96 |
|
$key = $this->buildKey($key); |
196
|
96 |
|
return $this->handler->set($key, $value, $ttl); |
|
|
|
|
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Stores multiple items in cache. Each item contains a value identified by a key. |
201
|
|
|
* If the cache already contains such a key, the existing value and |
202
|
|
|
* expiration time will be replaced with the new ones, respectively. |
203
|
|
|
* |
204
|
|
|
* @param array $items the items to be cached, as key-value pairs. |
205
|
|
|
* @param null|int|\DateInterval $ttl the TTL value of this item. If not set, default value is used. |
206
|
|
|
* @param Dependency $dependency dependency of the cached items. If the dependency changes, |
207
|
|
|
* the corresponding values in the cache will be invalidated when it is fetched via [[get()]]. |
208
|
|
|
* This parameter is ignored if [[serializer]] is false. |
209
|
|
|
* @return array array of failed keys |
210
|
|
|
* @since 2.0.7 |
211
|
|
|
*/ |
212
|
16 |
|
public function setMultiple($items, $ttl = 0, $dependency = null) |
213
|
|
|
{ |
214
|
16 |
|
if ($dependency !== null) { |
215
|
|
|
$dependency->evaluateDependency($this); |
216
|
|
|
} |
217
|
|
|
|
218
|
16 |
|
$data = []; |
219
|
16 |
|
foreach ($items as $key => $value) { |
220
|
16 |
|
if ($dependency !== null) { |
221
|
|
|
$value = [$value, $dependency]; |
222
|
|
|
} |
223
|
16 |
|
$key = $this->buildKey($key); |
224
|
16 |
|
$data[$key] = $value; |
225
|
|
|
} |
226
|
|
|
|
227
|
16 |
|
return $this->handler->setMultiple($data, $ttl); |
|
|
|
|
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* {@inheritdoc} |
232
|
|
|
* @since 2.1 |
233
|
|
|
*/ |
234
|
|
|
public function deleteMultiple($keys) |
235
|
|
|
{ |
236
|
|
|
$actualKeys = []; |
237
|
|
|
foreach ($keys as $key) { |
238
|
|
|
$actualKeys[] = $this->buildKey($key); |
239
|
|
|
} |
240
|
|
|
return $this->handler->deleteMultiple($actualKeys); |
|
|
|
|
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Stores multiple items in cache. Each item contains a value identified by a key. |
245
|
|
|
* If the cache already contains such a key, the existing value and expiration time will be preserved. |
246
|
|
|
* |
247
|
|
|
* @param array $values the items to be cached, as key-value pairs. |
248
|
|
|
* @param null|int|\DateInterval $ttl the TTL value of this item. If not set, default value is used. |
249
|
|
|
* @param Dependency $dependency dependency of the cached items. If the dependency changes, |
250
|
|
|
* the corresponding values in the cache will be invalidated when it is fetched via [[get()]]. |
251
|
|
|
* This parameter is ignored if [[serializer]] is false. |
252
|
|
|
* @return array array of failed keys |
253
|
|
|
* @since 2.0.7 |
254
|
|
|
*/ |
255
|
5 |
|
public function addMultiple($values, $ttl = 0, $dependency = null) |
256
|
|
|
{ |
257
|
5 |
|
if ($dependency !== null) { |
258
|
|
|
$dependency->evaluateDependency($this); |
259
|
|
|
} |
260
|
|
|
|
261
|
5 |
|
$data = []; |
262
|
5 |
|
foreach ($values as $key => $value) { |
263
|
5 |
|
if ($dependency !== null) { |
264
|
|
|
$value = [$value, $dependency]; |
265
|
|
|
} |
266
|
|
|
|
267
|
5 |
|
$key = $this->buildKey($key); |
268
|
5 |
|
$data[$key] = $value; |
269
|
|
|
} |
270
|
|
|
|
271
|
5 |
|
$existingValues = $this->handler->getMultiple(array_keys($data)); |
|
|
|
|
272
|
5 |
|
foreach ($existingValues as $key => $value) { |
273
|
5 |
|
if ($value !== null) { |
274
|
5 |
|
unset($data[$key]); |
275
|
|
|
} |
276
|
|
|
} |
277
|
5 |
|
return $this->handler->setMultiple($data, $ttl); |
|
|
|
|
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Stores a value identified by a key into cache if the cache does not contain this key. |
282
|
|
|
* Nothing will be done if the cache already contains the key. |
283
|
|
|
* @param mixed $key a key identifying the value to be cached. This can be a simple string or |
284
|
|
|
* a complex data structure consisting of factors representing the key. |
285
|
|
|
* @param mixed $value the value to be cached |
286
|
|
|
* @param null|int|\DateInterval $ttl the TTL value of this item. If not set, default value is used. |
287
|
|
|
* @param Dependency $dependency dependency of the cached item. If the dependency changes, |
288
|
|
|
* the corresponding value in the cache will be invalidated when it is fetched via [[get()]]. |
289
|
|
|
* This parameter is ignored if [[serializer]] is false. |
290
|
|
|
* @return bool whether the value is successfully stored into cache |
291
|
|
|
*/ |
292
|
8 |
|
public function add($key, $value, $ttl = null, $dependency = null) |
293
|
|
|
{ |
294
|
8 |
|
if ($dependency !== null) { |
295
|
|
|
$dependency->evaluateDependency($this); |
296
|
|
|
$value = [$value, $dependency]; |
297
|
|
|
} |
298
|
|
|
|
299
|
8 |
|
$key = $this->buildKey($key); |
300
|
|
|
|
301
|
8 |
|
if ($this->handler->has($key)) { |
|
|
|
|
302
|
5 |
|
return false; |
303
|
|
|
} |
304
|
|
|
|
305
|
8 |
|
return $this->handler->set($key, $value, $ttl); |
|
|
|
|
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Deletes a value with the specified key from cache |
310
|
|
|
* @param mixed $key a key identifying the value to be deleted from cache. This can be a simple string or |
311
|
|
|
* a complex data structure consisting of factors representing the key. |
312
|
|
|
* @return bool if no error happens during deletion |
313
|
|
|
*/ |
314
|
93 |
|
public function delete($key) |
315
|
|
|
{ |
316
|
93 |
|
$key = $this->buildKey($key); |
317
|
|
|
|
318
|
93 |
|
return $this->handler->delete($key); |
|
|
|
|
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* Deletes all values from cache. |
323
|
|
|
* Be careful of performing this operation if the cache is shared among multiple applications. |
324
|
|
|
* @return bool whether the flush operation was successful. |
325
|
|
|
*/ |
326
|
55 |
|
public function clear() |
327
|
|
|
{ |
328
|
55 |
|
return $this->handler->clear(); |
|
|
|
|
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* Returns whether there is a cache entry with a specified key. |
333
|
|
|
* This method is required by the interface [[\ArrayAccess]]. |
334
|
|
|
* @param string $key a key identifying the cached value |
335
|
|
|
* @return bool |
336
|
|
|
*/ |
337
|
|
|
public function offsetExists($key) |
338
|
|
|
{ |
339
|
|
|
return $this->get($key) !== false; |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/** |
343
|
|
|
* Retrieves the value from cache with a specified key. |
344
|
|
|
* This method is required by the interface [[\ArrayAccess]]. |
345
|
|
|
* @param string $key a key identifying the cached value |
346
|
|
|
* @return mixed the value stored in cache, false if the value is not in the cache or expired. |
347
|
|
|
*/ |
348
|
5 |
|
public function offsetGet($key) |
349
|
|
|
{ |
350
|
5 |
|
return $this->get($key); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* Stores the value identified by a key into cache. |
355
|
|
|
* If the cache already contains such a key, the existing value will be |
356
|
|
|
* replaced with the new ones. To add expiration and dependencies, use the [[set()]] method. |
357
|
|
|
* This method is required by the interface [[\ArrayAccess]]. |
358
|
|
|
* @param string $key the key identifying the value to be cached |
359
|
|
|
* @param mixed $value the value to be cached |
360
|
|
|
*/ |
361
|
50 |
|
public function offsetSet($key, $value) |
362
|
|
|
{ |
363
|
50 |
|
$this->set($key, $value); |
364
|
50 |
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Deletes the value with the specified key from cache |
368
|
|
|
* This method is required by the interface [[\ArrayAccess]]. |
369
|
|
|
* @param string $key the key of the value to be deleted |
370
|
|
|
*/ |
371
|
|
|
public function offsetUnset($key) |
372
|
|
|
{ |
373
|
|
|
$this->delete($key); |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
/** |
377
|
|
|
* Method combines both [[set()]] and [[get()]] methods to retrieve value identified by a $key, |
378
|
|
|
* or to store the result of $callable execution if there is no cache available for the $key. |
379
|
|
|
* |
380
|
|
|
* Usage example: |
381
|
|
|
* |
382
|
|
|
* ```php |
383
|
|
|
* public function getTopProducts($count = 10) { |
384
|
|
|
* $cache = $this->cache; // Could be Yii::$app->cache |
385
|
|
|
* return $cache->getOrSet(['top-n-products', 'n' => $count], function ($cache) use ($count) { |
386
|
|
|
* return Products::find()->mostPopular()->limit(10)->all(); |
387
|
|
|
* }, 1000); |
388
|
|
|
* } |
389
|
|
|
* ``` |
390
|
|
|
* |
391
|
|
|
* @param mixed $key a key identifying the value to be cached. This can be a simple string or |
392
|
|
|
* a complex data structure consisting of factors representing the key. |
393
|
|
|
* @param callable|\Closure $callable the callable or closure that will be used to generate a value to be cached. |
394
|
|
|
* In case $callable returns `false`, the value will not be cached. |
395
|
|
|
* @param null|int|\DateInterval $ttl the TTL value of this item. If not set, default value is used. |
396
|
|
|
* @param Dependency $dependency dependency of the cached item. If the dependency changes, |
397
|
|
|
* the corresponding value in the cache will be invalidated when it is fetched via [[get()]]. |
398
|
|
|
* This parameter is ignored if [[serializer]] is `false`. |
399
|
|
|
* @return mixed result of $callable execution |
400
|
|
|
* @since 2.0.11 |
401
|
|
|
*/ |
402
|
10 |
|
public function getOrSet($key, $callable, $ttl = null, $dependency = null) |
403
|
|
|
{ |
404
|
10 |
|
if (($value = $this->get($key)) !== null) { |
405
|
5 |
|
return $value; |
406
|
|
|
} |
407
|
|
|
|
408
|
10 |
|
$value = call_user_func($callable, $this); |
409
|
10 |
|
if (!$this->set($key, $value, $ttl, $dependency)) { |
410
|
|
|
Yii::warning('Failed to set cache value for key ' . json_encode($key), __METHOD__); |
411
|
|
|
} |
412
|
|
|
|
413
|
10 |
|
return $value; |
414
|
|
|
} |
415
|
|
|
} |
416
|
|
|
|
It seems like the method you are trying to call exists only in some of the possible types.
Let’s take a look at an example:
Available Fixes
Add an additional type-check:
Only allow a single type to be passed if the variable comes from a parameter: