8000 add cache pruner command · symfony/symfony@5127fd2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5127fd2

Browse files
committed
add cache pruner command
1 parent 5700093 commit 5127fd2

File tree

12 files changed

+428
-56
lines changed

12 files changed

+428
-56
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Command;
13+
14+
use Symfony\Component\Cache\PruneableInterface;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\Exception\InvalidArgumentException;
17+
use Symfony\Component\Console\Exception\RuntimeException;
18+
use Symfony\Component\Console\Input\InputInterface;
19+
use Symfony\Component\Console\Input\InputArgument;
20+
use Symfony\Component\Console\Output\OutputInterface;
21+
use Symfony\Component\Console\Style\SymfonyStyle;
22+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
23+
24+
/**
25+
* Cache pool pruner command.
26+
*/
27+
final class CachePoolPruneCommand extends Command
28+
{
29+
/**
30+
* @var RewindableGenerator
31+
*/
32+
private $pools;
33+
34+
/**
35+
* @param RewindableGenerator $pools
36+
*/
37+
public function __construct(RewindableGenerator $pools)
38+
{
39+
parent::__construct();
40+
$this->pools = $pools;
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
protected function configure()
47+
{
48+
$this
49+
->setName('cache:pool:prune')
50+
->addArgument('pools', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'A list of cache pool service ids')
51+
->setDescription('Prune cache pools')
52+
->setHelp(<<<'EOF'
53+
The <info>%command.name%</info> command prunes all or the given cache pools.
54+
55+
%command.full_name% [<cache pool service id 1> [...<cache pool service id N>]]
56+
EOF
57+
)
58+
;
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
protected function execute(InputInterface $input, OutputInterface $output)
65+
{
66+
$io = new SymfonyStyle($input, $output);
67+
68+
if (0 === count($pools = $this->getMatchingPools($input))) {
69+
throw new RuntimeException('No pruneable cache pools found.');
70+
}
71+
72+
foreach ($pools as $name => $pool) {
73+
$io->comment(sprintf('Pruning cache pool: <info>%s</info>', $name));
74+
$pool->prune();
75+
}
76+
77+
$io->success('Cache was successfully pruned.');
78+
}
79+
80+
/**
81+
* @param InputInterface $input
82+
*
83+
* @return PruneableInterface[]
84+
*/
85+
private function getMatchingPools(InputInterface $input)
86+
{
87+
$allPools = $this->getPrunablePools();
88+
$reqNames = $input->getArgument('pools');
89+
90+
if (0 === count($reqNames)) {
91+
return $allPools;
92+
}
93+
94+
$reqPools = array();
95+
foreach ($input->getArgument('pools') as $name) {
96+
if (!isset($allPools[$name])) {
97+
throw new InvalidArgumentException(sprintf('The "%s" pool does not exist or is not pruneable.', $name));
98+
}
99+
100+
$reqPools[$name] = $allPools[$name];
101+
}
102+
103+
return $reqPools;
104+
}
105+
106+
/**
107+
* @return PruneableInterface[]
108+
*/
109+
private function getPrunablePools()
110+
{
111+
$pools = array();
112+
113+
foreach ($this->pools as $name => $pool) {
114+
if ($pool instanceof PruneableInterface) {
115+
$pools[$name] = $pool;
116+
}
117+
}
118+
119+
return $pools;
120+
}
121+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
class CachePoolPrunerPass implements CompilerPassInterface
20+
{
21+
/**
22+
* {@inheritdoc}
23+
*/
24+
public function process(ContainerBuilder $container)
25+
{
26+
if ($container->hasDefinition('cache.command.pool_pruner')) {
27+
$container->getDefinition('cache.command.pool_pruner')->replaceArgument(0, $this->getCachePoolsIterator($container));
28+
}
29+
}
30+
31+
/**
32+
* @param ContainerBuilder $container
33+
*
34+
* @return IteratorArgument
35+
*/
36+
private function getCachePoolsIterator(ContainerBuilder $container)
37+
{
38+
$services = $this->getConcreteCachePoolServices($container);
39+
40+
return new IteratorArgument(array_combine($services, array_map(function ($id) {
41+
return new Reference($id);
42+
}, $services)));
43+
}
44+
45+
/**
46+
* @param ContainerBuilder $container
47+
*
48+
* @return string[]
49+
*/
50+
private function getConcreteCachePoolServices(ContainerBuilder $container)
51+
{
52+
return array_filter(array_keys($container->findTaggedServiceIds('cache.pool')), function ($name) use ($container) {
53+
return !$container->getDefinition($name)->isAbstract();
54+
});
55+
}
56+
}

src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheCollectorPass;
1717
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass;
1818
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolClearerPass;
19+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPrunerPass;
1920
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\DataCollectorTranslatorPass;
2021
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
2122
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass;
@@ -97,6 +98,7 @@ public function build(ContainerBuilder $container)
9798
$container->addCompilerPass(new LoggingTranslatorPass());
9899
$container->addCompilerPass(new AddCacheWarmerPass());
99100
$container->addCompilerPass(new AddCacheClearerPass());
101+
$container->addCompilerPass(new CachePoolPrunerPass());
100102
$container->addCompilerPass(new AddExpressionLanguageProvidersPass());
101103
$this->addCompilerPassIfExists($container, TranslationExtractorPass::class);
102104
$this->addCompilerPassIfExists($container, TranslationDumperPass::class);

src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@
100100
</call>
101101
</service>
102102

103+
<service id="cache.command.pool_pruner" class="Symfony\Bundle\FrameworkBundle\Command\CachePoolPruneCommand" public="true">
104+
<argument type="iterator" />
105+
<tag name="console.command" command="cache:pool:prune" />
106+
</service>
107+
103108
<service id="cache.default_clearer" class="Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer" public="true">
104109
<tag name="kernel.cache_clearer" />
105110
</service>
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
13+
14+
use Symfony\Bundle\FrameworkBundle\Command\CachePoolPruneCommand;
15+
use Symfony\Bundle\FrameworkBundle\Console\Application;
16+
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
17+
use Symfony\Component\Cache\PruneableInterface;
18+
use Symfony\Component\Console\Tester\CommandTester;
19+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
20+
use Symfony\Component\HttpKernel\KernelInterface;
21+
22+
class CachePruneCommandTest extends TestCase
23+
{
24+
public function testCommandWithNoArgument()
25+
{
26+
$tester = $this->getCommandTester($this->getKernel(), $this->getRewindableGenerator());
27+
$tester->execute(array());
28+
}
29+
30+
public function testCommandWithArgument()
31+
{
32+
$tester = $this->getCommandTester($this->getKernel(), $this->getRewindableGenerator());
33+
$tester->execute(array('pools' => array('my_pool')));
34+
}
35+
36+
/**
37+
* @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException
38+
* @expectedExceptionMessage The "my_invalid_pool" pool does not exist or is not pruneable.
39+
*/
40+
public function testCommandThrowsWithInvalidArgument1()
41+
{
42+
$tester = $this->getCommandTester($this->getKernel(), $this->getRewindableGenerator(false));
43+
$tester->execute(array('pools' => array('my_invalid_pool')));
44+
}
45+
46+
/**
47+
* @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException
48+
* @expectedExceptionMessage The "my_invalid_pool" pool does not exist or is not pruneable.
49+
*/
50+
public function testCommandThrowsWithInvalidArgument2()
51+
{
52+
$tester = $this->getCommandTester($this->getKernel(), $this->getEmptyRewindableGenerator());
53+
$tester->execute(array('pools' => array('my_invalid_pool')));
54+
}
55+
56+
/**
57+
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
58+
* @expectedExceptionMessage No pruneable cache pools found.
59+
*/
60+
public function testCommandThrowsWithNoPools()
61+
{
62+
$tester = $this->getCommandTester($this->getKernel(), $this->getEmptyRewindableGenerator());
63+
$tester->execute(array());
64+
}
65+
66+
/**
67+
* @return RewindableGenerator
68+
*/
69+
private function getRewindableGenerator($expectsPrune = true)
70+
{
71+
return new RewindableGenerator(function () use ($expectsPrune) {
72+
yield 'my_pool' => $this->getPruneable($expectsPrune);
73+
}, 1);
74+
}
75+
76+
/**
77+
* @return RewindableGenerator
78+
*/
79+
private function getEmptyRewindableGenerator()
80+
{
81+
return new RewindableGenerator(function () {
82+
return new \ArrayIterator(array());
83+
}, 1);
84+
}
85+
86+
/**
87+
* @return \PHPUnit_Framework_MockObject_MockObject|KernelInterface
88+
*/
89+
private function getKernel()
90+
{
91+
$container = $this
92+
->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')
93+
->getMock();
94+
95+
$kernel = $this
96+
->getMockBuilder(KernelInterface::class)
97+
->getMock();
98+
99+
$kernel
100+
->expects($this->any())
101+
->method('getContainer')
102+
->willReturn($container);
103+
104+
$kernel
105+
->expects($this->once())
106+
->method('getBundles')
107+
->willReturn(array());
108+
109+
return $kernel;
110+
}
111+
112+
/**
113+
* @return \PHPUnit_Framework_MockObject_MockObject|PruneableInterface
114+
*/
115+
private function getPruneable($expectsPrune = true)
116+
{
117+
$pruneable = $this
118+
->getMockBuilder(PruneableInterface::class)
119+
->getMock();
120+
121+
if (true === $expectsPrune) {
122+
$pruneable
123+
->expects($this->atLeastOnce())
124+
->method('prune');
125+
}
126+
127+
return $pruneable;
128+
}
129+
130+
/**
131+
* @param KernelInterface $kernel
132+
* @param RewindableGenerator $generator
133+
*
134+
* @return CommandTester
135+
*/
136+
private function getCommandTester(KernelInterface $kernel, RewindableGenerator $generator)
137+
{
138+
$application = new Application($kernel);
139+
$application->add(new CachePoolPruneCommand($generator));
140+
141+
return new CommandTester($application->find('cache:pool:prune'));
142+
}
143+
}

0 commit comments

Comments
 (0)
0