Skip to content

Commit eeb341c

Browse files
authored
Merge pull request #35 from bigcommerce/lru
Fix LRU cache mechanism to evict least recently used instead of FIFO
2 parents 1121022 + 90517d4 commit eeb341c

2 files changed

Lines changed: 51 additions & 4 deletions

File tree

src/Reflection/ReflectionClassCache.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,14 @@ public function __construct(int $maxSize)
2727
*/
2828
public function put(ReflectionClass $reflection): void
2929
{
30-
if (count($this->map) >= $this->maxSize) {
30+
$className = $reflection->getName();
31+
// if already present, remove it so we can re-insert it as most recently used item
32+
if (isset($this->map[$className])) {
33+
unset($this->map[$className]);
34+
} elseif (count($this->map) >= $this->maxSize) {
3135
$this->evictOneObject();
3236
}
33-
$this->map[$reflection->getName()] = $reflection;
37+
$this->map[$className] = $reflection;
3438
}
3539

3640
/**
@@ -40,7 +44,11 @@ public function put(ReflectionClass $reflection): void
4044
public function get(string $className): ?ReflectionClass
4145
{
4246
if (isset($this->map[$className])) {
43-
return $this->map[$className];
47+
// promote to most recently used item by re-inserting this to the end of the array
48+
$reflection = $this->map[$className];
49+
unset($this->map[$className]);
50+
$this->map[$className] = $reflection;
51+
return $reflection;
4452
}
4553
return null;
4654
}
@@ -57,7 +65,6 @@ public function count(): int
5765

5866
private function evictOneObject(): void
5967
{
60-
// Get the least recently used item (first in insertion order)
6168
$lruClassName = array_key_first($this->map);
6269
if ($lruClassName !== null) {
6370
unset($this->map[$lruClassName]);

tests/Reflection/ReflectionClassCacheTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,44 @@ public function testCacheDoesNotExceedMaxSize(): void
6868

6969
$this->assertEquals(2, $result);
7070
}
71+
72+
public function testGetPromotesToMostRecentlyUsedSoItDoesNotEvictNext(): void
73+
{
74+
$cache = new ReflectionClassCache(2);
75+
76+
$classA = DummyDependency::class;
77+
$classB = DummyNoConstructor::class;
78+
$classC = DummySimpleConstructor::class;
79+
80+
$cache->put(new ReflectionClass($classA));
81+
$cache->put(new ReflectionClass($classB));
82+
83+
$this->assertNotNull($cache->get($classA));
84+
85+
$cache->put(new ReflectionClass($classC));
86+
87+
$this->assertTrue($cache->has($classA));
88+
$this->assertFalse($cache->has($classB));
89+
$this->assertTrue($cache->has($classC));
90+
}
91+
92+
public function testPutRefreshesRecencySoReinsertedEntryIsNotEvicted(): void
93+
{
94+
$cache = new ReflectionClassCache(2);
95+
96+
$classA = DummyDependency::class;
97+
$classB = DummyNoConstructor::class;
98+
$classC = DummySimpleConstructor::class;
99+
100+
$cache->put(new ReflectionClass($classA));
101+
$cache->put(new ReflectionClass($classB));
102+
103+
$cache->put(new ReflectionClass($classA));
104+
105+
$cache->put(new ReflectionClass($classC));
106+
107+
$this->assertTrue($cache->has($classA));
108+
$this->assertFalse($cache->has($classB));
109+
$this->assertTrue($cache->has($classC));
110+
}
71111
}

0 commit comments

Comments
 (0)