diff --git a/composer.json b/composer.json index 2b00a36a..708115ce 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ }, "require": { "php": ">=7.2", - "ext-swoole": ">=4.6" + "ext-swoole": ">=4.6", + "psr/cache": "^1.0" }, "require-dev": { "ext-sockets": "*", @@ -48,7 +49,8 @@ "src/vendor_init.php" ], "psr-4": { - "Swoole\\": "src/core" + "Swoole\\": "src/core", + "Swoole\\Psr\\": "src/psr" } }, "autoload-dev": { diff --git a/src/psr/Cache/PhpSerializer.php b/src/psr/Cache/PhpSerializer.php new file mode 100644 index 00000000..7774fea3 --- /dev/null +++ b/src/psr/Cache/PhpSerializer.php @@ -0,0 +1,25 @@ +key = $key; + $this->value = $value; + } + + public function getKey(): string + { + return $this->key; + } + + public function get() + { + return $this->value; + } + + public function isHit(): bool + { + return true; + } + + public function set($value): self + { + $this->value = $value; + return $this; + } + + public function expiresAt($expiration) + { + $this->ttl = $expiration->getTimestamp(); + return $this; + } + + public function expiresAfter($time) + { + $this->ttl = (new \DateTime())->add($time)->getTimestamp(); + return $this; + } +} diff --git a/src/psr/Cache/Table/CacheItemPool.php b/src/psr/Cache/Table/CacheItemPool.php new file mode 100644 index 00000000..22ae228e --- /dev/null +++ b/src/psr/Cache/Table/CacheItemPool.php @@ -0,0 +1,149 @@ +size = $size; + $this->serializer = $serializer; + + $this->deferred = []; + $this->table = $this->createTable(); + } + + /** + * @param string $key + */ + public function getItem($key): CacheItemInterface + { + $row = $this->table->get($key); + return $this->cacheItemFromRow($row); + } + + public function getItems(array $keys = []): iterable + { + foreach ($keys as $key) { + yield $this->getItem($key); + } + } + + /** + * @param string $key + */ + public function hasItem($key): bool + { + return $this->table->exists($key); + } + + public function clear(): bool + { + $destroyed = $this->table->destroy(); + $this->table = $this->createTable(); + return $destroyed; + } + + /** + * @param string $key + */ + public function deleteItem($key): bool + { + return $this->table->delete($key); + } + + /** + * @param array $keys + */ + public function deleteItems(array $keys): bool + { + foreach ($keys as $key) { + if (!$this->deleteItem($key)) { + return false; + } + } + + return true; + } + + public function save(CacheItemInterface $item): bool + { + return $this->table->set($item->getKey(), $this->rowFromCacheItem($item)); + } + + public function saveDeferred(CacheItemInterface $item): bool + { + $this->deferred[] = $item; + return true; + } + + public function commit(): bool + { + foreach ($this->deferred as $item) { + if (!$this->save($item)) { + return false; + } + } + + $this->deferred = []; + return true; + } + + private function createTable(): Table + { + $table = new Table($this->size); + $table->column('key', Table::TYPE_STRING, 64); + $table->column('value', Table::TYPE_STRING, 64); + $table->column('ttl', Table::TYPE_INT); + $table->create(); + + return $table; + } + + private function cacheItemFromRow(array $row): CacheItemInterface + { + return new CacheItem($row['key'], $this->serializer->unserialize($row['value'])); + } + + private function rowFromCacheItem(CacheItemInterface $item): array + { + return [ + 'key' => $item->getKey(), + 'value' => $this->serializer->serialize($item->get()), + ]; + } +} diff --git a/tests/unit/Psr/Cache/Table/CacheItemPoolTest.php b/tests/unit/Psr/Cache/Table/CacheItemPoolTest.php new file mode 100644 index 00000000..46040a3f --- /dev/null +++ b/tests/unit/Psr/Cache/Table/CacheItemPoolTest.php @@ -0,0 +1,82 @@ +pool = new CacheItemPool(1024, new PhpSerializer()); + } + + public function testClear() + { + $this->pool->save(new CacheItem('foo', 'bar')); + $this->assertTrue($this->pool->hasItem('foo')); + $this->pool->clear(); + $this->assertFalse($this->pool->hasItem('foo')); + } + + public function testDeleteItem() + { + $this->pool->save(new CacheItem('foo', 'bar')); + $this->assertTrue($this->pool->hasItem('foo')); + $this->pool->deleteItem('foo'); + $this->assertFalse($this->pool->hasItem('foo')); + } + + public function testSaveDeferredAndCommit() + { + $this->pool->saveDeferred(new CacheItem('foo', 'bar')); + $this->assertFalse($this->pool->hasItem('foo')); + $this->pool->commit(); + $this->assertTrue($this->pool->hasItem('foo')); + } + + public function testDeleteItems() + { + $this->pool->save(new CacheItem('foo', 'bar')); + $this->assertTrue($this->pool->hasItem('foo')); + $this->pool->deleteItems(['foo']); + $this->assertFalse($this->pool->hasItem('foo')); + } + + public function testGetItems() + { + $this->pool->save(new CacheItem('foo', 'bar')); + $item = $this->pool->getItem('foo'); + $this->assertSame('foo', $item->getKey()); + $this->assertSame('bar', $item->get()); + } + + public function testGetItem() + { + $this->pool->save(new CacheItem('foo', 'bar')); + + foreach ($this->pool->getItems(['foo']) as $item) { + $this->assertSame('foo', $item->getKey()); + $this->assertSame('bar', $item->get()); + } + } +}