From a5823af605343d28d1610a5d07c25176e760f548 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 3 Sep 2025 11:01:13 +0200 Subject: [PATCH] [Cache] Fix internal representation of non-static values --- .../Cache/Adapter/PhpArrayAdapter.php | 21 +++++++++---------- .../Cache/Adapter/PhpFilesAdapter.php | 9 +++++--- .../Cache/Traits/CachedValueInterface.php | 20 ++++++++++++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 src/Symfony/Component/Cache/Traits/CachedValueInterface.php diff --git a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php index 950fb51d81deb..6c3d70724582f 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php +++ b/src/Symfony/Component/Cache/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/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php index a1876114a5660..70d08944513af 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php +++ b/src/Symfony/Component/Cache/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; @@ -114,8 +115,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; @@ -152,7 +155,7 @@ protected function doFetch(array $ids): iterable if ($now >= $expiresAt) { unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]); } - } catch (\ErrorException $e) { + } catch (\ErrorException) { unset($missingIds[$k]); } } @@ -236,7 +239,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/src/Symfony/Component/Cache/Traits/CachedValueInterface.php b/src/Symfony/Component/Cache/Traits/CachedValueInterface.php new file mode 100644 index 0000000000000..8e95f60b07b65 --- /dev/null +++ b/src/Symfony/Component/Cache/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; +}