diff --git a/composer.json b/composer.json index 621b44a..78797cf 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,6 @@ }, "require-dev": { "phpunit/phpunit": "^9.5", - "openclassrooms/doctrine-cache-extension": "1.0.*@dev", "phpstan/phpstan": "^1.9", "phpstan/phpstan-strict-rules": "^1.4", "phpstan/phpstan-deprecation-rules": "^1.1", diff --git a/devenv.lock b/devenv.lock index 37cacc4..cdaec47 100644 --- a/devenv.lock +++ b/devenv.lock @@ -3,10 +3,10 @@ "devenv": { "locked": { "dir": "src/modules", - "lastModified": 1732025403, + "lastModified": 1738414267, "owner": "cachix", "repo": "devenv", - "rev": "6473534b5f3a7ae956ee751084bc4bf2391ccc28", + "rev": "3f49b4afbb9a80b1e81fb6071f59dac152177efa", "type": "github" }, "original": { @@ -19,10 +19,10 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, + "lastModified": 1733328505, "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -37,14 +37,13 @@ "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable" + ] }, "locked": { - "lastModified": 1732021966, + "lastModified": 1737465171, "owner": "cachix", "repo": "git-hooks.nix", - "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", + "rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17", "type": "github" }, "original": { @@ -75,30 +74,15 @@ }, "nixpkgs": { "locked": { - "lastModified": 1716977621, - "owner": "cachix", - "repo": "devenv-nixpkgs", - "rev": "4267e705586473d3e5c8d50299e71503f16a6fb6", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "rolling", - "repo": "devenv-nixpkgs", - "type": "github" - } - }, - "nixpkgs-stable": { - "locked": { - "lastModified": 1731797254, + "lastModified": 1738739810, "owner": "NixOS", "repo": "nixpkgs", - "rev": "e8c38b73aeb218e27163376a2d617e61a2ad9b59", + "rev": "0a9533e00b72a74d4c69cf1dbb36d5cac7477ea7", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-24.05", + "ref": "nixpkgs-24.11-darwin", "repo": "nixpkgs", "type": "github" } diff --git a/devenv.nix b/devenv.nix index 5a6287c..b15a7e7 100644 --- a/devenv.nix +++ b/devenv.nix @@ -1,5 +1,24 @@ { pkgs, lib, config, inputs, ... }: { - languages.php.enable = true; + languages.php = { + enable = true; + + ini = '' + memory_limit = -1 + ''; + }; + + pre-commit.hooks = { + phpstan = { + enable = true; + entry = "vendor/phpstan/phpstan/phpstan analyze -c phpstan.neon"; + pass_filenames = false; + }; + phpunit = { + enable = true; + entry = "vendor/phpunit/phpunit/phpunit"; + pass_filenames = false; + }; + }; } diff --git a/devenv.yaml b/devenv.yaml index 116a2ad..e458b16 100644 --- a/devenv.yaml +++ b/devenv.yaml @@ -1,7 +1,7 @@ # yaml-language-server: $schema=https://devenv.sh/devenv.schema.json inputs: nixpkgs: - url: github:cachix/devenv-nixpkgs/rolling + url: github:NixOS/nixpkgs/nixpkgs-24.11-darwin # If you're using non-OSS software, you can set allowUnfree to true. # allowUnfree: true diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b5c4278..b95c57e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,19 +1,20 @@ - - - + + + + ./src + + + ./tests + ./vendor + + ./tests/ - - - ./src - - ./tests - ./vendor - - - diff --git a/src/Handler/Impl/Cache/DoctrineCacheHandler.php b/src/Handler/Impl/Cache/DoctrineCacheHandler.php index 60c4d5a..964d0e9 100644 --- a/src/Handler/Impl/Cache/DoctrineCacheHandler.php +++ b/src/Handler/Impl/Cache/DoctrineCacheHandler.php @@ -4,12 +4,11 @@ namespace OpenClassrooms\ServiceProxy\Handler\Impl\Cache; -use Doctrine\Common\Cache\Psr6\CacheItem; -use OpenClassrooms\DoctrineCacheExtension\CacheProviderDecorator; use OpenClassrooms\ServiceProxy\Handler\Contract\CacheHandler; use OpenClassrooms\ServiceProxy\Handler\Impl\ConfigurableHandler; -use OpenClassrooms\ServiceProxy\Util\ArrayCache; use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; /** * @deprecated use SymfonyCacheHandler instead @@ -18,11 +17,11 @@ final class DoctrineCacheHandler implements CacheHandler { use ConfigurableHandler; - private CacheProviderDecorator $cacheProvider; + private CacheItemPoolInterface $pool; - public function __construct(?CacheProviderDecorator $cacheProvider = null, ?string $name = null) + public function __construct(CacheItemPoolInterface $pool = null, ?string $name = null) { - $this->cacheProvider = $cacheProvider ?? new CacheProviderDecorator(new ArrayCache()); + $this->pool = $pool ?? new ArrayAdapter(storeSerialized: false); $this->name = $name; } @@ -31,19 +30,31 @@ public function fetch(string $poolName, string $id): CacheItemInterface $tags = explode('|', $id); $id = array_shift($tags); - $value = $this->cacheProvider->fetchWithNamespace($id, $tags[0] ?? null); - - return new CacheItem($id, $value, $value !== false); + return $this->fetchWithNamespace($id, $tags[0] ?? null); } public function save(string $poolName, string $id, $data, ?int $lifeTime = null, array $tags = []): void { - $this->cacheProvider->saveWithNamespace($id, $data, $tags[0] ?? null, $lifeTime); - } + $namespaceId = $tags[0] ?? null; - public function contains(string $poolName, string $id): bool - { - return $this->cacheProvider->contains($id); + if ($namespaceId !== null) { + $namespace = $this->doFetch($namespaceId); + if (!$namespace->isHit()) { + $namespace->set($namespaceId . '_' . mt_rand(0, 1000000)) + // 7 days as no expiration can prevent cache eviction forever (like redis) + ->expiresAfter(7 * 24 * 60 * 60); + + $this->pool->save($namespace); + } + $id = $namespace->get() . $id; + } + + $item = $this->doFetch($id); + + $item->set($data) + ->expiresAfter($lifeTime ?? 3600); + + $this->pool->save($item); } public function invalidateTags(string $poolName, array $tags): void @@ -55,4 +66,22 @@ public function getName(): string { return $this->name ?? 'doctrine_array'; } + + private function fetchWithNamespace(string $id, string $namespaceId = null): CacheItemInterface + { + if ($namespaceId !== null) { + $namespace = $this->doFetch($namespaceId); + + if ($namespace->isHit()) { + $id = $namespace->get() . $id; + } + } + + return $this->doFetch($id); + } + + private function doFetch(string $id): CacheItemInterface + { + return $this->pool->getItem(rawurlencode($id)); + } } diff --git a/src/Handler/Impl/Cache/SymfonyCacheHandler.php b/src/Handler/Impl/Cache/SymfonyCacheHandler.php index bc6cf57..9efbe0d 100644 --- a/src/Handler/Impl/Cache/SymfonyCacheHandler.php +++ b/src/Handler/Impl/Cache/SymfonyCacheHandler.php @@ -46,12 +46,6 @@ public function save(string $poolName, string $id, $data, ?int $ttl = null, arra $pool->save($item); } - public function contains(string $poolName, string $id): bool - { - return $this->getPool($poolName) - ->hasItem($id); - } - public function invalidateTags(string $poolName, array $tags): void { $this->getPool($poolName) diff --git a/src/Util/ArrayCache.php b/src/Util/ArrayCache.php deleted file mode 100644 index 5484745..0000000 --- a/src/Util/ArrayCache.php +++ /dev/null @@ -1,110 +0,0 @@ -> $data each element being a tuple of [$data, $expiration], where the expiration is int|bool - */ - private array $data = []; - - private int $hitsCount = 0; - - private int $missesCount = 0; - - private int $upTime; - - /** - * {@inheritdoc} - */ - public function __construct() - { - $this->upTime = time(); - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - if (!$this->doContains($id)) { - ++$this->missesCount; - - return false; - } - - ++$this->hitsCount; - - return $this->data[$id][0]; - } - - /** - * {@inheritdoc} - */ - protected function doContains($id): bool - { - if (!isset($this->data[$id])) { - return false; - } - - $expiration = $this->data[$id][1]; - - if ($expiration && $expiration < time()) { - $this->doDelete($id); - - return false; - } - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id): bool - { - unset($this->data[$id]); - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doFlush(): bool - { - $this->data = []; - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doGetStats(): ?array - { - return [ - Cache::STATS_HITS => $this->hitsCount, - Cache::STATS_MISSES => $this->missesCount, - Cache::STATS_UPTIME => $this->upTime, - Cache::STATS_MEMORY_USAGE => null, - Cache::STATS_MEMORY_AVAILABLE => null, - ]; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0): bool - { - $this->data[$id] = [$data, $lifeTime ? time() + $lifeTime : false]; - - return true; - } -} diff --git a/tests/Double/Mock/Cache/DoctrineCacheHandlerMock.php b/tests/Double/Mock/Cache/DoctrineCacheHandlerMock.php index 4612d0a..90594e5 100644 --- a/tests/Double/Mock/Cache/DoctrineCacheHandlerMock.php +++ b/tests/Double/Mock/Cache/DoctrineCacheHandlerMock.php @@ -4,11 +4,10 @@ namespace OpenClassrooms\ServiceProxy\Tests\Double\Mock\Cache; -use OpenClassrooms\DoctrineCacheExtension\CacheProviderDecorator; use OpenClassrooms\ServiceProxy\Handler\Contract\CacheHandler; use OpenClassrooms\ServiceProxy\Handler\Impl\Cache\DoctrineCacheHandler; -use OpenClassrooms\ServiceProxy\Util\ArrayCache; use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; final class DoctrineCacheHandlerMock implements CacheHandler { @@ -16,8 +15,6 @@ final class DoctrineCacheHandlerMock implements CacheHandler private CacheHandler $wrappedHandler; - private CacheProviderDecorator $cacheProvider; - private bool $default; private string $name; @@ -27,8 +24,7 @@ public function __construct(?string $name = null, bool $default = true) $this->name = $name ?? 'array'; $this->default = $default; - $cacheProvider = new CacheProviderDecorator(new ArrayCache()); - $this->wrappedHandler = new DoctrineCacheHandler($cacheProvider, $name); + $this->wrappedHandler = new DoctrineCacheHandler(new ArrayAdapter(storeSerialized: false), $name); } public function fetch(string $poolName, string $id): CacheItemInterface diff --git a/tests/Handler/Impl/Lock/SymfonyLockHandlerTest.php b/tests/Handler/Impl/Lock/SymfonyLockHandlerTest.php index b1f0ad0..1eb963a 100644 --- a/tests/Handler/Impl/Lock/SymfonyLockHandlerTest.php +++ b/tests/Handler/Impl/Lock/SymfonyLockHandlerTest.php @@ -11,6 +11,10 @@ class SymfonyLockHandlerTest extends TestCase { + private SymfonyLockHandler $handler1; + + private SymfonyLockHandler $handler2; + protected function setUp(): void { $factory1 = new LockFactory(new FlockStore()); diff --git a/tests/Interceptor/LegacyCacheInterceptorTest.php b/tests/Interceptor/LegacyCacheInterceptorTest.php index 5b5cd4e..ea78cbf 100644 --- a/tests/Interceptor/LegacyCacheInterceptorTest.php +++ b/tests/Interceptor/LegacyCacheInterceptorTest.php @@ -95,7 +95,12 @@ public function testInCacheWithNamespaceReturnData(): void $inCacheData = 'InCacheData'; $this->cacheHandlerMock->save( 'default', - md5(CacheAnnotatedClass::class . '::cacheWithNamespace'), + md5('test-namespace'), + '12345' + ); + $this->cacheHandlerMock->save( + 'default', + '12345' . md5(CacheAnnotatedClass::class . '::cacheWithNamespace'), $inCacheData ); $data = $this->proxy->cacheWithNamespace();