diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php index 0eceb6e572..498c00ca64 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php @@ -59,6 +59,26 @@ abstract class AdapterTestCase extends CachePoolTest $this->assertFalse($isHit); } + public function testRecursiveGet() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createCachePool(0, __FUNCTION__); + + $v = $cache->get('k1', function () use (&$counter, $cache) { + $v = $cache->get('k2', function () use (&$counter) { return ++$counter; }); + $v = $cache->get('k2', function () use (&$counter) { return ++$counter; }); + + return $v; + }); + + $this->assertSame(1, $counter); + $this->assertSame(1, $v); + $this->assertSame(1, $cache->get('k2', function () { return 2; })); + } + public function testGetMetadata() { if (isset($this->skippedTests[__FUNCTION__])) { diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php index cee80ac196..3a5904b107 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php @@ -23,6 +23,7 @@ class PhpArrayAdapterTest extends AdapterTestCase { protected $skippedTests = [ 'testGet' => 'PhpArrayAdapter is read-only.', + 'testRecursiveGet' => 'PhpArrayAdapter is read-only.', 'testBasicUsage' => 'PhpArrayAdapter is read-only.', 'testBasicUsageWithLongKey' => 'PhpArrayAdapter is read-only.', 'testClear' => 'PhpArrayAdapter is read-only.', diff --git a/src/Symfony/Component/Cache/Traits/ContractsTrait.php b/src/Symfony/Component/Cache/Traits/ContractsTrait.php index bd7be08dd5..6d602dfece 100644 --- a/src/Symfony/Component/Cache/Traits/ContractsTrait.php +++ b/src/Symfony/Component/Cache/Traits/ContractsTrait.php @@ -31,6 +31,7 @@ trait ContractsTrait } private $callbackWrapper = [LockRegistry::class, 'compute']; + private $computing = []; /** * Wraps the callback passed to ->get() in a callable. @@ -68,26 +69,27 @@ trait ContractsTrait CacheItem::class ); - return $this->contractsGet($pool, $key, function (CacheItem $item, bool &$save) use ($pool, $callback, $setMetadata, &$metadata) { + return $this->contractsGet($pool, $key, function (CacheItem $item, bool &$save) use ($pool, $callback, $setMetadata, &$metadata, $key) { // don't wrap nor save recursive calls - if (null === $callbackWrapper = $this->callbackWrapper) { + if (isset($this->computing[$key])) { $value = $callback($item, $save); $save = false; return $value; } - $this->callbackWrapper = null; + + $this->computing[$key] = $key; $startTime = microtime(true); try { - $value = $callbackWrapper($callback, $item, $save, $pool, function (CacheItem $item) use ($setMetadata, $startTime, &$metadata) { + $value = ($this->callbackWrapper)($callback, $item, $save, $pool, function (CacheItem $item) use ($setMetadata, $startTime, &$metadata) { $setMetadata($item, $startTime, $metadata); }); $setMetadata($item, $startTime, $metadata); return $value; } finally { - $this->callbackWrapper = $callbackWrapper; + unset($this->computing[$key]); } }, $beta, $metadata); }