feature #26223 [FrameworkBundle] Add command to delete an item from a cache pool (pierredup)

This PR was squashed before being merged into the 4.1-dev branch (closes #26223).

Discussion
----------

[FrameworkBundle] Add command to delete an item from a cache pool

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | N/A
| License       | MIT
| Doc PR        | TBD

Currently there is no way to clear a specific item from a cache pool (except programatically), the entire pool needs to be cleared.
Especially during development, when implementing caching, it is useful to delete a specific key to test functionality. Clearing the entire pool, means that everything will need to be cached again, adding unnecessary execution time.

I propose adding a new command, `cache:pool:delete` to delete a specific item from a cache pool

Commits
-------

fd43e81 [FrameworkBundle] Add command to delete an item from a cache pool
This commit is contained in:
Nicolas Grekas 2018-03-02 13:13:05 +01:00
commit 1f7b9f0fc5
4 changed files with 217 additions and 0 deletions

View File

@ -0,0 +1,81 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer;
/**
* Delete an item from a cache pool.
*
* @author Pierre du Plessis <pdples@gmail.com>
*/
final class CachePoolDeleteCommand extends Command
{
protected static $defaultName = 'cache:pool:delete';
private $poolClearer;
public function __construct(Psr6CacheClearer $poolClearer)
{
parent::__construct();
$this->poolClearer = $poolClearer;
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setDefinition(array(
new InputArgument('pool', InputArgument::REQUIRED, 'The cache pool from which to delete an item'),
new InputArgument('key', InputArgument::REQUIRED, 'The cache key to delete from the pool'),
))
->setDescription('Deletes an item from a cache pool')
->setHelp(<<<'EOF'
The <info>%command.name%</info> deletes an item from a given cache pool.
%command.full_name% <pool> <key>
EOF
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$pool = $input->getArgument('pool');
$key = $input->getArgument('key');
$cachePool = $this->poolClearer->getPool($pool);
if (!$cachePool->hasItem($key)) {
$io->note(sprintf('Cache item "%s" does not exist in cache pool "%s".', $key, $pool));
return;
}
if (!$cachePool->deleteItem($key)) {
throw new \Exception(sprintf('Cache item "%s" could not be deleted.', $key));
}
$io->success(sprintf('Cache item "%s" was successfully deleted.', $key));
}
}

View File

@ -38,6 +38,11 @@
<tag name="console.command" command="cache:pool:prune" />
</service>
<service id="console.command.cache_pool_delete" class="Symfony\Bundle\FrameworkBundle\Command\CachePoolDeleteCommand">
<argument type="service" id="cache.global_clearer" />
<tag name="console.command" command="cache:pool:delete" />
</service>
<service id="console.command.cache_warmup" class="Symfony\Bundle\FrameworkBundle\Command\CacheWarmupCommand">
<argument type="service" id="cache_warmer" />
<tag name="console.command" command="cache:warmup" />

View File

@ -0,0 +1,122 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Bundle\FrameworkBundle\Command\CachePoolDeleteCommand;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer;
use Symfony\Component\HttpKernel\KernelInterface;
class CachePoolDeleteCommandTest extends TestCase
{
private $cachePool;
protected function setUp()
{
$this->cachePool = $this->getMockBuilder(CacheItemPoolInterface::class)
->getMock();
}
public function testCommandWithValidKey()
{
$this->cachePool->expects($this->once())
->method('hasItem')
->with('bar')
->willReturn(true);
$this->cachePool->expects($this->once())
->method('deleteItem')
->with('bar')
->willReturn(true);
$tester = $this->getCommandTester($this->getKernel());
$tester->execute(array('pool' => 'foo', 'key' => 'bar'));
$this->assertContains('[OK] Cache item "bar" was successfully deleted.', $tester->getDisplay());
}
public function testCommandWithInValidKey()
{
$this->cachePool->expects($this->once())
->method('hasItem')
->with('bar')
->willReturn(false);
$this->cachePool->expects($this->never())
->method('deleteItem')
->with('bar');
$tester = $this->getCommandTester($this->getKernel());
$tester->execute(array('pool' => 'foo', 'key' => 'bar'));
$this->assertContains('[NOTE] Cache item "bar" does not exist in cache pool "foo".', $tester->getDisplay());
}
public function testCommandDeleteFailed()
{
$this->cachePool->expects($this->once())
->method('hasItem')
->with('bar')
->willReturn(true);
$this->cachePool->expects($this->once())
->method('deleteItem')
->with('bar')
->willReturn(false);
if (method_exists($this, 'expectExceptionMessage')) {
$this->expectExceptionMessage('Cache item "bar" could not be deleted.');
} else {
$this->setExpectedException('Exception', 'Cache item "bar" could not be deleted.');
}
$tester = $this->getCommandTester($this->getKernel());
$tester->execute(array('pool' => 'foo', 'key' => 'bar'));
}
/**
* @return \PHPUnit_Framework_MockObject_MockObject|KernelInterface
*/
private function getKernel()
{
$container = $this
->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')
->getMock();
$kernel = $this
->getMockBuilder(KernelInterface::class)
->getMock();
$kernel
->expects($this->any())
->method('getContainer')
->willReturn($container);
$kernel
->expects($this->once())
->method('getBundles')
->willReturn(array());
return $kernel;
}
private function getCommandTester(KernelInterface $kernel): CommandTester
{
$application = new Application($kernel);
$application->add(new CachePoolDeleteCommand(new Psr6CacheClearer(array('foo' => $this->cachePool))));
return new CommandTester($application->find('cache:pool:delete'));
}
}

View File

@ -28,6 +28,15 @@ class Psr6CacheClearer implements CacheClearerInterface
return isset($this->pools[$name]);
}
public function getPool($name)
{
if (!$this->hasPool($name)) {
throw new \InvalidArgumentException(sprintf('Cache pool not found: %s.', $name));
}
return $this->pools[$name];
}
public function clearPool($name)
{
if (!isset($this->pools[$name])) {