diff --git a/README.md b/README.md index dd33a6a..c50d4d0 100644 --- a/README.md +++ b/README.md @@ -50,13 +50,34 @@ $storage->exists('icanboogie'); // false $storage->retrieve('icanboogie'); // null ``` +### Time To Live + +The Time To Live (TTL) of an item is the amount of seconds between when that item is stored and it +is considered stale. + +> **Warning:** [`apc.use_request_time`][] needs to be set to `false` if you want to use that feature +with APCU. + +```php +store('icanboogie', "Yes Sir, I Can Boogie", $ttl = 3); +$storage->retrieve('icanboogie'); // "Yes Sir, I Can Boogie" +sleep(4); +$storage->exists('icanboogie'); // false +$storage->retrieve('icanboogie'); // null +``` + -### Use storages like arrays +### Use storage like arrays -Storage implement the `ArrayAccess` interface and may be accessed as arrays. +Storage implements the `ArrayAccess` interface and may be accessed as arrays. ```php prefix . $key, $data, $ttl); + apcu_store($this->prefix . $key, $data, $ttl ?: 0); } /** diff --git a/lib/FileStorage.php b/lib/FileStorage.php index e0a9948..216bcd4 100644 --- a/lib/FileStorage.php +++ b/lib/FileStorage.php @@ -59,27 +59,27 @@ public function __construct(string $path, Adapter $adapter = null) */ public function exists(string $key): bool { - return file_exists($this->format_pathname($key)); + $pathname = $this->format_pathname($key); + $ttl_mark = $this->format_pathname_with_ttl($pathname); + + if (file_exists($ttl_mark) && fileatime($ttl_mark) < time() || !file_exists($pathname)) + { + return false; + } + + return file_exists($pathname); } /** * @inheritdoc - * - * @param mixed $default The value returned if the key does not exists. Defaults to `null`. */ public function retrieve(string $key) { - $this->check_writable(); - - $pathname = $this->format_pathname($key); - $ttl_mark = $this->format_pathname_with_ttl($pathname); - - if (file_exists($ttl_mark) && fileatime($ttl_mark) < time() || !file_exists($pathname)) - { + if (!$this->exists($key)) { return null; } - return $this->read($pathname); + return $this->read($this->format_pathname($key)); } /** diff --git a/lib/RunTimeStorage.php b/lib/RunTimeStorage.php index 622ec02..65c6e6e 100644 --- a/lib/RunTimeStorage.php +++ b/lib/RunTimeStorage.php @@ -18,13 +18,25 @@ class RunTimeStorage implements Storage, \ArrayAccess { use Storage\ArrayAccess; + /** + * @var array + */ private $values = []; + /** + * @var array + */ + private $until = []; + /** * @inheritdoc */ public function exists(string $key): bool { + if (isset($this->until[$key]) && $this->until[$key] < time()) { + return false; + } + return array_key_exists($key, $this->values); } @@ -42,6 +54,7 @@ public function retrieve(string $key) public function store(string $key, $value, int $ttl = null): void { $this->values[$key] = $value; + $this->until[$key] = time() + $ttl; } /** diff --git a/tests/StorageCollectionTest.php b/tests/StorageCollectionTest.php index 8f6e917..9e85e74 100644 --- a/tests/StorageCollectionTest.php +++ b/tests/StorageCollectionTest.php @@ -15,6 +15,8 @@ class StorageCollectionTest extends TestCase { + use TestStorageTrait; + /** * @var StorageCollection */ @@ -33,14 +35,14 @@ class StorageCollectionTest extends TestCase /** * @var StorageCollection */ - private $collection; + private $storage; public function setUp() { $this->s1 = $s1 = new RunTimeStorage; $this->s2 = $s2 = new RunTimeStorage; $this->s3 = $s3 = new RunTimeStorage; - $this->collection = new StorageCollection([ $s1, $s2, $s3 ]); + $this->storage = new StorageCollection([ $s1, $s2, $s3 ]); } public function test_store() @@ -48,11 +50,11 @@ public function test_store() $key = uniqid(); $value = uniqid(); - $this->collection->store($key, $value); + $this->storage->store($key, $value); /* @var $storage Storage */ - foreach([ $this->collection, $this->s1, $this->s2, $this->s3 ] as $storage) + foreach([ $this->storage, $this->s1, $this->s2, $this->s3 ] as $storage) { $this->assertTrue($storage->exists($key)); $this->assertSame($value, $storage->retrieve($key)); @@ -64,7 +66,7 @@ public function test_update_up() $s1 = $this->s1; $s2 = $this->s2; $s3 = $this->s3; - $collection = $this->collection; + $collection = $this->storage; $key = uniqid(); $value = uniqid(); @@ -82,7 +84,7 @@ public function test_eliminate() $s1 = $this->s1; $s2 = $this->s2; $s3 = $this->s3; - $collection = $this->collection; + $collection = $this->storage; $k1 = uniqid(); $v1 = uniqid(); $k2 = uniqid(); @@ -113,7 +115,7 @@ public function test_clear() $s1 = $this->s1; $s2 = $this->s2; $s3 = $this->s3; - $collection = $this->collection; + $collection = $this->storage; $k1 = uniqid(); $v1 = uniqid(); $k2 = uniqid(); @@ -135,7 +137,7 @@ public function test_clear() public function test_array_access() { - $collection = $this->collection; + $collection = $this->storage; $k = uniqid(); $v = uniqid(); @@ -150,16 +152,16 @@ public function test_array_access() public function test_find_by_type() { - $this->assertSame($this->s1, $this->collection->find_by_type(RunTimeStorage::class)); + $this->assertSame($this->s1, $this->storage->find_by_type(RunTimeStorage::class)); } public function test_find_by_type_undefined() { - $this->assertNull($this->collection->find_by_type(RedisStorage::class)); + $this->assertNull($this->storage->find_by_type(RedisStorage::class)); } public function test_iterator() { - $this->assertInstanceOf(\Iterator::class, $this->collection->getIterator()); + $this->assertInstanceOf(\Iterator::class, $this->storage->getIterator()); } } diff --git a/tests/TestStorageTrait.php b/tests/TestStorageTrait.php index c7308d7..f883244 100644 --- a/tests/TestStorageTrait.php +++ b/tests/TestStorageTrait.php @@ -49,6 +49,17 @@ public function test_storage() $this->assertNull($s->retrieve($k2)); } + public function test_store_with_ttl() + { + $storage = $this->storage; + $storage->store($key = uniqid(), $value = uniqid(), $ttl = 1); + $this->assertTrue($storage->exists($key)); + $this->assertSame($value, $storage->retrieve($key)); + sleep($ttl + 1); + $this->assertFalse($storage->exists($key)); + $this->assertNull($storage->retrieve($key)); + } + public function test_iterator() { $s = $this->storage; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index e90c7a9..6a56f6c 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -11,5 +11,8 @@ define('ICanBoogie\Storage\SANDBOX_DIR', __DIR__ . '/sandbox'); +// https://www.php.net/manual/en/apcu.configuration.php#ini.apcu.use-request-time +ini_set('apc.use_request_time', false); + require __DIR__ . '/../vendor/autoload.php'; require 'TestStorageTrait.php';