19
19
use Symfony \Contracts \Cache \CacheInterface ;
20
20
21
21
/**
22
+ * An in-memory cache storage.
23
+ *
24
+ * Acts as a least-recently-used (LRU) storage when configured with a maximum number of items.
25
+ *
22
26
* @author Nicolas Grekas <[email protected] >
23
27
*/
24
28
class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
@@ -29,13 +33,17 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
29
33
private $ values = [];
30
34
private $ expiries = [];
31
35
private $ createCacheItem ;
36
+ private $ maxLifetime ;
37
+ private $ maxItems ;
32
38
33
39
/**
34
40
* @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
35
41
*/
36
- public function __construct (int $ defaultLifetime = 0 , bool $ storeSerialized = true )
42
+ public function __construct (int $ defaultLifetime = 0 , bool $ storeSerialized = true , int $ maxLifetime = 0 , int $ maxItems = 0 )
37
43
{
38
44
$ this ->storeSerialized = $ storeSerialized ;
45
+ $ this ->maxLifetime = 0 < $ maxLifetime ? $ maxLifetime : 0 ;
46
+ $ this ->maxItems = 0 < $ maxItems ? $ maxItems : 0 ;
39
47
$ this ->createCacheItem = \Closure::bind (
40
48
static function ($ key , $ value , $ isHit ) use ($ defaultLifetime ) {
41
49
$ item = new CacheItem ();
@@ -84,6 +92,13 @@ public function delete(string $key): bool
84
92
public function hasItem ($ key )
85
93
{
86
94
if (\is_string ($ key ) && isset ($ this ->expiries [$ key ]) && $ this ->expiries [$ key ] > microtime (true )) {
95
+ if ($ this ->maxItems ) {
96
+ // Move the item last in the storage
97
+ $ value = $ this ->values [$ key ];
98
+ unset($ this ->values [$ key ]);
99
+ $ this ->values [$ key ] = $ value ;
100
+ }
101
+
87
102
return true ;
88
103
}
89
104
CacheItem::validateKey ($ key );
@@ -97,7 +112,12 @@ public function hasItem($key)
97
112
public function getItem ($ key )
98
113
{
99
114
if (!$ isHit = $ this ->hasItem ($ key )) {
100
- $ this ->values [$ key ] = $ value = null ;
115
+ $ value = null ;
116
+
117
+ if (!$ this ->maxItems ) {
118
+ // Track misses in non-LRU mode only
119
+ $ this ->values [$ key ] = null ;
120
+ }
101
121
} else {
102
122
$ value = $ this ->storeSerialized ? $ this ->unfreeze ($ key , $ isHit ) : $ this ->values [$ key ];
103
123
}
@@ -164,7 +184,9 @@ public function save(CacheItemInterface $item)
164
184
$ value = $ item ["\0* \0value " ];
165
185
$ expiry = $ item ["\0* \0expiry " ];
166
186
167
- if (null !== $ expiry && $ expiry <= microtime (true )) {
187
+ $ now = microtime (true );
188
+
189
+ if (null !== $ expiry && $ expiry <= $ now ) {
168
190
$ this ->deleteItem ($ key );
169
191
170
192
return true ;
@@ -173,7 +195,23 @@ public function save(CacheItemInterface $item)
173
195
return false ;
174
196
}
175
197
if (null === $ expiry && 0 < $ item ["\0* \0defaultLifetime " ]) {
176
- $ expiry = microtime (true ) + $ item ["\0* \0defaultLifetime " ];
198
+ $ expiry = $ item ["\0* \0defaultLifetime " ]
199
+ $ expiry = $ now + ($ expiry > ($ this ->maxLifetime ?: $ expiry ) : $ this ->maxLifetime : $ expiry );
200
+ } elseif ($ this ->maxLifetime && (null === $ expiry || $ expiry > $ now + $ this ->maxLifetime )) {
201
+ $ expiry = $ now + $ this ->maxLifetime ;
202
+ }
203
+
204
+ if ($ this ->maxItems ) {
205
+ unset($ this ->values [$ key ]);
206
+
207
+ // Iterate items and vacuum expired ones while we are at it
208
+ foreach ($ this ->values as $ k => $ v ) {
209
+ if ($ this ->expiries [$ k ] > $ now && \count ($ this ->values ) < $ this ->maxItems ) {
210
+ break ;
211
+ }
212
+
213
+ unset($ this ->values [$ k ], $ this ->expiries [$ k ]);
214
+ }
177
215
}
178
216
179
217
$ this ->values [$ key ] = $ value ;
@@ -210,15 +248,21 @@ public function commit()
210
248
public function clear (string $ prefix = '' )
211
249
{
212
250
if ('' !== $ prefix ) {
251
+ $ now = microtime (true );
252
+
213
253
foreach ($ this ->values as $ key => $ value ) {
214
- if (0 === strpos ($ key , $ prefix )) {
254
+ if (! isset ( $ this -> expiries [ $ key ]) || $ this -> expiries [ $ key ] <= $ now || 0 === strpos ($ key , $ prefix )) {
215
255
unset($ this ->values [$ key ], $ this ->expiries [$ key ]);
216
256
}
217
257
}
218
- } else {
219
- $ this ->values = $ this ->expiries = [];
258
+
259
+ if ($ this ->values ) {
260
+ return true ;
261
+ }
220
262
}
221
263
264
+ $ this ->values = $ this ->expiries = [];
265
+
222
266
return true ;
223
267
}
224
268
@@ -258,8 +302,20 @@ private function generateItems(array $keys, $now, $f)
258
302
{
259
303
foreach ($ keys as $ i => $ key ) {
260
304
if (!$ isHit = isset ($ this ->expiries [$ key ]) && ($ this ->expiries [$ key ] > $ now || !$ this ->deleteItem ($ key ))) {
261
- $ this ->values [$ key ] = $ value = null ;
305
+ $ value = null ;
306
+
307
+ if (!$ this ->maxItems ) {
308
+ // Track misses in non-LRU mode only
309
+ $ this ->values [$ key ] = null ;
310
+ }
262
311
} else {
312
+ if ($ this ->maxItems ) {
313
+ // Move the item last in the storage
314
+ $ value = $ this ->values [$ key ];
315
+ unset($ this ->values [$ key ]);
316
+ $ this ->values [$ key ] = $ value ;
317
+ }
318
+
263
319
$ value = $ this ->storeSerialized ? $ this ->unfreeze ($ key , $ isHit ) : $ this ->values [$ key ];
264
320
}
265
321
unset($ keys [$ i ]);
@@ -314,8 +370,12 @@ private function unfreeze(string $key, bool &$isHit)
314
370
$ value = false ;
315
371
}
316
372
if (false === $ value ) {
317
- $ this -> values [ $ key ] = $ value = null ;
373
+ $ value = null ;
318
374
$ isHit = false ;
375
+
376
+ if (!$ this ->maxItems ) {
377
+ $ this ->values [$ key ] = null ;
378
+ }
319
379
}
320
380
}
321
381
0 commit comments