bug #31479 [Cache] fix saving unrelated keys in recursive callback calls (nicolas-grekas)

This PR was merged into the 4.2 branch.

Discussion
----------

[Cache] fix saving unrelated keys in recursive callback calls

| Q             | A
| ------------- | ---
| Branch?       | 4.2
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #31399
| License       | MIT
| Doc PR        | -

Commits
-------

4443c2018c [Cache] fix saving unrelated keys in recursive callback calls
This commit is contained in:
Nicolas Grekas 2019-05-12 12:40:49 +02:00
commit 1ecc6a6c03
3 changed files with 28 additions and 5 deletions

View File

@ -59,6 +59,26 @@ abstract class AdapterTestCase extends CachePoolTest
$this->assertFalse($isHit); $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() public function testGetMetadata()
{ {
if (isset($this->skippedTests[__FUNCTION__])) { if (isset($this->skippedTests[__FUNCTION__])) {

View File

@ -23,6 +23,7 @@ class PhpArrayAdapterTest extends AdapterTestCase
{ {
protected $skippedTests = [ protected $skippedTests = [
'testGet' => 'PhpArrayAdapter is read-only.', 'testGet' => 'PhpArrayAdapter is read-only.',
'testRecursiveGet' => 'PhpArrayAdapter is read-only.',
'testBasicUsage' => 'PhpArrayAdapter is read-only.', 'testBasicUsage' => 'PhpArrayAdapter is read-only.',
'testBasicUsageWithLongKey' => 'PhpArrayAdapter is read-only.', 'testBasicUsageWithLongKey' => 'PhpArrayAdapter is read-only.',
'testClear' => 'PhpArrayAdapter is read-only.', 'testClear' => 'PhpArrayAdapter is read-only.',

View File

@ -31,6 +31,7 @@ trait ContractsTrait
} }
private $callbackWrapper = [LockRegistry::class, 'compute']; private $callbackWrapper = [LockRegistry::class, 'compute'];
private $computing = [];
/** /**
* Wraps the callback passed to ->get() in a callable. * Wraps the callback passed to ->get() in a callable.
@ -68,26 +69,27 @@ trait ContractsTrait
CacheItem::class 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 // don't wrap nor save recursive calls
if (null === $callbackWrapper = $this->callbackWrapper) { if (isset($this->computing[$key])) {
$value = $callback($item, $save); $value = $callback($item, $save);
$save = false; $save = false;
return $value; return $value;
} }
$this->callbackWrapper = null;
$this->computing[$key] = $key;
$startTime = microtime(true); $startTime = microtime(true);
try { 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);
}); });
$setMetadata($item, $startTime, $metadata); $setMetadata($item, $startTime, $metadata);
return $value; return $value;
} finally { } finally {
$this->callbackWrapper = $callbackWrapper; unset($this->computing[$key]);
} }
}, $beta, $metadata); }, $beta, $metadata);
} }