[FrameworkBundle] Add cache-pool tag and wiring

This commit is contained in:
Nicolas Grekas 2016-03-04 18:43:16 +01:00
parent 281eafa5cb
commit e44bfdcde9
8 changed files with 134 additions and 161 deletions

View File

@ -1,88 +0,0 @@
<?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\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
*/
class CacheAdapterPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$adapters = array();
foreach ($container->findTaggedServiceIds('cache.adapter') as $id => $tags) {
foreach ($tags as $attributes) {
$adapters[$attributes['id']] = array(
'definition_id' => $id,
'namespace_argument_index' => isset($attributes['namespace-arg-index']) ? $attributes['namespace-arg-index'] : null,
);
}
}
foreach ($container->getDefinitions() as $id => $definition) {
$definition->setArguments($this->resolveArguments($adapters, $id, $definition->getArguments()));
$calls = $definition->getMethodCalls();
foreach ($calls as $index => $call) {
$calls[$index] = array($call[0], $this->resolveArguments($adapters, $id, $call[1]));
}
$definition->setMethodCalls($calls);
$definition->setProperties($this->resolveArguments($adapters, $id, $definition->getProperties()));
}
}
private function resolveArguments(array $adapters, $id, array $arguments)
{
foreach ($arguments as $index => $argument) {
if ($argument instanceof Reference) {
$arguments[$index] = $this->createCacheAdapter($adapters, $id, $argument);
}
}
return $arguments;
}
private function createCacheAdapter(array $adapters, $serviceId, Reference $argument)
{
$adapterId = (string) $argument;
if (0 !== strpos($adapterId, 'cache.adapter.')) {
return $argument;
}
$name = substr($adapterId, 14);
if (!isset($adapters[$name])) {
throw new \InvalidArgumentException(sprintf('The cache adapter "%s" is not configured.', $name));
}
$adapter = new DefinitionDecorator($adapters[$name]['definition_id']);
if (null !== $adapters[$name]['namespace_argument_index']) {
$adapter->replaceArgument($adapters[$name]['namespace_argument_index'], sha1($serviceId));
}
return $adapter;
}
}

View File

@ -0,0 +1,64 @@
<?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\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class CachePoolPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
foreach ($container->findTaggedServiceIds('cache.pool') as $id => $tags) {
$pool = $container->getDefinition($id);
$namespaceArgIndex = isset($tags[0]['namespace_arg_index']) ? $tags[0]['namespace_arg_index'] : -1;
if (!$pool instanceof DefinitionDecorator) {
throw new \InvalidArgumentException(sprintf('Services tagged with "cache.pool" must have a parent service but "%s" has none.', $id));
}
$adapter = $pool;
do {
$adapterId = $adapter->getParent();
$adapter = $container->getDefinition($adapterId);
} while ($adapter instanceof DefinitionDecorator && !$adapter->getTag('cache.adapter'));
$tags = $adapter->getTag('cache.adapter');
if (!isset($tags[0]['namespace_arg_index'])) {
throw new \InvalidArgumentException(sprintf('Invalid "cache.adapter" tag for service "%s": attribute "namespace_arg_index" is missing.', $adapterId));
}
if (!$adapter->isAbstract()) {
throw new \InvalidArgumentException(sprintf('Services tagged as "cache.adapter" must be abstract: "%s" is not.', $adapterId));
}
if (0 <= $namespaceArgIndex) {
$pool->replaceArgument($namespaceArgIndex, $this->getNamespace($id));
}
}
}
private function getNamespace($id)
{
return substr(str_replace('/', '-', base64_encode(md5('symfony.'.$id, true)), 0, 10));
}
}

View File

@ -555,40 +555,22 @@ class Configuration implements ConfigurationInterface
->children()
->arrayNode('cache')
->info('Cache configuration')
->fixXmlConfig('adapter')
->fixXmlConfig('pool')
->children()
->arrayNode('adapters')
->arrayNode('pool')
->useAttributeAsKey('name')
->prototype('array')
->beforeNormalization()
->always(function ($v) {
if (!isset($v['options'])) {
$v['options'] = array();
}
foreach ($v as $key => $value) {
if (!in_array($key, array('type', 'name', 'options'))) {
$v['options'][$key] = $value;
unset($v[$key]);
}
}
return $v;
})
->end()
->children()
->enumNode('type')
->info('The cache adapter type (one of "apcu", "doctrine", "filesystem")')
->info('The cache pool type (one of "apcu", "doctrine", "psr6" or "filesystem")')
->isRequired()
->values(array('apcu', 'doctrine', 'filesystem'))
->end()
->arrayNode('options')
->children()
->integerNode('default_lifetime')->end()
->scalarNode('cache_provider_service')->end()
->scalarNode('directory')->end()
->end()
->values(array('apcu', 'doctrine', 'psr6', 'filesystem'))
->end()
->integerNode('default_lifetime')->default(0)->end()
->scalarNode('cache_provider_service')->defaultNull()->end()
->scalarNode('directory')->defaultNull()->end()
->end()
->end()
->end()

View File

@ -12,9 +12,6 @@
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
@ -1023,43 +1020,24 @@ class FrameworkExtension extends Extension
}
}
private function registerCacheConfiguration(array $config, ContainerBuilder $container)
private function registerCacheConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
foreach ($config['adapters'] as $name => $adapter) {
$class = null;
$arguments = array();
$namespaceArgumentIndex = null;
if (!empty($config['pool'])) {
$loader->load('cache_adapters.xml');
}
switch ($adapter['type']) {
case 'apcu':
$class = ApcuAdapter::class;
$arguments[] = null;
$arguments[] = isset($adapter['options']['default_lifetime']) ? $adapter['options']['default_lifetime'] : 0;
$namespaceArgumentIndex = 0;
break;
case 'doctrine':
$class = DoctrineAdapter::class;
$arguments[] = isset($adapter['options']['cache_provider_service']) ? new Reference($adapter['options']['cache_provider_service']) : null;
$arguments[] = isset($adapter['options']['default_lifetime']) ? $adapter['options']['default_lifetime'] : null;
break;
case 'filesystem':
$class = FilesystemAdapter::class;
$arguments[] = isset($adapter['options']['directory']) ? $adapter['options']['directory'] : null;
$arguments[] = isset($adapter['options']['default_lifetime']) ? $adapter['options']['default_lifetime'] : null;
break;
foreach ($config['pool'] as $name => $poolConfig) {
$poolDefinition = new DefinitionDecorator('cache.adapter.'.$poolConfig['type']);
$poolDefinition->replaceArgument(1, $poolConfig['default_lifetime']);
if ('doctrine' === $poolConfig['type'] || 'psr6' === $poolConfig['type']) {
$poolDefinition->replaceArgument(0, new Reference($poolConfig['cache_provider_service']));
} elseif ('filesystem' === $poolConfig['type'] && isset($poolConfig['directory'][0])) {
$poolDefinition->replaceArgument(0, $poolConfig['directory']);
}
$tagAttributes = array('id' => $name);
if (null !== $namespaceArgumentIndex) {
$tagAttributes['namespace-arg-index'] = $namespaceArgumentIndex;
}
$adapterDefinition = new Definition($class);
$adapterDefinition->setArguments($arguments);
$adapterDefinition->setAbstract(true);
$adapterDefinition->addTag('cache.adapter', $tagAttributes);
$container->setDefinition('cache.adapter.'.$name, $adapterDefinition);
$poolDefinition->addTag('cache.pool');
$container->setDefinition('cache.pool.'.$name, $poolDefinition);
}
}

View File

@ -14,7 +14,7 @@ namespace Symfony\Bundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheAdapterPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass;
@ -90,7 +90,7 @@ class FrameworkBundle extends Bundle
$container->addCompilerPass(new SerializerPass());
$container->addCompilerPass(new PropertyInfoPass());
$container->addCompilerPass(new ControllerArgumentValueResolverPass());
$container->addCompilerPass(new CacheAdapterPass());
$container->addCompilerPass(new CachePoolPass());
if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING);

View File

@ -0,0 +1,37 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="cache.adapter.apcu" class="Symfony\Component\Cache\Adapter\ApcuAdapter" abstract="true">
<tag name="cache.adapter" namespace-arg-index="0"></tag>
<argument /> <!-- namespace -->
<argument /> <!-- default lifetime -->
</service>
<service id="cache.adapter.doctrine" class="Symfony\Component\Cache\Adapter\DoctrineAdapter" abstract="true">
<tag name="cache.adapter" namespace-arg-index="2"></tag>
<argument /> <!-- doctrine provider service -->
<argument /> <!-- default lifetime -->
<argument /> <!-- namespace -->
</service>
<service id="cache.adapter.psr6" class="Symfony\Component\Cache\Adapter\ProxyAdapter" abstract="true">
<tag name="cache.adapter" namespace-arg-index="2"></tag>
<argument /> <!-- PSR-6 provider service -->
<argument /> <!-- default lifetime -->
<argument /> <!-- namespace -->
</service>
<service id="cache.adapter.filesystem" class="Symfony\Component\Cache\Adapter\FilesystemAdapter" abstract="true">
<tag name="cache.adapter" namespace-arg-index="2"></tag>
<argument>%kernel.cache_dir%</argument>
<argument /> <!-- default lifetime -->
<argument /> <!-- namespace -->
</service>
</services>
</container>

View File

@ -206,11 +206,11 @@
<xsd:complexType name="cache">
<xsd:choice minOccurs="1" maxOccurs="unbounded">
<xsd:element name="adapter" type="cache_adapter" />
<xsd:element name="pool" type="cache_pool" />
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="cache_adapter">
<xsd:complexType name="cache_pool">
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="type" type="xsd:string" use="required" />
<xsd:attribute name="default-lifetime" type="xsd:integer" />

View File

@ -11,24 +11,24 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheAdapterPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
class CacheAdapterPassTest extends \PHPUnit_Framework_TestCase
class CachePoolPassTest extends \PHPUnit_Framework_TestCase
{
private $cacheAdapterPass;
private $cachePoolPass;
protected function setUp()
{
$this->cacheAdapterPass = new CacheAdapterPass();
$this->cachePoolPass = new CachePoolPass();
}
public function testAdapterIsInjectedIntoConstructorArguments()
{
$container = $this->initializeContainer();
$this->cacheAdapterPass->process($container);
$this->cachePoolPass->process($container);
$adapter = $container->getDefinition('foo')->getArgument(0);
$this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter);
@ -40,7 +40,7 @@ class CacheAdapterPassTest extends \PHPUnit_Framework_TestCase
public function testAdapterIsInjectedIntoMethodArguments()
{
$container = $this->initializeContainer();
$this->cacheAdapterPass->process($container);
$this->cachePoolPass->process($container);
$methodCalls = $container->getDefinition('bar')->getMethodCalls();
$arguments = $methodCalls[0][1];
$adapter = $arguments[0];
@ -53,7 +53,7 @@ class CacheAdapterPassTest extends \PHPUnit_Framework_TestCase
public function testAdapterIsInjectIntoProperties()
{
$container = $this->initializeContainer();
$this->cacheAdapterPass->process($container);
$this->cachePoolPass->process($container);
$properties = $container->getDefinition('baz')->getProperties();
$adapter = $properties['cache'];
@ -70,7 +70,7 @@ class CacheAdapterPassTest extends \PHPUnit_Framework_TestCase
{
$container = new ContainerBuilder();
$container->setDefinition('foo', new Definition('Foo', array(new Reference('cache.adapter.bar'))));
$this->cacheAdapterPass->process($container);
$this->cachePoolPass->process($container);
}
private function initializeContainer()