From 896be4cc2b7235745911876bbcc74b9c577bf39b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 14 Apr 2018 23:12:15 -0500 Subject: [PATCH] [FrameworkBundle] Allow configuring taggable cache pools --- .../Bundle/FrameworkBundle/CHANGELOG.md | 5 +++++ .../Compiler/CachePoolClearerPass.php | 6 ++--- .../Compiler/CachePoolPass.php | 12 +++++----- .../DependencyInjection/Configuration.php | 1 + .../FrameworkExtension.php | 22 ++++++++++++++++++- .../Compiler/CachePoolClearerPassTest.php | 13 +++++++++-- .../Compiler/CachePoolPassTest.php | 22 +++++++++++++++++++ .../Tests/Functional/CachePoolsTest.php | 20 +++++++++++++++++ .../Functional/app/CachePools/config.yml | 12 ++++++++++ .../app/CachePools/redis_config.yml | 14 ++++++++++++ .../app/CachePools/redis_custom_config.yml | 14 ++++++++++++ 11 files changed, 130 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index b03567d68e..139041aba8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.2.0 +----- + + * Allowed configuring taggable cache pools via a new `framework.cache.pools.tags` option (bool|service-id) + 4.1.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php index 094712ded6..c285e935cb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php @@ -31,9 +31,9 @@ final class CachePoolClearerPass implements CompilerPassInterface foreach ($container->findTaggedServiceIds('cache.pool.clearer') as $id => $attr) { $clearer = $container->getDefinition($id); $pools = array(); - foreach ($clearer->getArgument(0) as $id => $ref) { - if ($container->hasDefinition($id)) { - $pools[$id] = new Reference($id); + foreach ($clearer->getArgument(0) as $name => $ref) { + if ($container->hasDefinition($ref)) { + $pools[$name] = new Reference($ref); } } $clearer->replaceArgument(0, $pools); diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php index 2530d9e75e..670c5edd36 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php @@ -41,6 +41,7 @@ class CachePoolPass implements CompilerPassInterface $clearers = array(); $attributes = array( 'provider', + 'name', 'namespace', 'default_lifetime', 'reset', @@ -56,8 +57,9 @@ class CachePoolPass implements CompilerPassInterface $tags[0] += $t[0]; } } + $name = $tags[0]['name'] ?? $id; if (!isset($tags[0]['namespace'])) { - $tags[0]['namespace'] = $this->getNamespace($seed, $id); + $tags[0]['namespace'] = $this->getNamespace($seed, $name); } if (isset($tags[0]['clearer'])) { $clearer = $tags[0]['clearer']; @@ -67,7 +69,7 @@ class CachePoolPass implements CompilerPassInterface } else { $clearer = null; } - unset($tags[0]['clearer']); + unset($tags[0]['clearer'], $tags[0]['name']); if (isset($tags[0]['provider'])) { $tags[0]['provider'] = new Reference(static::getServiceProvider($container, $tags[0]['provider'])); @@ -86,14 +88,14 @@ class CachePoolPass implements CompilerPassInterface unset($tags[0][$attr]); } if (!empty($tags[0])) { - throw new InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "clearer", "provider", "namespace", "default_lifetime" and "reset", found "%s".', $id, implode('", "', array_keys($tags[0])))); + throw new InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "clearer", "provider", "name", "namespace", "default_lifetime" and "reset", found "%s".', $id, implode('", "', array_keys($tags[0])))); } if (null !== $clearer) { - $clearers[$clearer][$id] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); + $clearers[$clearer][$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); } - $pools[$id] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); + $pools[$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); } $clearer = 'cache.global_clearer'; diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 31e8e6a6ba..e9a380dd78 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -870,6 +870,7 @@ class Configuration implements ConfigurationInterface ->prototype('array') ->children() ->scalarNode('adapter')->defaultValue('cache.app')->end() + ->scalarNode('tags')->defaultNull()->end() ->booleanNode('public')->defaultFalse()->end() ->integerNode('default_lifetime')->end() ->scalarNode('provider') diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 318af6c8b9..f15e9ac12c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -22,6 +22,7 @@ use Symfony\Bundle\FullStack; use Symfony\Component\Cache\Adapter\AbstractAdapter; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderInterface; @@ -1556,12 +1557,31 @@ class FrameworkExtension extends Extension $config['pools']['cache.'.$name] = array( 'adapter' => $config[$name], 'public' => true, + 'tags' => false, ); } foreach ($config['pools'] as $name => $pool) { + if ($config['pools'][$pool['adapter']]['tags'] ?? false) { + $pool['adapter'] = '.'.$pool['adapter'].'.inner'; + } $definition = new ChildDefinition($pool['adapter']); + + if ($pool['tags']) { + if ($config['pools'][$pool['tags']]['tags'] ?? false) { + $pool['tags'] = '.'.$pool['tags'].'.inner'; + } + $container->register($name, TagAwareAdapter::class) + ->addArgument(new Reference('.'.$name.'.inner')) + ->addArgument(true !== $pool['tags'] ? new Reference($pool['tags']) : null) + ->setPublic($pool['public']) + ; + + $pool['name'] = $name; + $pool['public'] = false; + $name = '.'.$name.'.inner'; + } $definition->setPublic($pool['public']); - unset($pool['adapter'], $pool['public']); + unset($pool['adapter'], $pool['public'], $pool['tags']); $definition->addTag('cache.pool', $pool); $container->setDefinition($name, $definition); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php index 9230405d75..243061712a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php @@ -39,6 +39,11 @@ class CachePoolClearerPassTest extends TestCase $publicPool->addTag('cache.pool', array('clearer' => 'clearer_alias')); $container->setDefinition('public.pool', $publicPool); + $publicPool = new Definition(); + $publicPool->addArgument('namespace'); + $publicPool->addTag('cache.pool', array('clearer' => 'clearer_alias', 'name' => 'pool2')); + $container->setDefinition('public.pool2', $publicPool); + $privatePool = new Definition(); $privatePool->setPublic(false); $privatePool->addArgument('namespace'); @@ -55,7 +60,11 @@ class CachePoolClearerPassTest extends TestCase $pass->process($container); } - $this->assertEquals(array(array('public.pool' => new Reference('public.pool'))), $clearer->getArguments()); - $this->assertEquals(array(array('public.pool' => new Reference('public.pool'))), $globalClearer->getArguments()); + $expected = array(array( + 'public.pool' => new Reference('public.pool'), + 'pool2' => new Reference('public.pool2'), + )); + $this->assertEquals($expected, $clearer->getArguments()); + $this->assertEquals($expected, $globalClearer->getArguments()); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php index 4619301b6e..44443a52a5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php @@ -93,6 +93,28 @@ class CachePoolPassTest extends TestCase $this->assertSame(3, $cachePool->getArgument(2)); } + public function testWithNameAttribute() + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('cache.prefix.seed', 'foo'); + $cachePool = new Definition(); + $cachePool->addTag('cache.pool', array( + 'name' => 'foobar', + 'provider' => 'foobar', + )); + $cachePool->addArgument(null); + $cachePool->addArgument(null); + $cachePool->addArgument(null); + $container->setDefinition('app.cache_pool', $cachePool); + + $this->cachePoolPass->process($container); + + $this->assertSame('9HvPgAayyh', $cachePool->getArgument(1)); + } + /** * @expectedException \InvalidArgumentException * @expectedExceptionMessage Invalid "cache.pool" tag for service "app.cache_pool": accepted attributes are diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php index 9cdb93a493..d152f5cd87 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php @@ -13,6 +13,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Component\Cache\Exception\InvalidArgumentException; class CachePoolsTest extends WebTestCase @@ -94,6 +95,25 @@ class CachePoolsTest extends WebTestCase $item = $pool2->getItem($key); $this->assertTrue($item->isHit()); + + $prefix = "\0".TagAwareAdapter::class."\0"; + $pool4 = $container->get('cache.pool4'); + $this->assertInstanceof(TagAwareAdapter::class, $pool4); + $pool4 = (array) $pool4; + $this->assertSame($pool4[$prefix.'pool'], $pool4[$prefix.'tags'] ?? $pool4['tags']); + + $pool5 = $container->get('cache.pool5'); + $this->assertInstanceof(TagAwareAdapter::class, $pool5); + $pool5 = (array) $pool5; + $this->assertSame($pool2, $pool5[$prefix.'tags'] ?? $pool5['tags']); + + $pool6 = $container->get('cache.pool6'); + $this->assertInstanceof(TagAwareAdapter::class, $pool6); + $pool6 = (array) $pool6; + $this->assertSame($pool4[$prefix.'pool'], $pool6[$prefix.'tags'] ?? $pool6['tags']); + + $pool7 = $container->get('cache.pool7'); + $this->assertNotInstanceof(TagAwareAdapter::class, $pool7); } protected static function createKernel(array $options = array()) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/config.yml index de1e144dad..8c7bcb4eb1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/config.yml @@ -12,3 +12,15 @@ framework: adapter: cache.pool3 cache.pool3: clearer: ~ + cache.pool4: + tags: true + public: true + cache.pool5: + tags: cache.pool2 + public: true + cache.pool6: + tags: cache.pool4 + public: true + cache.pool7: + adapter: cache.pool4 + public: true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_config.yml index 3bf10f448f..30c69163d4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_config.yml @@ -15,3 +15,17 @@ framework: cache.pool2: public: true clearer: ~ + cache.pool3: + clearer: ~ + cache.pool4: + tags: true + public: true + cache.pool5: + tags: cache.pool2 + public: true + cache.pool6: + tags: cache.pool4 + public: true + cache.pool7: + adapter: cache.pool4 + public: true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_custom_config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_custom_config.yml index d0a219753e..df20c5357f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_custom_config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_custom_config.yml @@ -26,3 +26,17 @@ framework: cache.pool2: public: true clearer: ~ + cache.pool3: + clearer: ~ + cache.pool4: + tags: true + public: true + cache.pool5: + tags: cache.pool2 + public: true + cache.pool6: + tags: cache.pool4 + public: true + cache.pool7: + adapter: cache.pool4 + public: true