diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index bfe0e6c..55d4bc2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -71,8 +71,12 @@ jobs: # Step 7 - name: Check code style (PSR-12) - run: composer cs + run: composer cs:check # Step 8: Run entire tests suite - name: Run all tests run: composer test + + # Step 9: Static code analysis + - name: Run static code analysis + run: composer stan-dist \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1190cfc..1c155fa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ composer.lock /.phpunit.cache .phpunit.result.cache /.php-cs-fixer.cache +phpstan.neon /src/redis.php /src/redisTestCLI.php -/vendor +/vendor \ No newline at end of file diff --git a/LICENSE b/LICENSE index 36e08f5..ce44058 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017-2025 Laurent LEGAZ +Copyright (c) 2017-2026 Laurent LEGAZ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/composer.json b/composer.json index 0a240a8..1c44c80 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,8 @@ "phpunit/phpunit": "^10.5", "symfony/var-dumper": "~6.4", "friendsofphp/php-cs-fixer": "~3.3", - "cache/integration-tests": "dev-master" + "cache/integration-tests": "dev-master", + "phpstan/phpstan": "^2.1" }, "suggest": { "ext-redis": "^5.3" @@ -46,13 +47,16 @@ } }, "scripts": { + "cs":"@phpcsfixer", "pu":"@phpunit", "puv":"@phpunit-verbose", "pui":"@phpunit-int-suite", "puiv":"@phpunit-int-verbose", "puf":"@phpunit-func-suite", "pufv":"@phpunit-func-verbose", - "cs":"@phpcsfixer", + "cs:check": "@phpcsfixer --dry-run --format=txt --verbose --diff --ansi --allow-unsupported-php-version=yes", + "stan":"vendor/bin/phpstan analyse -c phpstan.neon", + "stan-dist":"vendor/bin/phpstan analyse -c phpstan.neon.dist", "test": "./vendor/bin/phpunit --display-deprecations --display-notices --display-warnings --colors=always --configuration ./phpunit.xml --bootstrap .phpunit_full", "test-only": "./vendor/bin/phpunit --display-deprecations --display-notices --display-warnings --colors=always --configuration ./phpunit.xml --bootstrap .phpunit_full --filter SecurityTest::testSpecialCharactersDoNotCauseInjection", "test-psr16": "./vendor/bin/phpunit --display-deprecations --display-notices --display-warnings --colors=always --configuration ./phpunit.xml --bootstrap .phpunit_full --filter CacheIntegrationTest", diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..63aadba --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,5 @@ +parameters: + level: 6 + paths: + - src + - tests diff --git a/src/CacheEntryPool/CacheEntryPool.php b/src/CacheEntryPool/CacheEntryPool.php index 9f6d675..bdb1e6a 100644 --- a/src/CacheEntryPool/CacheEntryPool.php +++ b/src/CacheEntryPool/CacheEntryPool.php @@ -35,10 +35,7 @@ */ class CacheEntryPool implements CacheItemPoolInterface { - /** - * - * @var Psr\SimpleCache\CacheInterface - */ + /** @var RedisEnhancedCache&\Psr\SimpleCache\CacheInterface */ private RedisEnhancedCache $cache; /** @@ -48,15 +45,12 @@ class CacheEntryPool implements CacheItemPoolInterface */ private ?string $poolName = null; - /** - * - * @var array - */ + /** @var array */ private array $deferredItems = []; /** * - * @param Psr\SimpleCache\CacheInterface $cache + * @param RedisEnhancedCache&\Psr\SimpleCache\CacheInterface $cache * @param string|null $pool */ public function __construct(RedisEnhancedCache $cache, ?string $pool = null) @@ -83,7 +77,7 @@ public function clear(): bool $this->cache->delete($this->poolName); unset($this->deferredItems); $this->deferredItems = []; - } catch (Exception $e) { + } catch (\Exception $e) { return false; } @@ -96,7 +90,7 @@ public function clear(): bool * @param string $key * The key to delete. * - * @throws InvalidArgumentException + * @throws \Psr\Cache\InvalidArgumentException * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException * MUST be thrown. * @@ -139,7 +133,7 @@ public function deleteItems(array $keys): bool * @param string $key * The key for which to return the corresponding Cache Item. * - * @throws InvalidArgumentException + * @throws \Psr\Cache\InvalidArgumentException * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException * MUST be thrown. * @@ -353,6 +347,7 @@ public function commit(): bool return true; } + $deferred = []; foreach ($this->deferredItems as $key => $item) { if (!$this->isExpired($item)) { $deferred[$key] = $item->get(); diff --git a/src/RedisCache.php b/src/RedisCache.php index e941ebf..695b9dc 100644 --- a/src/RedisCache.php +++ b/src/RedisCache.php @@ -236,6 +236,7 @@ public function get(string $key, mixed $default = null): mixed */ private function getWithNonStrKey(mixed $key, mixed $default = null): mixed { + // @phpstan-ignore method.void return $this->get($this->checkKeyValidity($key), $default); } @@ -405,7 +406,7 @@ public function setMultiple(iterable $values, null|int|\DateInterval $ttl = self $this->formatException($t); $redisResponse = false; } finally { - return $redisResponse; + return $redisResponse; // @phpstan-ignore variable.undefined } } @@ -523,6 +524,7 @@ protected function setCorrectValue(string &$value): mixed return self::DOES_NOT_EXIST; } finally { + // @phpstan-ignore variable.undefined if ($tmp !== false || ($tmp === false && $value === 'b:0;')) { $value = $tmp; // if value var wasn't a string affect its original value type to it } diff --git a/src/RedisEnhancedCache.php b/src/RedisEnhancedCache.php index b2959b4..ca5bd19 100644 --- a/src/RedisEnhancedCache.php +++ b/src/RedisEnhancedCache.php @@ -4,6 +4,7 @@ namespace LLegaz\Cache; +use LLegaz\Cache\Exception\InvalidArgumentException; use LLegaz\Cache\Exception\InvalidKeyException; /** @@ -161,12 +162,17 @@ public function storeToPool(array $values, string $pool = self::DEFAULT_POOL): b return $this->getRedis()->hmset($pool, $values) == 'OK'; } elseif ($cnt === 1) { $key = array_keys($values)[0]; - $value = isset($key) ? $values[$key] : (isset($values[0]) ? $values[0] : null); + $value = $values[$key]; if (!$this->exist($value)) { /** * @todo test this specific scenario (maybe apply it to hmset ?) + * @todo and maybe refactor that exception handling system inherited from previous project (redis-adapter) + * + * because all values are authorized except this predefined value to sort actual existing values internally... */ - $this->throwUEx('The value: ' . $value . ' isn\'t accepted'); // because all values are authorized except this predefined value to sort actual exisiting values internally... + $e = new InvalidArgumentException('The value: ' . $value . ' isn\'t accepted'); + $this->formatException($e); + $this->throwUEx(); } if ($value) { //hset should returns the number of fields stored for a single key (always one here) @@ -223,7 +229,11 @@ public function fetchFromPool(mixed $key, string $pool = self::DEFAULT_POOL): mi break; case 'array': - if (count($key)) { + if (count($key) === 1) { + // redirect to string's or integer's case + return $this->fetchFromPool(reset($key), $pool); + } + if (count($key) > 1) { $this->checkKeysValidity($key); $this->begin(); $data = array_combine( @@ -238,10 +248,8 @@ public function fetchFromPool(mixed $key, string $pool = self::DEFAULT_POOL): mi $data[$key] = self::DOES_NOT_EXIST; } } - if (count($data)) { - return $data; - } + return $data; } break; @@ -362,7 +370,7 @@ public function deleteFromPool(array $keys, string $pool = self::DEFAULT_POOL): try { $redisResponse = call_user_func_array([$this->getRedis(), 'hdel'], $params); - } catch (Exception $e) { + } catch (\Exception $e) { $redisResponse = false; $this->formatException($e); } diff --git a/tests/Integration/CacheIntegrationTest.php b/tests/Integration/CacheIntegrationTest.php index 0b3199f..ea70d80 100644 --- a/tests/Integration/CacheIntegrationTest.php +++ b/tests/Integration/CacheIntegrationTest.php @@ -7,6 +7,8 @@ use Cache\IntegrationTests\SimpleCacheTest; use LLegaz\Cache\RedisCache as SUT; use LLegaz\Cache\Tests\TestState; +use PHPUnit\Framework\Attributes\Before; +use PHPUnit\Framework\Attributes\DataProvider; use Psr\SimpleCache\CacheInterface; use TypeError; @@ -122,7 +124,7 @@ public static function invalidTEKeysSingle() /** * Type Error keys (psr/cache version 3) * - * @return type + * @return array */ public static function invalidTEKeys() { diff --git a/tests/Integration/CacheIntegrationWithPCTest.php b/tests/Integration/CacheIntegrationWithPCTest.php index 65bbde2..b8be434 100644 --- a/tests/Integration/CacheIntegrationWithPCTest.php +++ b/tests/Integration/CacheIntegrationWithPCTest.php @@ -7,6 +7,8 @@ use Cache\IntegrationTests\SimpleCacheTest; use LLegaz\Cache\RedisCache as SUT; use LLegaz\Cache\Tests\TestState; +use PHPUnit\Framework\Attributes\Before; +use PHPUnit\Framework\Attributes\DataProvider; use Psr\SimpleCache\CacheInterface; use TypeError; diff --git a/tests/Integration/PoolIntegrationTest.php b/tests/Integration/PoolIntegrationTest.php index 253ee57..027871f 100644 --- a/tests/Integration/PoolIntegrationTest.php +++ b/tests/Integration/PoolIntegrationTest.php @@ -8,6 +8,7 @@ use LLegaz\Cache\Pool\CacheEntryPool as SUT; use LLegaz\Cache\RedisEnhancedCache; use LLegaz\Cache\Tests\TestState; +use PHPUnit\Framework\Attributes\Before; use Psr\Cache\CacheItemPoolInterface; if (!defined('SKIP_INTEGRATION_TESTS')) { diff --git a/tests/Integration/PoolIntegrationWithPCTest.php b/tests/Integration/PoolIntegrationWithPCTest.php index 5a1f1d3..8dee8c1 100644 --- a/tests/Integration/PoolIntegrationWithPCTest.php +++ b/tests/Integration/PoolIntegrationWithPCTest.php @@ -8,6 +8,7 @@ use LLegaz\Cache\Pool\CacheEntryPool as SUT; use LLegaz\Cache\RedisEnhancedCache; use LLegaz\Cache\Tests\TestState; +use PHPUnit\Framework\Attributes\Before; use Psr\Cache\CacheItemPoolInterface; if (!defined('SKIP_INTEGRATION_TESTS')) { diff --git a/tests/Unit/SimpleCacheRCTest.php b/tests/Unit/SimpleCacheRCTest.php index 131495e..413a516 100644 --- a/tests/Unit/SimpleCacheRCTest.php +++ b/tests/Unit/SimpleCacheRCTest.php @@ -23,6 +23,7 @@ class SimpleCacheRCTest extends RedisAdapterTestBase { protected SUT $cache; + /** @var \PHPUnit\Framework\MockObject\MockObject&RedisClientInterface */ protected RedisClientInterface $redisClient; public static function setUpBeforeClass(): void @@ -378,11 +379,10 @@ public function testSetWithTtl() /** * - * @return type + * @return RedisClientInterface */ protected function getSelfClient(): RedisClientInterface { return $this->redisClient; } - } diff --git a/tests/Unit/SimpleCacheTest.php b/tests/Unit/SimpleCacheTest.php index e68eebc..11a54e5 100644 --- a/tests/Unit/SimpleCacheTest.php +++ b/tests/Unit/SimpleCacheTest.php @@ -28,6 +28,7 @@ class SimpleCacheTest extends RedisAdapterTestBase { protected SUT $cache; + /** @var \PHPUnit\Framework\MockObject\MockObject&RedisClientInterface */ protected RedisClientInterface $predisClient; public static function setUpBeforeClass(): void @@ -360,11 +361,10 @@ public function testSetMultipleWithTtl() /** * - * @return type + * @return RedisClientInterface */ protected function getSelfClient(): RedisClientInterface { return $this->predisClient; } - }