Skip to content

Commit c09579a

Browse files
committed
add cache:pool:invalidate-tags command
1 parent 3fb7af0 commit c09579a

File tree

7 files changed

+154
-1
lines changed

7 files changed

+154
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ CHANGELOG
44
6.1
55
---
66

7-
* Environment variable `SYMFONY_IDE` is read by default when `framework.ide` config is not set.
7+
* Environment variable `SYMFONY_IDE` is read by default when `framework.ide` config is not set
8+
* Add `cache:pool:invalidate-tags` command
89

910
6.0
1011
---
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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\Console\Attribute\AsCommand;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\Completion\CompletionInput;
17+
use Symfony\Component\Console\Completion\CompletionSuggestions;
18+
use Symfony\Component\Console\Input\InputArgument;
19+
use Symfony\Component\Console\Input\InputInterface;
20+
use Symfony\Component\Console\Input\InputOption;
21+
use Symfony\Component\Console\Output\OutputInterface;
22+
use Symfony\Component\Console\Style\SymfonyStyle;
23+
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
24+
use Symfony\Contracts\Cache\TagAwareCacheInterface;
25+
use Symfony\Contracts\Service\ServiceProviderInterface;
26+
27+
/**
28+
* @author Kevin Bond <kevinbond@gmail.com>
29+
*/
30+
#[AsCommand(name: 'cache:pool:invalidate-tags', description: 'Invalidate cache tags for all or a specific pool')]
31+
final class CachePoolInvalidateTagsCommand extends Command
32+
{
33+
private ServiceProviderInterface $pools;
34+
private array $poolNames;
35+
36+
public function __construct(ServiceProviderInterface $pools)
37+
{
38+
parent::__construct();
39+
40+
$this->pools = $pools;
41+
$this->poolNames = array_keys($pools->getProvidedServices());
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
protected function configure(): void
48+
{
49+
$this
50+
->addArgument('tags', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'The tags to invalidate')
51+
->addOption('pool', 'p', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The pools to invalidate on')
52+
->setHelp(<<<'EOF'
53+
The <info>%command.name%</info> command invalidates tags from taggable pools. By default, all pools
54+
have the passed tags invalidated. Pass <info>--pool=my_pool</info> to invalidate tags on a specific pool.
55+
56+
php %command.full_name% tag1 tag2
57+
php %command.full_name% tag1 tag2 --pool=cache2 --pool=cache1
58+
EOF)
59+
;
60+
}
61+
62+
/**
63+
* {@inheritdoc}
64+
*/
65+
protected function execute(InputInterface $input, OutputInterface $output): int
66+
{
67+
$io = new SymfonyStyle($input, $output);
68+
$pools = $input->getOption('pool') ?: $this->poolNames;
69+
$tags = $input->getArgument('tags');
70+
$tagList = implode(', ', $tags);
71+
$errors = false;
72+
73+
foreach ($pools as $name) {
74+
$io->comment(sprintf('Invalidating tag(s): <info>%s</info> from pool <comment>%s</comment>.', $tagList, $name));
75+
76+
try {
77+
$this->invalidateTags($name, $tags);
78+
} catch (\Throwable $e) {
79+
$this->getApplication() ? $this->getApplication()->renderThrowable($e, $output) : $io->error($e->getMessage());
80+
$errors = true;
81+
}
82+
}
83+
84+
if ($errors) {
85+
$io->error('Done but with errors.');
86+
87+
return self::FAILURE;
88+
}
89+
90+
$io->success('Successfully invalidated cache tags.');
91+
92+
return self::SUCCESS;
93+
}
94+
95+
private function invalidateTags(string $name, array $tags): void
96+
{
97+
try {
98+
$pool = $this->pools->get($name);
99+
} catch (ServiceNotFoundException $e) {
100+
throw new \InvalidArgumentException(sprintf('Pool "%s" not found.', $name), previous: $e);
101+
}
102+
103+
if (!$pool instanceof TagAwareCacheInterface) {
104+
throw new \LogicException(sprintf('Pool "%s" is not taggable.', $name));
105+
}
106+
107+
if (!$pool->invalidateTags($tags)) {
108+
throw new \Exception(sprintf('Cache tag(s) "%s" could not be invalidated for pool "%s".', implode(', ', $tags), $name));
109+
}
110+
}
111+
112+
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
113+
{
114+
if ($input->mustSuggestOptionValuesFor('pool')) {
115+
$suggestions->suggestValues($this->poolNames);
116+
}
117+
}
118+
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class UnusedTagsPass implements CompilerPassInterface
2727
'auto_alias',
2828
'cache.pool',
2929
'cache.pool.clearer',
30+
'cache.taggable',
3031
'chatter.transport_factory',
3132
'config_cache.resource_checker',
3233
'console.command',

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,6 +2128,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con
21282128
if ($isRedisTagAware) {
21292129
$tagAwareId = $name;
21302130
$container->setAlias('.'.$name.'.inner', $name);
2131+
$definition->addTag('cache.taggable', ['pool' => $name]);
21312132
} elseif ($pool['tags']) {
21322133
if (true !== $pool['tags'] && ($config['pools'][$pool['tags']]['tags'] ?? false)) {
21332134
$pool['tags'] = '.'.$pool['tags'].'.inner';
@@ -2136,6 +2137,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con
21362137
->addArgument(new Reference('.'.$name.'.inner'))
21372138
->addArgument(true !== $pool['tags'] ? new Reference($pool['tags']) : null)
21382139
->setPublic($pool['public'])
2140+
->addTag('cache.taggable', ['pool' => $name])
21392141
;
21402142

21412143
if (method_exists(TagAwareAdapter::class, 'setLogger')) {
@@ -2151,6 +2153,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con
21512153
$tagAwareId = '.'.$name.'.taggable';
21522154
$container->register($tagAwareId, TagAwareAdapter::class)
21532155
->addArgument(new Reference($name))
2156+
->addTag('cache.taggable', ['pool' => $name])
21542157
;
21552158
}
21562159

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
->set('cache.app.taggable', TagAwareAdapter::class)
4141
->args([service('cache.app')])
42+
->tag('cache.taggable', ['pool' => 'cache.app'])
4243

4344
->set('cache.system')
4445
->parent('cache.adapter.system')

src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Bundle\FrameworkBundle\Command\CacheClearCommand;
1717
use Symfony\Bundle\FrameworkBundle\Command\CachePoolClearCommand;
1818
use Symfony\Bundle\FrameworkBundle\Command\CachePoolDeleteCommand;
19+
use Symfony\Bundle\FrameworkBundle\Command\CachePoolInvalidateTagsCommand;
1920
use Symfony\Bundle\FrameworkBundle\Command\CachePoolListCommand;
2021
use Symfony\Bundle\FrameworkBundle\Command\CachePoolPruneCommand;
2122
use Symfony\Bundle\FrameworkBundle\Command\CacheWarmupCommand;
@@ -93,6 +94,12 @@
9394
])
9495
->tag('console.command')
9596

97+
->set('console.command.cache_pool_invalidate_tags', CachePoolInvalidateTagsCommand::class)
98+
->args([
99+
tagged_locator('cache.taggable', 'pool'),
100+
])
101+
->tag('console.command')
102+
96103
->set('console.command.cache_pool_delete', CachePoolDeleteCommand::class)
97104
->args([
98105
service('cache.global_clearer'),
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\Tests\TestCase;
15+
16+
final class CachePoolInvalidateTagsCommandTest extends TestCase
17+
{
18+
public function testCommandWithPools()
19+
{
20+
$this->markTestIncomplete('TODO');
21+
}
22+
}

0 commit comments

Comments
 (0)