Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e22800d

Browse filesBrowse files
[Cache] Add LRU + max-lifetime capabilities to ArrayCache
1 parent b350c80 commit e22800d
Copy full SHA for e22800d

File tree

3 files changed

+103
-10
lines changed
Filter options

3 files changed

+103
-10
lines changed

‎src/Symfony/Component/Cache/Adapter/ArrayAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
+66-10Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
use 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
*/
2428
class 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

‎src/Symfony/Component/Cache/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
5.1.0
5+
-----
6+
7+
* added LRU + max-lifetime capabilities to `ArrayCache`
8+
49
5.0.0
510
-----
611

‎src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,36 @@ public function testGetValuesHitAndMiss()
5555
$this->assertArrayHasKey('bar', $values);
5656
$this->assertNull($values['bar']);
5757
}
58+
59+
public function testMaxLifetime()
60+
{
61+
$cache = new ArrayAdapter(0, false, 1);
62+
63+
$item = $cache->getItem('foo');
64+
$item->expiresAfter(2);
65+
$cache->save($item->set(123));
66+
67+
$this->assertTrue($cache->hasItem('foo'));
68+
sleep(1);
69+
$this->assertFalse($cache->hasItem('foo'));
70+
}
71+
72+
public function testMaxItems()
73+
{
74+
$cache = new ArrayAdapter(0, false, 0, 2);
75+
76+
$cache->save($cache->getItem('foo'));
77+
$cache->save($cache->getItem('bar'));
78+
$cache->save($cache->getItem('buz'));
79+
80+
$this->assertFalse($cache->hasItem('foo'));
81+
$this->assertTrue($cache->hasItem('bar'));
82+
$this->assertTrue($cache->hasItem('buz'));
83+
84+
$cache->save($cache->getItem('foo'));
85+
86+
$this->assertFalse($cache->hasItem('bar'));
87+
$this->assertTrue($cache->hasItem('buz'));
88+
$this->assertTrue($cache->hasItem('foo'));
89+
}
5890
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.