Skip to content

Commit

Permalink
bugfix: CacheItemPoolDecorator uses `StorageItemInterface::hasItems…
Browse files Browse the repository at this point in the history
…` to verify deletion

The decorator has to verify if the keys still exist when the underlying storage states the deletion was not successful. This fixes a possible regression in 2.10.2.

Closes #99

Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com>
  • Loading branch information
boesing committed May 3, 2021
1 parent e0cd8fc commit 17c5669
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 3 deletions.
19 changes: 17 additions & 2 deletions src/Psr/CacheItemPool/CacheItemPoolDecorator.php
Expand Up @@ -15,6 +15,9 @@
use Laminas\Cache\Storage\StorageInterface;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use function array_unique;
use function in_array;
use function is_array;

/**
* Decorate laminas-cache adapters as PSR-6 cache item pools.
Expand Down Expand Up @@ -194,13 +197,25 @@ public function deleteItems(array $keys)
$this->deferred = array_diff_key($this->deferred, array_flip($keys));

try {
return $this->storage->removeItems($keys) === [];
$result = $this->storage->removeItems($keys);
} catch (Exception\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
} catch (Exception\ExceptionInterface $e) {
return false;
}

return false;
// BC compatibility can be removed in 3.0
if (! is_array($result)) {
return $result !== null;
}

if ($result === []) {
return true;
}

$existing = $this->storage->hasItems($result);
$unified = array_unique($existing);
return !in_array(true, $unified, true);
}

/**
Expand Down
42 changes: 42 additions & 0 deletions test/Psr/CacheItemPool/CacheItemPoolDecoratorTest.php
Expand Up @@ -555,4 +555,46 @@ private function getAdapter($storage = null)
}
return new CacheItemPoolDecorator($storage->reveal());
}

public function testCanHandleRemoveItemsReturningNonArray()
{
$adapter = $this->getStorageProphesy();
$adapter
->removeItems(Argument::type('array'))
->willReturn(null);

$cache = new CacheItemPoolDecorator($adapter->reveal());

self::assertFalse($cache->deleteItems(['foo']));
}

/**
* @param bool $exists
* @param bool $sucsessful
*
* @dataProvider deletionVerificationProvider
*/
public function testWillVerifyKeyExistenceByUsingHasItemsWhenDeletionWasNotSuccessful($exists, $sucsessful)
{
$adapter = $this->getStorageProphesy();
$adapter
->removeItems(Argument::type('array'))
->willReturn(['foo']);

$adapter
->hasItems(Argument::exact(['foo']))
->willReturn(['foo' => $exists]);

$cache = new CacheItemPoolDecorator($adapter->reveal());

self::assertEquals($sucsessful, $cache->deleteItems(['foo']));
}

public function deletionVerificationProvider()
{
return [
'deletion failed due to hasItems states the key still exists' => [true, false],
'deletion successful due to hasItems states the key does not exist' => [false, true],
];
}
}
12 changes: 11 additions & 1 deletion test/Psr/CacheItemPool/MockStorageTrait.php
Expand Up @@ -73,6 +73,16 @@ protected function getStorageProphesy($capabilities = false, $options = false, $
return $adapterOptions;
});

$storage->hasItems(Argument::type('array'))
->will(function ($args) use (&$items) {
$keys = $args[0];
$status = [];
foreach ($keys as $key) {
$status[$key] = array_key_exists($key, $items);
}

return $status;
});
$storage->hasItem(Argument::type('string'))
->will(function ($args) use (&$items) {
$key = $args[0];
Expand Down Expand Up @@ -118,7 +128,7 @@ protected function getStorageProphesy($capabilities = false, $options = false, $
$storage->removeItems(Argument::type('array'))
->will(function ($args) use (&$items) {
$items = array_diff_key($items, array_flip($args[0]));
return true;
return [];
});

return $storage;
Expand Down

0 comments on commit 17c5669

Please sign in to comment.