1919use Symfony \Contracts \Cache \CacheInterface ;
2020
2121/**
22+ * An in-memory cache storage.
23+ *
24+ * Acts as Least Recently Used (LRU) storage when configured with a maximum number of items.
25+ *
2226 * @author Nicolas Grekas <p@tchwork.com>
2327 */
2428class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
@@ -29,13 +33,17 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
2933 private $ values = [];
3034 private $ expiries = [];
3135 private $ createCacheItem ;
36+ private $ maxLifetime ;
37+ private $ maxItems ;
3238
3339 /**
3440 * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
3541 */
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 )
3743 {
3844 $ this ->storeSerialized = $ storeSerialized ;
45+ $ this ->maxLifetime = 0 < $ maxLifetime ? $ maxLifetime : 0 ;
46+ $ this ->maxItems = 0 < $ maxItems ? $ maxItems : 0 ;
3947 $ this ->createCacheItem = \Closure::bind (
4048 static function ($ key , $ value , $ isHit ) use ($ defaultLifetime ) {
4149 $ item = new CacheItem ();
@@ -84,6 +92,13 @@ public function delete(string $key): bool
8492 public function hasItem ($ key )
8593 {
8694 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+
87102 return true ;
88103 }
89104 CacheItem::validateKey ($ key );
@@ -97,7 +112,11 @@ public function hasItem($key)
97112 public function getItem ($ key )
98113 {
99114 if (!$ isHit = $ this ->hasItem ($ key )) {
100- $ this ->values [$ key ] = $ value = null ;
115+ $ value = null ;
116+
117+ if (!$ this ->maxItems ) {
118+ $ this ->values [$ key ] = null ;
119+ }
101120 } else {
102121 $ value = $ this ->storeSerialized ? $ this ->unfreeze ($ key , $ isHit ) : $ this ->values [$ key ];
103122 }
@@ -164,16 +183,32 @@ public function save(CacheItemInterface $item)
164183 $ value = $ item ["\0* \0value " ];
165184 $ expiry = $ item ["\0* \0expiry " ];
166185
167- if (null !== $ expiry && $ expiry <= microtime (true )) {
186+ $ now = microtime (true );
187+
188+ if (null !== $ expiry && $ expiry <= $ now ) {
168189 $ this ->deleteItem ($ key );
169190
170191 return true ;
171192 }
172193 if ($ this ->storeSerialized && null === $ value = $ this ->freeze ($ value , $ key )) {
173194 return false ;
174195 }
175- if (null === $ expiry && 0 < $ item ["\0* \0defaultLifetime " ]) {
176- $ expiry = microtime (true ) + $ item ["\0* \0defaultLifetime " ];
196+ if ($ this ->maxLifetime && (null === $ expiry || $ expiry > $ now + $ this ->maxLifetime )) {
197+ $ expiry = $ now + $ this ->maxLifetime ;
198+ } elseif (null === $ expiry && 0 < $ item ["\0* \0defaultLifetime " ]) {
199+ $ expiry = $ now + $ item ["\0* \0defaultLifetime " ];
200+ }
201+
202+ if ($ this ->maxItems ) {
203+ unset($ this ->values [$ key ]);
204+
205+ foreach ($ this ->values as $ k => $ v ) {
206+ if ($ this ->expiries [$ k ] > $ now && \count ($ this ->values ) < $ this ->maxItems ) {
207+ break ;
208+ }
209+
210+ unset($ this ->values [$ k ], $ this ->expiries [$ k ]);
211+ }
177212 }
178213
179214 $ this ->values [$ key ] = $ value ;
@@ -210,15 +245,21 @@ public function commit()
210245 public function clear (string $ prefix = '' )
211246 {
212247 if ('' !== $ prefix ) {
248+ $ now = microtime (true );
249+
213250 foreach ($ this ->values as $ key => $ value ) {
214- if (0 === strpos ($ key , $ prefix )) {
251+ if ($ this -> expiries [ $ key ] <= $ now || 0 === strpos ($ key , $ prefix )) {
215252 unset($ this ->values [$ key ], $ this ->expiries [$ key ]);
216253 }
217254 }
218- } else {
219- $ this ->values = $ this ->expiries = [];
255+
256+ if ($ this ->values ) {
257+ return true ;
258+ }
220259 }
221260
261+ $ this ->values = $ this ->expiries = [];
262+
222263 return true ;
223264 }
224265
@@ -258,8 +299,19 @@ private function generateItems(array $keys, $now, $f)
258299 {
259300 foreach ($ keys as $ i => $ key ) {
260301 if (!$ isHit = isset ($ this ->expiries [$ key ]) && ($ this ->expiries [$ key ] > $ now || !$ this ->deleteItem ($ key ))) {
261- $ this ->values [$ key ] = $ value = null ;
302+ $ value = null ;
303+
304+ if (!$ this ->maxItems ) {
305+ $ this ->values [$ key ] = null ;
306+ }
262307 } else {
308+ if ($ this ->maxItems ) {
309+ // move the item last in the storage
310+ $ value = $ this ->values [$ key ];
311+ unset($ this ->values [$ key ]);
312+ $ this ->values [$ key ] = $ value ;
313+ }
314+
263315 $ value = $ this ->storeSerialized ? $ this ->unfreeze ($ key , $ isHit ) : $ this ->values [$ key ];
264316 }
265317 unset($ keys [$ i ]);
@@ -314,8 +366,12 @@ private function unfreeze(string $key, bool &$isHit)
314366 $ value = false ;
315367 }
316368 if (false === $ value ) {
317- $ this -> values [ $ key ] = $ value = null ;
369+ $ value = null ;
318370 $ isHit = false ;
371+
372+ if (!$ this ->maxItems ) {
373+ $ this ->values [$ key ] = null ;
374+ }
319375 }
320376 }
321377
0 commit comments