feature #26934 [FrameworkBundle] Allow configuring taggable cache pools (nicolas-grekas)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[FrameworkBundle] Allow configuring taggable cache pools

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #25903
| License       | MIT
| Doc PR        | https://github.com/symfony/symfony-docs/pull/9652

This PR adds a new configuration option for cache pools:
```yaml
framework:
     cache:
         pools:
            app.taggable_pool:
                tags: true
            app.taggable_pool_with_separate_store_for_tags:
                tags: app.my_pool_for_tags
```

Commits
-------

896be4cc2b [FrameworkBundle] Allow configuring taggable cache pools
This commit is contained in:
Nicolas Grekas 2018-05-29 15:16:25 +02:00
commit 143628fa51
11 changed files with 130 additions and 11 deletions

View File

@ -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
-----

View File

@ -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);

View File

@ -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';

View File

@ -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')

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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

View File

@ -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())

View File

@ -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

View File

@ -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

View File

@ -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