diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..3a9251f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: byjg diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 22bba59..6dda630 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -16,10 +16,9 @@ jobs: strategy: matrix: php-version: + - "8.3" - "8.2" - "8.1" - - "8.0" - - "7.4" # Service containers to run services: @@ -37,6 +36,7 @@ jobs: - uses: actions/checkout@v4 - run: composer install - run: ./vendor/bin/phpunit --stderr + - run: ./vendor/bin/psalm Documentation: if: github.ref == 'refs/heads/master' diff --git a/.run/PHPUnit.run.xml b/.run/PHPUnit.run.xml new file mode 100644 index 0000000..7034700 --- /dev/null +++ b/.run/PHPUnit.run.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.run/PSalm.run.xml b/.run/PSalm.run.xml new file mode 100644 index 0000000..bd119ce --- /dev/null +++ b/.run/PSalm.run.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/composer.json b/composer.json index 5ce69c6..0053294 100644 --- a/composer.json +++ b/composer.json @@ -6,19 +6,26 @@ "ByJG\\Cache\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, "require": { - "php": ">=7.4", - "psr/cache": "^1.0|^2.0", + "php": ">=8.1 <8.4", + "psr/cache": "^1.0|^2.0|^3.0", "psr/log": "^1.0|^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0", "psr/container": "^1.0|^1.1|^2.0" }, "require-dev": { - "phpunit/phpunit": "5.7.*|7.4.*|^9.5" + "phpunit/phpunit": "^9.6", + "vimeo/psalm": "^5.9" }, "suggest": { "ext-memcached": "*", - "ext-redis": "*" + "ext-redis": "*", + "ext-shmop": "*" }, "provide": { "psr/cache-implementation": "1.0", diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..ebabb1a --- /dev/null +++ b/psalm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/CacheAvailabilityInterface.php b/src/CacheAvailabilityInterface.php index 9e66d23..a954805 100644 --- a/src/CacheAvailabilityInterface.php +++ b/src/CacheAvailabilityInterface.php @@ -9,5 +9,5 @@ interface CacheAvailabilityInterface * Return if this CacheEngine is available for use * @return bool */ - public function isAvailable(); + public function isAvailable(): bool; } diff --git a/src/CacheLockInterface.php b/src/CacheLockInterface.php index 0fecb31..e6ce2aa 100644 --- a/src/CacheLockInterface.php +++ b/src/CacheLockInterface.php @@ -14,11 +14,11 @@ interface CacheLockInterface * Lock resource before set it. * @param string $key */ - public function lock($key); + public function lock(string $key): void; /** * Unlock resource * @param string $key */ - public function unlock($key); + public function unlock(string $key): void; } diff --git a/src/Factory.php b/src/Factory.php index 074e3b5..6e1858d 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -10,17 +10,18 @@ use ByJG\Cache\Psr16\SessionCacheEngine; use ByJG\Cache\Psr16\ShmopCacheEngine; use ByJG\Cache\Psr6\CachePool; +use Psr\Log\LoggerInterface; class Factory { - public static function createNullPool() + public static function createNullPool(): CachePool { return new CachePool( new NoCacheEngine() ); } - public static function createSessionPool($prefix = null, $bufferSize = null) + public static function createSessionPool(string $prefix = 'cache', int $bufferSize = 10): CachePool { return new CachePool( new SessionCacheEngine($prefix), @@ -28,15 +29,15 @@ public static function createSessionPool($prefix = null, $bufferSize = null) ); } - public static function createFilePool($prefix = null, $path = null, $bufferSize = null, $logger = null) + public static function createFilePool(string $prefix = 'cache', ?string $path = null, int $bufferSize = 10, ?LoggerInterface $logger = null, bool $createPath = false): CachePool { return new CachePool( - new FileSystemCacheEngine($prefix, $path, $logger), + new FileSystemCacheEngine($prefix, $path, $logger, $createPath), $bufferSize ); } - public static function createShmopPool($config = [], $bufferSize = null, $logger = null) + public static function createShmopPool(array $config = [], int $bufferSize = 10, ?LoggerInterface $logger = null): CachePool { return new CachePool( new ShmopCacheEngine($config, $logger), @@ -44,7 +45,7 @@ public static function createShmopPool($config = [], $bufferSize = null, $logger ); } - public static function createArrayPool($bufferSize = null, $logger = null) + public static function createArrayPool(int $bufferSize = 10, ?LoggerInterface $logger = null): CachePool { return new CachePool( new ArrayCacheEngine($logger), @@ -52,7 +53,7 @@ public static function createArrayPool($bufferSize = null, $logger = null) ); } - public static function createMemcachedPool($servers = null, $bufferSize = null, $logger = null) + public static function createMemcachedPool(?array $servers = null, int $bufferSize = 10, ?LoggerInterface $logger = null): CachePool { return new CachePool( new MemcachedEngine($servers, $logger), @@ -60,7 +61,7 @@ public static function createMemcachedPool($servers = null, $bufferSize = null, ); } - public static function createRedisCacheEngine($servers = null, $password = null, $bufferSize = null, $logger = null) + public static function createRedisCacheEngine(?string $servers = null, ?string $password = null, int $bufferSize = 10, ?LoggerInterface $logger = null): CachePool { return new CachePool( new RedisCacheEngine($servers, $password, $logger), diff --git a/src/Psr16/ArrayCacheEngine.php b/src/Psr16/ArrayCacheEngine.php index 5c7b12b..67aa01f 100644 --- a/src/Psr16/ArrayCacheEngine.php +++ b/src/Psr16/ArrayCacheEngine.php @@ -2,17 +2,21 @@ namespace ByJG\Cache\Psr16; +use ByJG\Cache\Exception\InvalidArgumentException; use DateInterval; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; class ArrayCacheEngine extends BaseCacheEngine { - protected $cache = array(); + protected array $cache = []; - protected $logger = null; + protected LoggerInterface|null $logger = null; - public function __construct($logger = null) + public function __construct(LoggerInterface|null $logger = null) { $this->logger = $logger; if (is_null($logger)) { @@ -29,10 +33,11 @@ public function __construct($logger = null) * * @param string $key The cache item key. * @return bool - * @throws \Psr\SimpleCache\InvalidArgumentException - * MUST be thrown if the $key string is not a legal value. + * @throws InvalidArgumentException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ - public function has($key) + public function has(string $key): bool { $key = $this->getKeyFromContainer($key); if (isset($this->cache[$key])) { @@ -51,9 +56,11 @@ public function has($key) * @param string $key The object KEY * @param mixed $default IGNORED IN MEMCACHED. * @return mixed Description - * @throws \Psr\SimpleCache\InvalidArgumentException + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface */ - public function get($key, $default = null) + public function get(string $key, mixed $default = null): mixed { if ($this->has($key)) { $key = $this->getKeyFromContainer($key); @@ -78,7 +85,7 @@ public function get($key, $default = null) * * MUST be thrown if the $key string is not a legal value. */ - public function set($key, $value, $ttl = null) + public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool { $key = $this->getKeyFromContainer($key); @@ -92,9 +99,10 @@ public function set($key, $value, $ttl = null) return true; } - public function clear() + public function clear(): bool { $this->cache = []; + return true; } /** @@ -103,7 +111,7 @@ public function clear() * @param string $key * @return bool */ - public function delete($key) + public function delete(string $key): bool { $key = $this->getKeyFromContainer($key); @@ -112,7 +120,7 @@ public function delete($key) return true; } - public function isAvailable() + public function isAvailable(): bool { return true; } diff --git a/src/Psr16/BaseCacheEngine.php b/src/Psr16/BaseCacheEngine.php index f558e99..0c10629 100644 --- a/src/Psr16/BaseCacheEngine.php +++ b/src/Psr16/BaseCacheEngine.php @@ -6,7 +6,9 @@ use ByJG\Cache\Exception\InvalidArgumentException; use DateInterval; use DateTime; +use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; +use Psr\Container\NotFoundExceptionInterface; use Psr\SimpleCache\CacheInterface; abstract class BaseCacheEngine implements CacheInterface, CacheAvailabilityInterface @@ -14,16 +16,17 @@ abstract class BaseCacheEngine implements CacheInterface, CacheAvailabilityInter protected ?ContainerInterface $container; /** - * @param $keys - * @param null $default - * @return array|iterable + * @param string|iterable $keys + * @param mixed $default + * @return iterable * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function getMultiple($keys, $default = null) + public function getMultiple(string|iterable $keys, mixed $default = null): iterable { - if (!is_array($keys)) { - throw new InvalidArgumentException('getMultipleKeys expected an array'); + if (is_string($keys)) { + $keys = [$keys]; } + $result = []; foreach ($keys as $key) { $result[$key] = $this->get($key, $default); @@ -33,32 +36,34 @@ public function getMultiple($keys, $default = null) /** * @param iterable $values - * @param null $ttl - * @return bool|void + * @param DateInterval|int|null $ttl + * @return bool * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function setMultiple($values, $ttl = null) + public function setMultiple(iterable $values, DateInterval|int|null $ttl = null): bool { foreach ($values as $key => $value) { $this->set($key, $value, $ttl); } + return true; } /** * @param iterable $keys - * @return bool|void + * @return bool * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function deleteMultiple($keys) + public function deleteMultiple(iterable $keys): bool { foreach ($keys as $key) { $this->delete($key); } + return true; } - abstract public function isAvailable(); + abstract public function isAvailable(): bool; - protected function addToNow($ttl) + protected function addToNow(DateInterval|int|null $ttl): int|null { if (is_numeric($ttl)) { return strtotime("+$ttl second"); @@ -73,21 +78,25 @@ protected function addToNow($ttl) return null; } - protected function convertToSeconds($ttl) + /** + * @throws InvalidArgumentException + */ + protected function convertToSeconds(DateInterval|int|null $ttl): DateInterval|int|null { if (empty($ttl) || is_numeric($ttl)) { return $ttl; } - if ($ttl instanceof DateInterval) { - return $ttl->days*86400 + $ttl->h*3600 + $ttl->i*60 + $ttl->s; - } - - throw new InvalidArgumentException('Invalid TTL'); + return $ttl->days*86400 + $ttl->h*3600 + $ttl->i*60 + $ttl->s; } - protected function getKeyFromContainer($key) + /** + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface + */ + protected function getKeyFromContainer(string $key): mixed { if (empty($this->container)) { return $key; diff --git a/src/Psr16/FileSystemCacheEngine.php b/src/Psr16/FileSystemCacheEngine.php index 819ab11..3580446 100644 --- a/src/Psr16/FileSystemCacheEngine.php +++ b/src/Psr16/FileSystemCacheEngine.php @@ -5,20 +5,27 @@ use ByJG\Cache\CacheLockInterface; use DateInterval; use Exception; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; +use Psr\SimpleCache\InvalidArgumentException; class FileSystemCacheEngine extends BaseCacheEngine implements CacheLockInterface { - protected $logger = null; + protected ?LoggerInterface $logger = null; - protected $prefix = null; - protected $path = null; + protected ?string $prefix = null; + protected ?string $path = null; - public function __construct($prefix = 'cache', $path = null, $logger = null) + public function __construct(string $prefix = 'cache', ?string $path = null, ?LoggerInterface $logger = null, bool $createPath = false) { $this->prefix = $prefix; $this->path = $path ?? sys_get_temp_dir(); + if ($createPath && !file_exists($this->path)) { + mkdir($this->path, 0777, true); + } $this->logger = $logger; if (is_null($logger)) { @@ -30,9 +37,11 @@ public function __construct($prefix = 'cache', $path = null, $logger = null) * @param string $key The object KEY * @param mixed $default IGNORED IN MEMCACHED. * @return mixed Description - * @throws \Psr\SimpleCache\InvalidArgumentException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \ByJG\Cache\Exception\InvalidArgumentException */ - public function get($key, $default = null) + public function get(string $key, mixed $default = null): mixed { // Check if file is Locked $fileKey = $this->fixKey($key); @@ -78,7 +87,7 @@ public function get($key, $default = null) * * MUST be thrown if the $key string is not a legal value. */ - public function set($key, $value, $ttl = null) + public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool { $fileKey = $this->fixKey($key); @@ -104,7 +113,7 @@ public function set($key, $value, $ttl = null) $validUntil = $this->addToNow($ttl); if (!empty($validUntil)) { - file_put_contents($fileKey . ".ttl", $validUntil); + file_put_contents($fileKey . ".ttl", (string)$validUntil); } } catch (Exception $ex) { $this->logger->warning("[Filesystem cache] I could not write to cache on file '" . basename($key) . "'. Switching to nocache=true mode."); @@ -117,9 +126,8 @@ public function set($key, $value, $ttl = null) /** * @param string $key * @return bool - * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function delete($key) + public function delete(string $key): bool { $this->set($key, null); return true; @@ -129,7 +137,7 @@ public function delete($key) * Lock resource before set it. * @param string $key */ - public function lock($key) + public function lock(string $key): void { $this->logger->info("[Filesystem cache] Lock '$key'"); @@ -146,7 +154,7 @@ public function lock($key) * UnLock resource after set it. * @param string $key */ - public function unlock($key) + public function unlock(string $key): void { $this->logger->info("[Filesystem cache] Unlock '$key'"); @@ -158,12 +166,22 @@ public function unlock($key) } } - public function isAvailable() + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \ByJG\Cache\Exception\InvalidArgumentException + */ + public function isAvailable(): bool { return is_writable(dirname($this->fixKey('test'))); } - protected function fixKey($key) + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \ByJG\Cache\Exception\InvalidArgumentException + */ + protected function fixKey(string $key): string { $key = $this->getKeyFromContainer($key); @@ -177,8 +195,11 @@ protected function fixKey($key) * Wipes clean the entire cache's keys. * * @return bool True on success and false on failure. + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \ByJG\Cache\Exception\InvalidArgumentException */ - public function clear() + public function clear(): bool { $patternKey = $this->fixKey('*'); $list = glob($patternKey); @@ -197,10 +218,11 @@ public function clear() * * @param string $key The cache item key. * @return bool - * @throws \Psr\SimpleCache\InvalidArgumentException - * MUST be thrown if the $key string is not a legal value. + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \ByJG\Cache\Exception\InvalidArgumentException */ - public function has($key) + public function has(string $key): bool { $fileKey = $this->fixKey($key); if (file_exists($fileKey)) { diff --git a/src/Psr16/MemcachedEngine.php b/src/Psr16/MemcachedEngine.php index af9eb6f..299092a 100644 --- a/src/Psr16/MemcachedEngine.php +++ b/src/Psr16/MemcachedEngine.php @@ -2,9 +2,13 @@ namespace ByJG\Cache\Psr16; +use ByJG\Cache\Exception\InvalidArgumentException; use ByJG\Cache\Exception\StorageErrorException; use DateInterval; use Memcached; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; class MemcachedEngine extends BaseCacheEngine @@ -12,15 +16,15 @@ class MemcachedEngine extends BaseCacheEngine /** * - * @var Memcached + * @var Memcached|null */ - protected $memCached = null; + protected Memcached|null $memCached = null; - protected $logger = null; + protected LoggerInterface|null $logger = null; - protected $servers = null; + protected ?array $servers = null; - public function __construct($servers = null, $logger = null) + public function __construct(?array $servers = null, $logger = null) { $this->servers = (array)$servers; if (is_null($servers)) { @@ -35,7 +39,13 @@ public function __construct($servers = null, $logger = null) } } - protected function fixKey($key) { + /** + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface + */ + protected function fixKey(string $key): string + { $key = $this->getKeyFromContainer($key); return "cache-" . $key; } @@ -43,13 +53,13 @@ protected function fixKey($key) { /** * @throws StorageErrorException */ - protected function lazyLoadMemCachedServers() + protected function lazyLoadMemCachedServers(): void { if (is_null($this->memCached)) { $this->memCached = new Memcached(); foreach ($this->servers as $server) { $data = explode(":", $server); - $this->memCached->addServer($data[0], $data[1]); + $this->memCached->addServer($data[0], intval($data[1])); $stats = $this->memCached->getStats(); if (!isset($stats[$server]) || $stats[$server]['pid'] === -1) { @@ -60,12 +70,15 @@ protected function lazyLoadMemCachedServers() } /** - * @param string $key The object KEY - * @param int $default IGNORED IN MEMCACHED. - * @return mixed Description + * @param string $key + * @param mixed|null $default + * @return mixed + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface * @throws StorageErrorException */ - public function get($key, $default = null) + public function get(string $key, mixed $default = null): mixed { $this->lazyLoadMemCachedServers(); @@ -83,9 +96,12 @@ public function get($key, $default = null) * @param mixed $value The object to be cached * @param DateInterval|int|null $ttl The time to live in seconds of this objects * @return bool If the object is successfully posted + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface * @throws StorageErrorException */ - public function set($key, $value, $ttl = null) + public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool { $this->lazyLoadMemCachedServers(); @@ -103,9 +119,12 @@ public function set($key, $value, $ttl = null) /** * @param string $key * @return bool + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface * @throws StorageErrorException */ - public function delete($key) + public function delete(string $key): bool { $this->lazyLoadMemCachedServers(); @@ -113,7 +132,7 @@ public function delete($key) return true; } - public function isAvailable() + public function isAvailable(): bool { if (!class_exists('\Memcached')) { return false; @@ -131,7 +150,7 @@ public function isAvailable() * @return bool * @throws StorageErrorException */ - public function clear() + public function clear(): bool { $this->lazyLoadMemCachedServers(); $result = $this->memCached->flush(); @@ -141,9 +160,12 @@ public function clear() /** * @param string $key * @return bool + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface * @throws StorageErrorException */ - public function has($key) + public function has(string $key): bool { $this->lazyLoadMemCachedServers(); diff --git a/src/Psr16/NoCacheEngine.php b/src/Psr16/NoCacheEngine.php index f4af906..c384277 100644 --- a/src/Psr16/NoCacheEngine.php +++ b/src/Psr16/NoCacheEngine.php @@ -3,27 +3,37 @@ namespace ByJG\Cache\Psr16; use ByJG\Cache\CacheLockInterface; +use ByJG\Cache\Exception\InvalidArgumentException; +use DateInterval; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; class NoCacheEngine extends BaseCacheEngine implements CacheLockInterface { /** - * @param string $key The object KEY - * @param int $default IGNORED IN MEMCACHED. - * @return mixed Description + * @param string $key + * @param mixed $default + * @return mixed + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface */ - public function get($key, $default = null) + public function get(string $key, mixed $default = null): mixed { $key = $this->getKeyFromContainer($key); return $default; } /** - * @param string $key The object Key - * @param object $value The object to be cached - * @param int $ttl The time to live in seconds of this objects - * @return bool If the object is successfully posted + * @param string $key + * @param mixed $value + * @param DateInterval|int|null $ttl + * @return bool + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface */ - public function set($key, $value, $ttl = 0) + public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool { $key = $this->getKeyFromContainer($key); return true; @@ -32,8 +42,11 @@ public function set($key, $value, $ttl = 0) /** * @param string $key * @return bool + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface */ - public function delete($key) + public function delete(string $key): bool { $key = $this->getKeyFromContainer($key); return true; @@ -43,7 +56,7 @@ public function delete($key) * Lock resource before set it. * @param string $key */ - public function lock($key) + public function lock(string $key): void { return; } @@ -52,12 +65,12 @@ public function lock($key) * UnLock resource after set it * @param string $key */ - public function unlock($key) + public function unlock(string $key): void { return; } - public function isAvailable() + public function isAvailable(): bool { return true; } @@ -67,7 +80,7 @@ public function isAvailable() * * @return bool True on success and false on failure. */ - public function clear() + public function clear(): bool { return true; } @@ -81,10 +94,11 @@ public function clear() * * @param string $key The cache item key. * @return bool - * @throws \Psr\SimpleCache\InvalidArgumentException - * MUST be thrown if the $key string is not a legal value. - */ - public function has($key) + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface + */ + public function has(string $key): bool { $key = $this->getKeyFromContainer($key); return false; diff --git a/src/Psr16/RedisCacheEngine.php b/src/Psr16/RedisCacheEngine.php index ff896a8..703b2b9 100644 --- a/src/Psr16/RedisCacheEngine.php +++ b/src/Psr16/RedisCacheEngine.php @@ -2,24 +2,31 @@ namespace ByJG\Cache\Psr16; +use ByJG\Cache\Exception\InvalidArgumentException; +use DateInterval; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; +use Redis; +use RedisException; class RedisCacheEngine extends BaseCacheEngine { /** * - * @var \Redis + * @var Redis */ - protected $redis = null; + protected ?Redis $redis = null; - protected $logger = null; + protected LoggerInterface|null $logger = null; - protected $server = null; + protected ?string $server = null; - protected $password = null; + protected ?string $password = null; - public function __construct($server = null, $password = null, $logger = null) + public function __construct(?string $server = null, ?string $password = null, ?LoggerInterface $logger = null) { $this->server = $server; if (is_null($server)) { @@ -34,33 +41,46 @@ public function __construct($server = null, $password = null, $logger = null) } } - protected function lazyLoadRedisServer() + /** + * @throws RedisException + */ + protected function lazyLoadRedisServer(): void { if (is_null($this->redis)) { - $this->redis = new \Redis(); + $this->redis = new Redis(); $data = explode(":", $this->server); - $this->redis->connect($data[0], isset($data[1]) ? $data[1] : 6379); + $this->redis->connect($data[0], intval($data[1] ?? 6379)); if (!empty($this->password)) { $this->redis->auth($this->password); } - $this->redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_NONE); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); $this->redis->info('redis_version'); } } - protected function fixKey($key) { + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws InvalidArgumentException + */ + protected function fixKey(string $key): string + { $key = $this->getKeyFromContainer($key); return "cache:$key"; } /** - * @param string $key The object KEY - * @param int $default IGNORED IN MEMCACHED. - * @return mixed Description + * @param string $key + * @param mixed $default + * @return mixed + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface + * @throws RedisException */ - public function get($key, $default = null) + public function get(string $key, mixed $default = null): mixed { $this->lazyLoadRedisServer(); @@ -71,12 +91,16 @@ public function get($key, $default = null) } /** - * @param string $key The object Key - * @param object $value The object to be cached - * @param int $ttl The time to live in seconds of this objects - * @return bool If the object is successfully posted + * @param string $key + * @param mixed $value + * @param DateInterval|int|null $ttl + * @return bool + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface + * @throws RedisException */ - public function set($key, $value, $ttl = null) + public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool { $this->lazyLoadRedisServer(); @@ -88,7 +112,13 @@ public function set($key, $value, $ttl = null) return true; } - public function delete($key) + /** + * @throws NotFoundExceptionInterface + * @throws InvalidArgumentException + * @throws RedisException + * @throws ContainerExceptionInterface + */ + public function delete(string $key): bool { $this->lazyLoadRedisServer(); @@ -97,7 +127,13 @@ public function delete($key) return true; } - public function clear() + /** + * @throws NotFoundExceptionInterface + * @throws InvalidArgumentException + * @throws RedisException + * @throws ContainerExceptionInterface + */ + public function clear(): bool { $keys = $this->redis->keys('cache:*'); foreach ((array)$keys as $key) { @@ -105,9 +141,16 @@ public function clear() $this->delete($matches['key']); } } + return true; } - public function has($key) + /** + * @throws NotFoundExceptionInterface + * @throws InvalidArgumentException + * @throws RedisException + * @throws ContainerExceptionInterface + */ + public function has(string $key): bool { $result = $this->redis->exists($this->fixKey($key)); @@ -115,10 +158,14 @@ public function has($key) return $result !== 0; } + if ($result instanceof Redis) { + return true; + } + return $result; } - public function isAvailable() + public function isAvailable(): bool { if (!class_exists('\Redis')) { return false; diff --git a/src/Psr16/SessionCacheEngine.php b/src/Psr16/SessionCacheEngine.php index 9c5fa01..6b69cc1 100644 --- a/src/Psr16/SessionCacheEngine.php +++ b/src/Psr16/SessionCacheEngine.php @@ -2,36 +2,52 @@ namespace ByJG\Cache\Psr16; +use ByJG\Cache\Exception\InvalidArgumentException; +use DateInterval; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + class SessionCacheEngine extends BaseCacheEngine { - protected $prefix = null; + protected string $prefix; /** * SessionCacheEngine constructor. * * @param string $prefix */ - public function __construct($prefix = 'cache') + public function __construct(string $prefix = 'cache') { $this->prefix = $prefix; } - protected function checkSession() + protected function checkSession(): void { if (session_status() == PHP_SESSION_NONE) { session_start(); } } - protected function keyName($key) + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws InvalidArgumentException + */ + protected function keyName($key): string { $key = $this->getKeyFromContainer($key); return $this->prefix . '-' . $key; } - public function get($key, $default = null) + /** + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface + * @throws ContainerExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function get(string $key, mixed $default = null): mixed { $this->checkSession(); @@ -44,7 +60,12 @@ public function get($key, $default = null) } } - public function delete($key) + /** + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface + */ + public function delete(string $key): bool { $this->checkSession(); @@ -56,9 +77,11 @@ public function delete($key) if (isset($_SESSION["$keyName.ttl"])) { unset($_SESSION["$keyName.ttl"]); } + + return true; } - public function set($key, $value, $ttl = null) + public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool { $this->checkSession(); @@ -67,14 +90,17 @@ public function set($key, $value, $ttl = null) if (!empty($ttl)) { $_SESSION["$keyName.ttl"] = $this->addToNow($ttl); } + + return true; } - public function clear() + public function clear(): bool { session_destroy(); + return true; } - public function has($key) + public function has(string $key): bool { $keyName = $this->keyName($key); @@ -90,7 +116,7 @@ public function has($key) return false; } - public function isAvailable() + public function isAvailable(): bool { try { $this->checkSession(); diff --git a/src/Psr16/ShmopCacheEngine.php b/src/Psr16/ShmopCacheEngine.php index 5fd1528..d2ea023 100644 --- a/src/Psr16/ShmopCacheEngine.php +++ b/src/Psr16/ShmopCacheEngine.php @@ -4,6 +4,10 @@ use ByJG\Cache\Exception\InvalidArgumentException; use ByJG\Cache\Exception\StorageErrorException; +use DateInterval; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; /** @@ -23,11 +27,11 @@ */ class ShmopCacheEngine extends BaseCacheEngine { - protected $logger = null; + protected LoggerInterface|null $logger = null; - protected $config = []; + protected array $config = []; - public function __construct($config = [], $logger = null) + public function __construct(array $config = [], ?LoggerInterface $logger = null) { $this->config = $config; @@ -44,7 +48,12 @@ public function __construct($config = [], $logger = null) } } - protected function getFilenameToken($key) + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws InvalidArgumentException + */ + protected function getFilenameToken(string $key): string { $key = $this->getKeyFromContainer($key); return sys_get_temp_dir() . '/shmop-' . sha1($key) . '.cache'; @@ -60,7 +69,7 @@ protected function getDefaultPermission() return $this->config['default-permission']; } - protected function getFTok($file) + protected function getFTok(string $file): int { if (!file_exists($file)) { touch($file); @@ -72,8 +81,11 @@ protected function getFTok($file) * @param string $key The object KEY * @param mixed $default The time to live in seconds of the object. Depends on implementation. * @return mixed The Object + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface */ - public function get($key, $default = null) + public function get(string $key, mixed $default = null): mixed { if ($default === false) { $this->logger->info("[Shmop Cache] Ignored $key because TTL=FALSE"); @@ -102,7 +114,7 @@ public function get($key, $default = null) return unserialize($serialized); } - protected function isValidAge($file) + protected function isValidAge(string $file): bool { if (file_exists("$file.ttl")) { $fileTtl = intval(file_get_contents("$file.ttl")); @@ -126,10 +138,12 @@ protected function isValidAge($file) * the driver supports TTL then the library may set a default value * for it or let the driver take care of that. * @return bool True on success and false on failure. + * @throws ContainerExceptionInterface * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface * @throws StorageErrorException */ - public function set($key, $value, $ttl = null) + public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool { $this->logger->info("[Shmop Cache] set '$key'"); @@ -163,7 +177,7 @@ public function set($key, $value, $ttl = null) $validUntil = $this->addToNow($ttl); if (!empty($validUntil)) { - file_put_contents("$file.ttl", $validUntil); + file_put_contents("$file.ttl", (string)$validUntil); } return true; @@ -172,8 +186,11 @@ public function set($key, $value, $ttl = null) /** * @param string $key * @return bool + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface */ - public function delete($key) + public function delete(string $key): bool { $this->logger->info("[Shmop Cache] release '$key'"); @@ -187,7 +204,7 @@ public function delete($key) return true; } - private function deleteFromFilenameToken($file) + private function deleteFromFilenameToken(string $file): void { $filekey = $this->getFTok($file); $shm_id = @shmop_open($filekey, "w", 0, 0); @@ -208,16 +225,22 @@ private function deleteFromFilenameToken($file) } } - public function clear() + public function clear(): bool { $patternKey = sys_get_temp_dir() . '/shmop-*.cache'; $list = glob($patternKey); foreach ($list as $file) { $this->deleteFromFilenameToken($file); } + return true; } - public function has($key) + /** + * @throws ContainerExceptionInterface + * @throws InvalidArgumentException + * @throws NotFoundExceptionInterface + */ + public function has(string $key): bool { $file = $this->getFilenameToken($key); $fileKey = $this->getFTok($file); @@ -232,11 +255,11 @@ public function has($key) return $this->isValidAge($file); } - return $exists; + return false; } - public function isAvailable() + public function isAvailable(): bool { return function_exists('shmop_open'); } diff --git a/src/Psr6/CacheItem.php b/src/Psr6/CacheItem.php index 6f4aaa9..fa809d6 100644 --- a/src/Psr6/CacheItem.php +++ b/src/Psr6/CacheItem.php @@ -12,22 +12,22 @@ class CacheItem implements CacheItemInterface /** * @var string */ - protected $key; + protected string $key; /** * @var mixed */ - protected $value; + protected mixed $value; /** * @var boolean */ - protected $hit; + protected bool $hit; /** * @var DateTime */ - protected $expiration; + protected DateTimeInterface $expiration; /** * CacheItem constructor. @@ -35,7 +35,7 @@ class CacheItem implements CacheItemInterface * @param mixed $value * @param bool $hit */ - public function __construct($key, $value, $hit = true) + public function __construct(string $key, mixed $value, bool $hit = true) { $this->key = $key; $this->value = $value; @@ -46,7 +46,7 @@ public function __construct($key, $value, $hit = true) /** * {@inheritdoc} */ - public function getKey() + public function getKey(): string { return $this->key; } @@ -54,14 +54,14 @@ public function getKey() /** * {@inheritdoc} */ - public function get() + public function get(): mixed { return $this->isHit() ? $this->value : null; } /** * {@inheritdoc} */ - public function set($value = null) + public function set(mixed $value = null): static { $this->value = $value; $this->hit = !is_null($value); @@ -70,25 +70,27 @@ public function set($value = null) /** * {@inheritdoc} */ - public function isHit() + public function isHit(): bool { return $this->hit; } /** * {@inheritdoc} */ - public function expiresAt($expiration) + public function expiresAt(?DateTimeInterface $expiration): static { - $this->expiration = new DateTime('now +1 year'); - if ($expiration instanceof DateTimeInterface) { - $this->expiration = $expiration; + if (empty($expiration)) { + $this->expiration = new DateTime('now +1 year'); + return $this; } + + $this->expiration = $expiration; return $this; } /** * {@inheritdoc} */ - public function expiresAfter($time) + public function expiresAfter(int|\DateInterval|null $time): static { $this->expiration = new DateTime('now +1 year'); if (is_numeric($time)) { @@ -104,12 +106,12 @@ public function expiresAfter($time) /** * @return DateTime */ - public function getExpiresAt() + public function getExpiresAt(): DateTime { return $this->expiration; } - public function getExpiresInSecs() + public function getExpiresInSecs(): int { return $this->getExpiresAt()->getTimestamp() - (new DateTime('now'))->getTimestamp(); } diff --git a/src/Psr6/CachePool.php b/src/Psr6/CachePool.php index 2f9b92a..e3141ce 100644 --- a/src/Psr6/CachePool.php +++ b/src/Psr6/CachePool.php @@ -6,33 +6,34 @@ use ByJG\Cache\Psr16\BaseCacheEngine; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; +use Psr\SimpleCache\CacheInterface; class CachePool implements CacheItemPoolInterface { /** - * @var \Psr\SimpleCache\CacheInterface + * @var BaseCacheEngine */ - protected $_cacheEngine; + protected BaseCacheEngine $_cacheEngine; /** * @var CacheItem */ - protected $_lastCacheItem; + protected CacheItem $_lastCacheItem; /** * @var int */ - protected $bufferSize = 10; + protected int $bufferSize = 10; /** * @var CacheItem[] */ - protected $buffer = []; + protected array $buffer = []; /** * @var array */ - protected $bufferKeys = []; + protected array $bufferKeys = []; /** * CachePool constructor. @@ -40,16 +41,16 @@ class CachePool implements CacheItemPoolInterface * @param BaseCacheEngine $_cacheEngine * @param int $bufferSize */ - public function __construct(BaseCacheEngine $_cacheEngine, $bufferSize = 10) + public function __construct(BaseCacheEngine $_cacheEngine, int $bufferSize = 10) { $this->_cacheEngine = $_cacheEngine; - $this->bufferSize = intval($bufferSize); + $this->bufferSize = $bufferSize; } /** * @return int */ - public function getBufferSize() + public function getBufferSize(): int { return $this->bufferSize; } @@ -57,7 +58,7 @@ public function getBufferSize() /** * @param int $bufferSize */ - public function setBufferSize($bufferSize) + public function setBufferSize(int $bufferSize): void { $this->bufferSize = $bufferSize; } @@ -68,7 +69,7 @@ public function setBufferSize($bufferSize) * * @param CacheItem $cacheItem */ - protected function addElementToBuffer(CacheItem $cacheItem) + protected function addElementToBuffer(CacheItem $cacheItem): void { if ($this->bufferSize < 1) { return; @@ -94,7 +95,7 @@ protected function addElementToBuffer(CacheItem $cacheItem) * * @param $key */ - protected function removeElementFromBuffer($key) + protected function removeElementFromBuffer(string $key): void { $result = array_search($key, $this->bufferKeys); if ($result === false) { @@ -113,7 +114,7 @@ protected function removeElementFromBuffer($key) * @throws \Psr\SimpleCache\InvalidArgumentException * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function getItem($key) + public function getItem(string $key): CacheItemInterface { // Get the element from the buffer if still remains valid! if (in_array($key, $this->bufferKeys)) { @@ -139,7 +140,7 @@ public function getItem($key) * @return array * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function getItems(array $keys = array()) + public function getItems(array $keys = array()): iterable { $result = []; foreach ($keys as $key) { @@ -156,7 +157,7 @@ public function getItems(array $keys = array()) * @return bool * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function hasItem($key) + public function hasItem(string $key): bool { return $this->getItem($key)->isHit(); } @@ -164,11 +165,12 @@ public function hasItem($key) /** * Psr implementation of clear() */ - public function clear() + public function clear(): bool { $this->_cacheEngine->clear(); $this->bufferKeys = []; $this->buffer = []; + return true; } /** @@ -178,7 +180,7 @@ public function clear() * @return bool * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function deleteItem($key) + public function deleteItem(string $key): bool { return $this->deleteItems([$key]); } @@ -191,7 +193,7 @@ public function deleteItem($key) * @throws \Psr\SimpleCache\InvalidArgumentException * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function deleteItems(array $keys) + public function deleteItems(array $keys): bool { foreach ($keys as $key) { $this->_cacheEngine->delete($key); @@ -206,7 +208,7 @@ public function deleteItems(array $keys) * @return bool * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function save(CacheItemInterface $item) + public function save(CacheItemInterface $item): bool { if (!($item instanceof CacheItem)) { throw new InvalidArgumentException('The cache item must be an implementation of \ByJG\Cache\Psr\CacheItem'); @@ -225,7 +227,7 @@ public function save(CacheItemInterface $item) /** * @var CacheItem[] */ - protected $deferredItem = []; + protected array $deferredItem = []; /** * Psr Implementation of saveDeferred() @@ -233,7 +235,7 @@ public function save(CacheItemInterface $item) * @param CacheItemInterface $item * @return bool */ - public function saveDeferred(CacheItemInterface $item) + public function saveDeferred(CacheItemInterface $item): bool { $this->deferredItem[] = $item; return true; @@ -244,16 +246,17 @@ public function saveDeferred(CacheItemInterface $item) * * @throws \Psr\SimpleCache\InvalidArgumentException */ - public function commit() + public function commit(): bool { foreach ($this->deferredItem as $item) { $this->save($item); } $this->deferredItem = []; + return true; } - public function isAvailable() + public function isAvailable(): bool { return $this->_cacheEngine->isAvailable(); } diff --git a/tests/BaseCacheTest.php b/tests/BaseCacheTest.php index a0099be..ac24d6e 100644 --- a/tests/BaseCacheTest.php +++ b/tests/BaseCacheTest.php @@ -1,11 +1,9 @@ isAvailable()) { // First time - $items = $cacheEngine->getMultiple(['chave1', 'chave2']); + $items = [...$cacheEngine->getMultiple(['chave1', 'chave2'])]; $this->assertNull($items['chave1']); $this->assertNull($items['chave2']); - $items = $cacheEngine->getMultiple(['chave1', 'chave2'], 'default'); + $items = [...$cacheEngine->getMultiple(['chave1', 'chave2'], 'default')]; $this->assertEquals('default', $items['chave1']); $this->assertEquals('default', $items['chave2']); @@ -73,7 +68,7 @@ public function testGetMultipleItems(BaseCacheEngine $cacheEngine) // Get Object if (!($cacheEngine instanceof NoCacheEngine)) { - $item2 = $cacheEngine->getMultiple(['chave1', 'chave2']); + $item2 = [...$cacheEngine->getMultiple(['chave1', 'chave2'])]; $this->assertEquals('valor1', $item2['chave1']); $this->assertEquals('valor2', $item2['chave2']); } @@ -82,7 +77,7 @@ public function testGetMultipleItems(BaseCacheEngine $cacheEngine) $cacheEngine->deleteMultiple(['chave1', 'chave2']); // Check Removed - $items = $cacheEngine->getMultiple(['chave1', 'chave2']); + $items = [...$cacheEngine->getMultiple(['chave1', 'chave2'])]; $this->assertNull($items['chave1']); $this->assertNull($items['chave2']); } else { @@ -92,7 +87,7 @@ public function testGetMultipleItems(BaseCacheEngine $cacheEngine) /** * @dataProvider CachePoolProvider - * @param \ByJG\Cache\Psr16\BaseCacheEngine $cacheEngine + * @param BaseCacheEngine $cacheEngine * @throws \Psr\SimpleCache\InvalidArgumentException */ public function testTtl(BaseCacheEngine $cacheEngine) @@ -129,7 +124,7 @@ public function testTtl(BaseCacheEngine $cacheEngine) /** * @dataProvider CachePoolProvider - * @param \ByJG\Cache\Psr16\BaseCacheEngine $cacheEngine + * @param BaseCacheEngine $cacheEngine * @throws \Psr\SimpleCache\InvalidArgumentException */ public function testCacheObject(BaseCacheEngine $cacheEngine) @@ -162,8 +157,7 @@ public function testCacheObject(BaseCacheEngine $cacheEngine) /** * @dataProvider CachePoolProvider - * @param \ByJG\Cache\Psr16\BaseCacheEngine $cacheEngine - * @throws \ByJG\Cache\InvalidArgumentException + * @param BaseCacheEngine $cacheEngine * @throws \Psr\SimpleCache\InvalidArgumentException */ public function testClear(BaseCacheEngine $cacheEngine) diff --git a/tests/CachePSR6Test.php b/tests/CachePSR6Test.php index 36bb268..649be98 100644 --- a/tests/CachePSR6Test.php +++ b/tests/CachePSR6Test.php @@ -1,13 +1,11 @@