-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
<?php | ||
|
||
namespace Phikl\Cache; | ||
|
||
use Psr\SimpleCache\CacheInterface; | ||
|
||
/** | ||
* Simple implementation of the PSR-16 CacheInterface using APCu | ||
* for the Pkl modules evaluation cache. | ||
*/ | ||
final class APCuCache implements CacheInterface | ||
{ | ||
/** | ||
* @param non-empty-string $key | ||
* @param Entry|null $default | ||
*/ | ||
public function get(string $key, mixed $default = null): Entry|null | ||
{ | ||
if ($default !== null && !$default instanceof Entry) { | ||
Check failure on line 19 in src/Cache/APCuCache.php
|
||
throw new \InvalidArgumentException('Default value must be null or an instance of Entry'); | ||
} | ||
|
||
$entry = \apcu_fetch($key); | ||
if ($entry === false) { | ||
return $default; | ||
} | ||
|
||
$entry = @unserialize($entry); | ||
if ($entry === false) { | ||
return $default; | ||
} | ||
|
||
return $entry; | ||
} | ||
|
||
/** | ||
* @param Entry $value | ||
*/ | ||
public function set(string $key, mixed $value, \DateInterval|int|null $ttl = null): bool | ||
{ | ||
if (!$value instanceof Entry) { | ||
return false; | ||
} | ||
|
||
return \apcu_store( | ||
$key, | ||
serialize($value), | ||
$ttl instanceof \DateInterval ? $ttl->format('U') - \time() : $ttl | ||
Check failure on line 48 in src/Cache/APCuCache.php
|
||
); | ||
} | ||
|
||
public function delete(string $key): bool | ||
{ | ||
return \apcu_delete($key); | ||
} | ||
|
||
/** | ||
* Caution, this method will clear the entire cache, not just the cache for this application. | ||
*/ | ||
public function clear(): bool | ||
{ | ||
return \apcu_clear_cache(); | ||
} | ||
|
||
/** | ||
* @param iterable<string> $keys | ||
* @return iterable<string, Entry|null> | ||
*/ | ||
public function getMultiple(iterable $keys, mixed $default = null): iterable | ||
{ | ||
$entries = []; | ||
foreach ($keys as $key) { | ||
$entries[$key] = $this->get($key, $default); | ||
} | ||
Check failure on line 74 in src/Cache/APCuCache.php
|
||
|
||
return $entries; | ||
} | ||
|
||
/** | ||
* @param iterable<string, Entry> $values | ||
*/ | ||
public function setMultiple(iterable $values, \DateInterval|int|null $ttl = null): bool | ||
{ | ||
foreach ($values as $key => $value) { | ||
if (!$this->set($key, $value, $ttl)) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* @param iterable<string> $keys | ||
*/ | ||
public function deleteMultiple(iterable $keys): bool | ||
{ | ||
$success = true; | ||
foreach ($keys as $key) { | ||
if (!$this->delete($key)) { | ||
$success = false; | ||
} | ||
} | ||
|
||
return $success; | ||
} | ||
|
||
public function has(string $key): bool | ||
{ | ||
return \apcu_exists($key); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
<?php | ||
|
||
namespace Cache; | ||
|
||
use Phikl\Cache\APCuCache; | ||
use Phikl\Cache\Entry; | ||
use PHPUnit\Framework\Attributes\CoversClass; | ||
use PHPUnit\Framework\Attributes\RequiresPhpExtension; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
#[RequiresPhpExtension('apcu')] | ||
#[CoversClass(APCuCache::class)] | ||
class APCuCacheTest extends TestCase | ||
{ | ||
protected function tearDown(): void | ||
{ | ||
\apcu_clear_cache(); | ||
} | ||
|
||
public function testGetWithDefaultOtherThanEntryInstance(): void | ||
{ | ||
$cache = new APCuCache(); | ||
|
||
$this->expectException(\InvalidArgumentException::class); | ||
$this->expectExceptionMessage('Default value must be null or an instance of Entry'); | ||
|
||
$cache->get('key', 'invalid'); | ||
Check failure on line 27 in tests/Cache/APCuCacheTest.php
|
||
} | ||
|
||
public function testGetReturnsDefaultIfKeyDoesNotExist(): void | ||
{ | ||
$cache = new APCuCache(); | ||
|
||
$entry = new Entry('content', 'hash', 0); | ||
|
||
$this->assertNull($cache->get('nonexistent')); | ||
$this->assertSame($entry, $cache->get('nonexistent', $entry)); | ||
$this->assertFalse($cache->has('nonexistent')); | ||
} | ||
|
||
public function testGetOnValidSetEntry(): void | ||
{ | ||
$cache = new APCuCache(); | ||
|
||
$entry = new Entry('content', 'hash', $time = \time()); | ||
$cache->set('key', $entry); | ||
|
||
$entry = $cache->get('key'); | ||
$this->assertSame('content', $entry->content); | ||
Check failure on line 49 in tests/Cache/APCuCacheTest.php
|
||
$this->assertSame('hash', $entry->hash); | ||
Check failure on line 50 in tests/Cache/APCuCacheTest.php
|
||
$this->assertSame($time, $entry->timestamp); | ||
Check failure on line 51 in tests/Cache/APCuCacheTest.php
|
||
} | ||
|
||
public function testSetReturnsFalseOnInvalidEntry(): void | ||
{ | ||
$cache = new APCuCache(); | ||
|
||
$this->assertFalse($cache->set('key', 'invalid')); | ||
Check failure on line 58 in tests/Cache/APCuCacheTest.php
|
||
} | ||
|
||
public function testDeleteEntry(): void | ||
{ | ||
$cache = new APCuCache(); | ||
|
||
$entry = new Entry('content', 'hash', 0); | ||
$cache->set('key', $entry); | ||
|
||
$this->assertTrue($cache->delete('key')); | ||
$this->assertNull($cache->get('key')); | ||
} | ||
|
||
public function testClear(): void | ||
{ | ||
$cache = new APCuCache(); | ||
|
||
$entry = new Entry('content', 'hash', 0); | ||
$cache->set('key', $entry); | ||
|
||
$this->assertTrue($cache->clear()); | ||
$this->assertNull($cache->get('key')); | ||
} | ||
|
||
public function testGetSetMultiple(): void | ||
{ | ||
$cache = new APCuCache(); | ||
|
||
$entry1 = new Entry('content1', 'hash1', 0); | ||
$entry2 = new Entry('content2', 'hash2', 0); | ||
$entry3 = new Entry('content3', 'hash3', 0); | ||
|
||
$cache->setMultiple([ | ||
'key1' => $entry1, | ||
'key2' => $entry2, | ||
'key3' => $entry3, | ||
]); | ||
|
||
$entries = $cache->getMultiple(['key1', 'key2', 'key3']); | ||
|
||
$this->assertSame('content1', $entries['key1']->content); | ||
Check failure on line 99 in tests/Cache/APCuCacheTest.php
|
||
$this->assertSame('hash1', $entries['key1']->hash); | ||
Check failure on line 100 in tests/Cache/APCuCacheTest.php
|
||
|
||
$this->assertSame('content2', $entries['key2']->content); | ||
$this->assertSame('hash2', $entries['key2']->hash); | ||
|
||
$this->assertSame('content3', $entries['key3']->content); | ||
$this->assertSame('hash3', $entries['key3']->hash); | ||
} | ||
|
||
public function testDeleteMultiple(): void | ||
{ | ||
$cache = new APCuCache(); | ||
|
||
$entry1 = new Entry('content1', 'hash1', 0); | ||
$entry2 = new Entry('content2', 'hash2', 0); | ||
$entry3 = new Entry('content3', 'hash3', 0); | ||
|
||
$cache->setMultiple([ | ||
'key1' => $entry1, | ||
'key2' => $entry2, | ||
'key3' => $entry3, | ||
]); | ||
|
||
$this->assertTrue($cache->deleteMultiple(['key1', 'key2'])); | ||
$this->assertNull($cache->get('key1')); | ||
$this->assertNull($cache->get('key2')); | ||
$this->assertNotNull($cache->get('key3')); | ||
} | ||
|
||
public function testHas(): void | ||
{ | ||
$cache = new APCuCache(); | ||
|
||
$entry = new Entry('content', 'hash', 0); | ||
$cache->set('key', $entry); | ||
|
||
$this->assertTrue($cache->has('key')); | ||
$this->assertFalse($cache->has('invalid')); | ||
} | ||
} |