diff --git a/Adapter/PhpArrayAdapter.php b/Adapter/PhpArrayAdapter.php index 8047a8a..bffe9a0 100644 --- a/Adapter/PhpArrayAdapter.php +++ b/Adapter/PhpArrayAdapter.php @@ -17,6 +17,7 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\CachedValueInterface; use Symfony\Component\Cache\Traits\ContractsTrait; use Symfony\Component\Cache\Traits\ProxyTrait; use Symfony\Component\VarExporter\VarExporter; @@ -96,16 +97,15 @@ public function get(string $key, callable $callback, ?float $beta = null, ?array if ('N;' === $value) { return null; } + if (!$value instanceof CachedValueInterface) { + return $value; + } try { - if ($value instanceof \Closure) { - return $value(); - } + return $value->getValue(); } catch (\Throwable) { unset($this->keys[$key]); goto get_from_pool; } - - return $value; } public function getItem(mixed $key): CacheItem @@ -125,9 +125,9 @@ public function getItem(mixed $key): CacheItem if ('N;' === $value) { $value = null; - } elseif ($value instanceof \Closure) { + } elseif ($value instanceof CachedValueInterface) { try { - $value = $value(); + $value = $value->getValue(); } catch (\Throwable) { $value = null; $isHit = false; @@ -306,8 +306,7 @@ public function warmUp(array $values): array } if (!$isStaticValue) { - $value = str_replace("\n", "\n ", $value); - $value = "static function () {\n return {$value};\n}"; + $value = 'new class() implements \\'.CachedValueInterface::class." { public function getValue(): mixed { return {$value}; } }"; } $hash = hash('xxh128', $value); @@ -368,9 +367,9 @@ private function generateItems(array $keys): \Generator if ('N;' === $value) { yield $key => $f($key, null, true); - } elseif ($value instanceof \Closure) { + } elseif ($value instanceof CachedValueInterface) { try { - yield $key => $f($key, $value(), true); + yield $key => $f($key, $value->getValue(), true); } catch (\Throwable) { yield $key => $f($key, null, false); } diff --git a/Adapter/PhpFilesAdapter.php b/Adapter/PhpFilesAdapter.php index e64ec4c..75f67f4 100644 --- a/Adapter/PhpFilesAdapter.php +++ b/Adapter/PhpFilesAdapter.php @@ -14,6 +14,7 @@ use Symfony\Component\Cache\Exception\CacheException; use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\CachedValueInterface; use Symfony\Component\Cache\Traits\FilesystemCommonTrait; use Symfony\Component\VarExporter\VarExporter; @@ -113,8 +114,10 @@ protected function doFetch(array $ids): iterable $values[$id] = null; } elseif (!\is_object($value)) { $values[$id] = $value; + } elseif ($value instanceof CachedValueInterface) { + $values[$id] = $value->getValue(); } elseif (!$value instanceof LazyValue) { - $values[$id] = $value(); + $values[$id] = $value; } elseif (false === $values[$id] = include $value->file) { unset($values[$id], $this->values[$id]); $missingIds[] = $id; @@ -235,7 +238,7 @@ protected function doSave(array $values, int $lifetime): array|bool if ($isStaticValue) { $value = "return [{$expiry}, {$value}];"; } elseif ($this->appendOnly) { - $value = "return [{$expiry}, static fn () => {$value}];"; + $value = "return [{$expiry}, new class() implements \\".CachedValueInterface::class." { public function getValue(): mixed { return {$value}; } }];"; } else { // We cannot use a closure here because of https://bugs.php.net/76982 $value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value); diff --git a/Adapter/TagAwareAdapter.php b/Adapter/TagAwareAdapter.php index 70927cf..75abad2 100644 --- a/Adapter/TagAwareAdapter.php +++ b/Adapter/TagAwareAdapter.php @@ -309,12 +309,12 @@ public function reset(): void $this->tags instanceof ResettableInterface && $this->tags->reset(); } - public function __sleep(): array + public function __serialize(): array { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } - public function __wakeup(): void + public function __unserialize(array $data): void { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } @@ -378,7 +378,7 @@ private function getTagVersions(array $tagsByKey, bool $persistTags): array (self::$saveTags)($this->tags, $newTags); } - while ($now > ($this->knownTagVersions[$tag = array_key_first($this->knownTagVersions)][0] ?? \INF)) { + while ($now > ($this->knownTagVersions[$tag = array_key_first($this->knownTagVersions) ?? ''][0] ?? \INF)) { unset($this->knownTagVersions[$tag]); } diff --git a/DependencyInjection/CachePoolPass.php b/DependencyInjection/CachePoolPass.php index 1a85764..2725d3b 100644 --- a/DependencyInjection/CachePoolPass.php +++ b/DependencyInjection/CachePoolPass.php @@ -16,6 +16,7 @@ use Symfony\Component\Cache\Adapter\ChainAdapter; use Symfony\Component\Cache\Adapter\NullAdapter; use Symfony\Component\Cache\Adapter\ParameterNormalizer; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Component\Cache\Messenger\EarlyExpirationDispatcher; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -153,7 +154,7 @@ public function process(ContainerBuilder $container): void ), ]); $pool->addTag('container.reversible'); - } elseif ('namespace' !== $attr || !\in_array($class, [ArrayAdapter::class, NullAdapter::class], true)) { + } elseif ('namespace' !== $attr || !\in_array($class, [ArrayAdapter::class, NullAdapter::class, TagAwareAdapter::class], true)) { $argument = $tags[0][$attr]; if ('default_lifetime' === $attr && !is_numeric($argument)) { diff --git a/Tests/Adapter/AdapterTestCase.php b/Tests/Adapter/AdapterTestCase.php index 896ca94..9a79365 100644 --- a/Tests/Adapter/AdapterTestCase.php +++ b/Tests/Adapter/AdapterTestCase.php @@ -399,7 +399,7 @@ public function testNamespaces() class NotUnserializable { - public function __wakeup(): void + public function __unserialize(array $data): void { throw new \Exception(__CLASS__); } diff --git a/Tests/DependencyInjection/CachePoolPassTest.php b/Tests/DependencyInjection/CachePoolPassTest.php index 6527cce..4c50874 100644 --- a/Tests/DependencyInjection/CachePoolPassTest.php +++ b/Tests/DependencyInjection/CachePoolPassTest.php @@ -17,6 +17,7 @@ use Symfony\Component\Cache\Adapter\ChainAdapter; use Symfony\Component\Cache\Adapter\NullAdapter; use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Component\Cache\DependencyInjection\CachePoolPass; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -102,15 +103,18 @@ public function testNamespaceArgumentIsSeededWithAdapterClassNameWithoutAffectin $this->assertSame('mVXLns1cYU', $cachePool->getArgument(0)); } - public function testNamespaceArgumentIsNotReplacedIfArrayAdapterIsUsed() + /** + * @dataProvider providerAdaptersNotNamespace + */ + public function testNamespaceArgumentIsNotReplacedIfAdapterWithoutNamespace(string $adapterClass) { $container = new ContainerBuilder(); $container->setParameter('kernel.container_class', 'app'); $container->setParameter('kernel.project_dir', 'foo'); - $container->register('cache.adapter.array', ArrayAdapter::class)->addArgument(0); + $container->register('cache.adapter', $adapterClass)->addArgument(0); - $cachePool = new ChildDefinition('cache.adapter.array'); + $cachePool = new ChildDefinition('cache.adapter'); $cachePool->addTag('cache.pool'); $container->setDefinition('app.cache_pool', $cachePool); @@ -119,21 +123,11 @@ public function testNamespaceArgumentIsNotReplacedIfArrayAdapterIsUsed() $this->assertCount(0, $container->getDefinition('app.cache_pool')->getArguments()); } - public function testNamespaceArgumentIsNotReplacedIfNullAdapterIsUsed() + public static function providerAdaptersNotNamespace(): iterable { - $container = new ContainerBuilder(); - $container->setParameter('kernel.container_class', 'app'); - $container->setParameter('kernel.project_dir', 'foo'); - - $container->register('cache.adapter.null', NullAdapter::class); - - $cachePool = new ChildDefinition('cache.adapter.null'); - $cachePool->addTag('cache.pool'); - $container->setDefinition('app.cache_pool', $cachePool); - - $this->cachePoolPass->process($container); - - $this->assertCount(0, $container->getDefinition('app.cache_pool')->getArguments()); + yield [ArrayAdapter::class]; + yield [NullAdapter::class]; + yield [TagAwareAdapter::class]; } public function testArgsAreReplaced() diff --git a/Tests/Psr16CacheTest.php b/Tests/Psr16CacheTest.php index b5a1fe4..95084ae 100644 --- a/Tests/Psr16CacheTest.php +++ b/Tests/Psr16CacheTest.php @@ -174,7 +174,7 @@ protected function isPruned(CacheInterface $cache, string $name): bool class NotUnserializable { - public function __wakeup(): void + public function __unserialize(array $data): void { throw new \Exception(__CLASS__); } diff --git a/Traits/AbstractAdapterTrait.php b/Traits/AbstractAdapterTrait.php index ac8dc97..92e106f 100644 --- a/Traits/AbstractAdapterTrait.php +++ b/Traits/AbstractAdapterTrait.php @@ -288,12 +288,12 @@ public function reset(): void $this->ids = []; } - public function __sleep(): array + public function __serialize(): array { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } - public function __wakeup(): void + public function __unserialize(array $data): void { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } diff --git a/Traits/CachedValueInterface.php b/Traits/CachedValueInterface.php new file mode 100644 index 0000000..8e95f60 --- /dev/null +++ b/Traits/CachedValueInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +/** + * @internal + */ +interface CachedValueInterface +{ + public function getValue(): mixed; +} diff --git a/Traits/FilesystemCommonTrait.php b/Traits/FilesystemCommonTrait.php index 98e0d3e..cc538aa 100644 --- a/Traits/FilesystemCommonTrait.php +++ b/Traits/FilesystemCommonTrait.php @@ -168,12 +168,12 @@ private function scanHashDir(string $directory): \Generator } } - public function __sleep(): array + public function __serialize(): array { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } - public function __wakeup(): void + public function __unserialize(array $data): void { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); }