From e9f5e71f8f4f6af212c07fdbe21bde365dea4756 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 21 Feb 2016 00:25:19 +0100 Subject: [PATCH 1/6] [FrameworkBundle] integrate the Cache component --- .../Compiler/CacheAdapterPass.php | 88 ++++++++++++++ .../DependencyInjection/Configuration.php | 50 ++++++++ .../FrameworkExtension.php | 47 ++++++++ .../FrameworkBundle/FrameworkBundle.php | 2 + .../Resources/config/schema/symfony-1.0.xsd | 15 +++ .../Compiler/CacheAdapterPassTest.php | 109 ++++++++++++++++++ .../Fixtures/php/cache.php | 28 +++++ .../Fixtures/xml/cache.xml | 15 +++ .../Fixtures/yml/cache.yml | 17 +++ .../FrameworkExtensionTest.php | 47 ++++++++ 10 files changed, 418 insertions(+) create mode 100644 src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheAdapterPass.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CacheAdapterPassTest.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheAdapterPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheAdapterPass.php new file mode 100644 index 0000000000000..d8453caa89fd4 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheAdapterPass.php @@ -0,0 +1,88 @@ + + * + * 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 + */ +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; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index ab55275b20b2c..6b2c4c21164bc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -114,6 +114,7 @@ public function getConfigTreeBuilder() $this->addSerializerSection($rootNode); $this->addPropertyAccessSection($rootNode); $this->addPropertyInfoSection($rootNode); + $this->addCacheSection($rootNode); return $treeBuilder; } @@ -547,4 +548,53 @@ private function addPropertyInfoSection(ArrayNodeDefinition $rootNode) ->end() ; } + + private function addCacheSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('cache') + ->info('Cache configuration') + ->fixXmlConfig('adapter') + ->children() + ->arrayNode('adapters') + ->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")') + ->isRequired() + ->values(array('apcu', 'doctrine', 'filesystem')) + ->end() + ->arrayNode('options') + ->children() + ->integerNode('default_lifetime')->end() + ->scalarNode('cache_provider_service')->end() + ->scalarNode('directory')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 324915ecb211d..8ae830b08f0a1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -12,6 +12,9 @@ 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; @@ -138,6 +141,10 @@ public function load(array $configs, ContainerBuilder $container) $this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader); } + if (isset($config['cache'])) { + $this->registerCacheConfiguration($config['cache'], $container); + } + $loader->load('debug_prod.xml'); $definition = $container->findDefinition('debug.debug_handlers_listener'); @@ -1017,6 +1024,46 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild } } + private function registerCacheConfiguration(array $config, ContainerBuilder $container) + { + foreach ($config['adapters'] as $name => $adapter) { + $class = null; + $arguments = array(); + $namespaceArgumentIndex = null; + + 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; + } + + $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); + } + } + /** * Gets a hash of the kernel root directory. * diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 94062da0039f5..40dfcf0acf76a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -14,6 +14,7 @@ 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\FormPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass; @@ -87,6 +88,7 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING); $container->addCompilerPass(new SerializerPass()); $container->addCompilerPass(new PropertyInfoPass()); + $container->addCompilerPass(new CacheAdapterPass()); if ($container->getParameter('kernel.debug')) { $container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index cead2295ed1ac..79f295205f5ad 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -25,6 +25,7 @@ + @@ -202,4 +203,18 @@ + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CacheAdapterPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CacheAdapterPassTest.php new file mode 100644 index 0000000000000..923189221f9e9 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CacheAdapterPassTest.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheAdapterPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class CacheAdapterPassTest extends \PHPUnit_Framework_TestCase +{ + private $cacheAdapterPass; + + protected function setUp() + { + $this->cacheAdapterPass = new CacheAdapterPass(); + } + + public function testAdapterIsInjectedIntoConstructorArguments() + { + $container = $this->initializeContainer(); + $this->cacheAdapterPass->process($container); + $adapter = $container->getDefinition('foo')->getArgument(0); + + $this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter); + $this->assertFalse($adapter->isAbstract()); + $this->assertSame('cache.adapter.apcu_adapter', $adapter->getParent()); + $this->assertSame('0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', $adapter->getArgument(0)); + } + + public function testAdapterIsInjectedIntoMethodArguments() + { + $container = $this->initializeContainer(); + $this->cacheAdapterPass->process($container); + $methodCalls = $container->getDefinition('bar')->getMethodCalls(); + $arguments = $methodCalls[0][1]; + $adapter = $arguments[0]; + + $this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter); + $this->assertFalse($adapter->isAbstract()); + $this->assertSame('cache.adapter.doctrine_adapter', $adapter->getParent()); + } + + public function testAdapterIsInjectIntoProperties() + { + $container = $this->initializeContainer(); + $this->cacheAdapterPass->process($container); + $properties = $container->getDefinition('baz')->getProperties(); + $adapter = $properties['cache']; + + $this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter); + $this->assertFalse($adapter->isAbstract()); + $this->assertSame('cache.adapter.fs_adapter', $adapter->getParent()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The cache adapter "bar" is not configured + */ + public function testThrowsExceptionWhenReferencedAdapterIsNotConfigured() + { + $container = new ContainerBuilder(); + $container->setDefinition('foo', new Definition('Foo', array(new Reference('cache.adapter.bar')))); + $this->cacheAdapterPass->process($container); + } + + private function initializeContainer() + { + $container = new ContainerBuilder(); + + $apcuAdapter = new Definition('Symfony\Component\Cache\Adapter\ApcuAdapter'); + $apcuAdapter->setAbstract(true); + $apcuAdapter->addTag('cache.adapter', array('id' => 'adapter1', 'namespace-arg-index' => 0)); + $container->setDefinition('cache.adapter.apcu_adapter', $apcuAdapter); + + $doctrineAdapter = new Definition('Symfony\Component\Cache\Adapter\DoctrineAdapter'); + $doctrineAdapter->setAbstract(true); + $doctrineAdapter->addTag('cache.adapter', array('id' => 'adapter2')); + $container->setDefinition('cache.adapter.doctrine_adapter', $doctrineAdapter); + + $filesystemAdapter = new Definition('Symfony\Component\Cache\Adapter\FilesystemAdapter'); + $filesystemAdapter->setAbstract(true); + $filesystemAdapter->addTag('cache.adapter', array('id' => 'adapter3')); + $container->setDefinition('cache.adapter.fs_adapter', $filesystemAdapter); + + $foo = new Definition(); + $foo->setArguments(array(new Reference('cache.adapter.adapter1'))); + $container->setDefinition('foo', $foo); + + $bar = new Definition(); + $bar->addMethodCall('setCache', array(new Reference('cache.adapter.adapter2'))); + $container->setDefinition('bar', $bar); + + $baz = new Definition(); + $baz->setProperty('cache', new Reference('cache.adapter.adapter3')); + $container->setDefinition('baz', $baz); + + return $container; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php new file mode 100644 index 0000000000000..a5c375ab3b740 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php @@ -0,0 +1,28 @@ +loadFromExtension('framework', array( + 'cache' => array( + 'adapters' => array( + 'foo' => array( + 'type' => 'apcu', + 'options' => array( + 'default_lifetime' => 30, + ), + ), + 'bar' => array( + 'type' => 'doctrine', + 'options' => array( + 'default_lifetime' => 5, + 'cache_provider_service' => 'app.doctrine_cache_provider', + ), + ), + 'baz' => array( + 'type' => 'filesystem', + 'options' => array( + 'default_lifetime' => 7, + 'directory' => 'app/cache/psr', + ), + ), + ), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml new file mode 100644 index 0000000000000..086ea63a017af --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml new file mode 100644 index 0000000000000..9d0de6ad89ff1 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml @@ -0,0 +1,17 @@ +framework: + cache: + adapters: + foo: + type: apcu + options: + default_lifetime: 30 + bar: + type: doctrine + options: + default_lifetime: 5 + cache_provider_service: app.doctrine_cache_provider + baz: + type: filesystem + options: + default_lifetime: 7 + directory: app/cache/psr diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 55d9a16e77e5c..6643e7f1f73c8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -13,6 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; +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\DefinitionDecorator; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; @@ -568,6 +571,15 @@ public function testPropertyInfoEnabled() $this->assertTrue($container->has('property_info')); } + public function testCacheAdaptersAbstractServices() + { + $container = $this->createContainerFromFile('cache'); + + $this->assertCacheAdapterIsRegistered($container, 'foo', 'apcu', array(null, 30), 0); + $this->assertCacheAdapterIsRegistered($container, 'bar', 'doctrine', array(new Reference('app.doctrine_cache_provider'), 5)); + $this->assertCacheAdapterIsRegistered($container, 'baz', 'filesystem', array('app/cache/psr', 7)); + } + protected function createContainer(array $data = array()) { return new ContainerBuilder(new ParameterBag(array_merge(array( @@ -636,4 +648,39 @@ private function assertVersionStrategy(ContainerBuilder $container, Reference $r $this->assertEquals($format, $versionStrategy->getArgument(1)); } } + + private function assertCacheAdapterIsRegistered(ContainerBuilder $container, $name, $type, array $arguments, $namespaceArgumentIndex = null) + { + $id = 'cache.adapter.'.$name; + + $this->assertTrue($container->has($id), sprintf('Service definition "%s" for cache adapter of type "%s" is registered', $id, $type)); + + $adapterDefinition = $container->getDefinition($id); + + switch ($type) { + case 'apcu': + $this->assertSame(ApcuAdapter::class, $adapterDefinition->getClass()); + break; + case 'doctrine': + $this->assertSame(DoctrineAdapter::class, $adapterDefinition->getClass()); + break; + case 'filesystem': + $this->assertSame(FilesystemAdapter::class, $adapterDefinition->getClass()); + break; + } + + $this->assertTrue($adapterDefinition->isAbstract(), sprintf('Service definition "%s" for cache adapter "%s" is abstract', $id, $name)); + $this->assertEquals($arguments, $adapterDefinition->getArguments()); + $this->assertTrue($adapterDefinition->hasTag('cache.adapter'), sprintf('Service definition "%s" is tagged with the "cache.adapter" tag.', $id)); + + $tag = $adapterDefinition->getTag('cache.adapter'); + + $this->assertTrue(isset($tag[0]['id']), 'The adapter name is the "id" attribute of the "cache.adapter" tag.'); + $this->assertSame($name, $tag[0]['id'], 'The adapter name is the "id" attribute of the "cache.adapter" tag.'); + + if (null !== $namespaceArgumentIndex) { + $this->assertTrue(isset($tag[0]['namespace-arg-index']), 'The namespace argument index is given by the "namespace-arg-index" attribute of the "cache.adapter" tag.'); + $this->assertSame($namespaceArgumentIndex, $tag[0]['namespace-arg-index'], 'The namespace argument index is given by the "namespace-arg-index" attribute of the "cache.adapter" tag.'); + } + } } From 0bfd3f591740eb0f7d18efb26be13f3c44b85f99 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 4 Mar 2016 18:43:16 +0100 Subject: [PATCH 2/6] [FrameworkBundle] Add wiring for cache-pool and cache-adapter service tags --- .../Compiler/CacheAdapterPass.php | 88 ------------------- .../Compiler/CachePoolPass.php | 64 ++++++++++++++ .../DependencyInjection/Configuration.php | 30 ++----- .../FrameworkExtension.php | 48 +++------- .../FrameworkBundle/FrameworkBundle.php | 4 +- .../Resources/config/cache_adapters.xml | 30 +++++++ .../Resources/config/schema/symfony-1.0.xsd | 4 +- ...pterPassTest.php => CachePoolPassTest.php} | 16 ++-- 8 files changed, 125 insertions(+), 159 deletions(-) delete mode 100644 src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheAdapterPass.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml rename src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/{Compiler/CacheAdapterPassTest.php => CachePoolPassTest.php} (91%) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheAdapterPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheAdapterPass.php deleted file mode 100644 index d8453caa89fd4..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheAdapterPass.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * 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 - */ -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; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php new file mode 100644 index 0000000000000..e4e6487209fe1 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php @@ -0,0 +1,64 @@ + + * + * 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 + */ +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)); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 6b2c4c21164bc..fed11eac7d8ea 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -555,40 +555,22 @@ private function addCacheSection(ArrayNodeDefinition $rootNode) ->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" 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() - ->end() + ->integerNode('default_lifetime')->default(0)->end() + ->scalarNode('cache_provider_service')->defaultNull()->end() + ->scalarNode('directory')->defaultNull()->end() ->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 8ae830b08f0a1..28be434f40ffd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -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; @@ -1024,43 +1021,24 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild } } - 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; - - 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; - } + if (!empty($config['pool'])) { + $loader->load('cache_adapters.xml'); + } - $tagAttributes = array('id' => $name); + foreach ($config['pool'] as $name => $poolConfig) { + $poolDefinition = new DefinitionDecorator('cache.adapter.'.$poolConfig['type']); + $poolDefinition->replaceArgument(1, $poolConfig['default_lifetime']); - if (null !== $namespaceArgumentIndex) { - $tagAttributes['namespace-arg-index'] = $namespaceArgumentIndex; + if ('doctrine' === $poolConfig['type']) { + $poolDefinition->replaceArgument(0, new Reference($poolConfig['cache_provider_service'])); + } elseif ('filesystem' === $poolConfig['type'] && isset($poolConfig['directory'][0])) { + $poolDefinition->replaceArgument(0, $poolConfig['directory']); } - $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); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 40dfcf0acf76a..9c0c91eda6b1f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -14,7 +14,7 @@ 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\FormPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass; @@ -88,7 +88,7 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING); $container->addCompilerPass(new SerializerPass()); $container->addCompilerPass(new PropertyInfoPass()); - $container->addCompilerPass(new CacheAdapterPass()); + $container->addCompilerPass(new CachePoolPass()); if ($container->getParameter('kernel.debug')) { $container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml new file mode 100644 index 0000000000000..02a9e6dfb5ccb --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + %kernel.cache_dir% + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index 79f295205f5ad..cee9299e44e58 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -206,11 +206,11 @@ - + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CacheAdapterPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/CachePoolPassTest.php similarity index 91% rename from src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CacheAdapterPassTest.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/CachePoolPassTest.php index 923189221f9e9..033809fcda76d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CacheAdapterPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/CachePoolPassTest.php @@ -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 @@ public function testAdapterIsInjectedIntoConstructorArguments() 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 @@ public function testAdapterIsInjectedIntoMethodArguments() 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 @@ public function testThrowsExceptionWhenReferencedAdapterIsNotConfigured() { $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() From 6bf21a485eda2500887b973f0710ab775522d37a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 10 Mar 2016 18:21:29 +0100 Subject: [PATCH 3/6] [FrameworkBundle] Add PSR-6 cache pool proxying --- .../DependencyInjection/Configuration.php | 4 ++-- .../DependencyInjection/FrameworkExtension.php | 2 +- .../FrameworkBundle/Resources/config/cache_adapters.xml | 9 ++++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index fed11eac7d8ea..d5ee6e65ac266 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -564,9 +564,9 @@ private function addCacheSection(ArrayNodeDefinition $rootNode) ->end() ->children() ->enumNode('type') - ->info('The cache pool type (one of "apcu", "doctrine" or "filesystem")') + ->info('The cache pool type (one of "apcu", "doctrine", "psr6" or "filesystem")') ->isRequired() - ->values(array('apcu', 'doctrine', 'filesystem')) + ->values(array('apcu', 'doctrine', 'psr6', 'filesystem')) ->end() ->integerNode('default_lifetime')->default(0)->end() ->scalarNode('cache_provider_service')->defaultNull()->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 28be434f40ffd..d0d8d1a29e499 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1031,7 +1031,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con $poolDefinition = new DefinitionDecorator('cache.adapter.'.$poolConfig['type']); $poolDefinition->replaceArgument(1, $poolConfig['default_lifetime']); - if ('doctrine' === $poolConfig['type']) { + 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']); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml index 02a9e6dfb5ccb..13612a1d28612 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml @@ -14,7 +14,14 @@ - + + + + + + + + From fba8f64a0cc07609f4591c2d66e790cf0c0c6a6b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 11 Mar 2016 09:48:42 +0100 Subject: [PATCH 4/6] fix cache pool wiring --- .../DependencyInjection/Compiler/CachePoolPass.php | 2 +- .../FrameworkBundle/DependencyInjection/Configuration.php | 4 +--- .../DependencyInjection/{ => Compiler}/CachePoolPassTest.php | 0 3 files changed, 2 insertions(+), 4 deletions(-) rename src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/{ => Compiler}/CachePoolPassTest.php (100%) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php index e4e6487209fe1..de32ab761ddcf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php @@ -59,6 +59,6 @@ public function process(ContainerBuilder $container) private function getNamespace($id) { - return substr(str_replace('/', '-', base64_encode(md5('symfony.'.$id, true)), 0, 10)); + return substr(str_replace('/', '-', base64_encode(md5('symfony.'.$id, true))), 0, 10); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index d5ee6e65ac266..59ecd8c7c1c51 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -560,15 +560,13 @@ private function addCacheSection(ArrayNodeDefinition $rootNode) ->arrayNode('pool') ->useAttributeAsKey('name') ->prototype('array') - ->beforeNormalization() - ->end() ->children() ->enumNode('type') ->info('The cache pool type (one of "apcu", "doctrine", "psr6" or "filesystem")') ->isRequired() ->values(array('apcu', 'doctrine', 'psr6', 'filesystem')) ->end() - ->integerNode('default_lifetime')->default(0)->end() + ->integerNode('default_lifetime')->defaultValue(0)->end() ->scalarNode('cache_provider_service')->defaultNull()->end() ->scalarNode('directory')->defaultNull()->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/CachePoolPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/CachePoolPassTest.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php From aecb0078de7c7b73a6920be51a716a321bf702cd Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 11 Mar 2016 10:22:28 +0100 Subject: [PATCH 5/6] fix FrameworkExtension tests --- .../Compiler/CachePoolPass.php | 10 +- .../DependencyInjection/Configuration.php | 2 +- .../FrameworkExtension.php | 6 +- .../Resources/config/cache_adapters.xml | 8 +- .../Compiler/CachePoolPassTest.php | 113 +++++++++--------- .../Fixtures/php/cache.php | 18 +-- .../Fixtures/xml/cache.xml | 6 +- .../Fixtures/yml/cache.yml | 15 +-- .../FrameworkExtensionTest.php | 26 ++-- 9 files changed, 98 insertions(+), 106 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php index de32ab761ddcf..395169caaca47 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php @@ -14,7 +14,6 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\DefinitionDecorator; -use Symfony\Component\DependencyInjection\Reference; /** * @author Nicolas Grekas @@ -28,7 +27,6 @@ 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)); @@ -39,7 +37,11 @@ public function process(ContainerBuilder $container) do { $adapterId = $adapter->getParent(); $adapter = $container->getDefinition($adapterId); - } while ($adapter instanceof DefinitionDecorator && !$adapter->getTag('cache.adapter')); + } while ($adapter instanceof DefinitionDecorator && !$adapter->hasTag('cache.adapter')); + + if (!$adapter->hasTag('cache.adapter')) { + throw new \InvalidArgumentException(sprintf('Services tagged with "cache.pool" must have a parent service tagged with "cache.adapter" but "%s" has none.', $id)); + } $tags = $adapter->getTag('cache.adapter'); @@ -51,7 +53,7 @@ public function process(ContainerBuilder $container) throw new \InvalidArgumentException(sprintf('Services tagged as "cache.adapter" must be abstract: "%s" is not.', $adapterId)); } - if (0 <= $namespaceArgIndex) { + if (0 <= $namespaceArgIndex = $tags[0]['namespace_arg_index']) { $pool->replaceArgument($namespaceArgIndex, $this->getNamespace($id)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 59ecd8c7c1c51..83981d8c76603 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -557,7 +557,7 @@ private function addCacheSection(ArrayNodeDefinition $rootNode) ->info('Cache configuration') ->fixXmlConfig('pool') ->children() - ->arrayNode('pool') + ->arrayNode('pools') ->useAttributeAsKey('name') ->prototype('array') ->children() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index d0d8d1a29e499..956610f410c85 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -139,7 +139,7 @@ public function load(array $configs, ContainerBuilder $container) } if (isset($config['cache'])) { - $this->registerCacheConfiguration($config['cache'], $container); + $this->registerCacheConfiguration($config['cache'], $container, $loader); } $loader->load('debug_prod.xml'); @@ -1023,11 +1023,11 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild private function registerCacheConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - if (!empty($config['pool'])) { + if (!empty($config['pools'])) { $loader->load('cache_adapters.xml'); } - foreach ($config['pool'] as $name => $poolConfig) { + foreach ($config['pools'] as $name => $poolConfig) { $poolDefinition = new DefinitionDecorator('cache.adapter.'.$poolConfig['type']); $poolDefinition->replaceArgument(1, $poolConfig['default_lifetime']); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml index 13612a1d28612..9c49c8672de8d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_adapters.xml @@ -7,27 +7,27 @@ - + - + - + - + %kernel.cache_dir% diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php index 033809fcda76d..f07c04c7e0767 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php @@ -14,7 +14,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\DefinitionDecorator; class CachePoolPassTest extends \PHPUnit_Framework_TestCase { @@ -25,85 +25,84 @@ protected function setUp() $this->cachePoolPass = new CachePoolPass(); } - public function testAdapterIsInjectedIntoConstructorArguments() + public function testNamespaceArgumentIsReplaced() { - $container = $this->initializeContainer(); + $container = new ContainerBuilder(); + $adapter = new Definition(); + $adapter->setAbstract(true); + $adapter->addTag('cache.adapter', array('namespace_arg_index' => 0)); + $container->setDefinition('app.cache_adapter', $adapter); + $cachePool = new DefinitionDecorator('app.cache_adapter'); + $cachePool->addArgument(null); + $cachePool->addTag('cache.pool'); + $container->setDefinition('app.cache_pool', $cachePool); + $this->cachePoolPass->process($container); - $adapter = $container->getDefinition('foo')->getArgument(0); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter); - $this->assertFalse($adapter->isAbstract()); - $this->assertSame('cache.adapter.apcu_adapter', $adapter->getParent()); - $this->assertSame('0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', $adapter->getArgument(0)); + $this->assertSame('yRnzIIVLvL', $cachePool->getArgument(0)); } - public function testAdapterIsInjectedIntoMethodArguments() + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Services tagged with "cache.pool" must have a parent service but "app.cache_pool" has none. + */ + public function testThrowsExceptionWhenCachePoolHasNoParentDefinition() { - $container = $this->initializeContainer(); - $this->cachePoolPass->process($container); - $methodCalls = $container->getDefinition('bar')->getMethodCalls(); - $arguments = $methodCalls[0][1]; - $adapter = $arguments[0]; + $container = new ContainerBuilder(); + $cachePool = new Definition(); + $cachePool->addTag('cache.pool'); + $container->setDefinition('app.cache_pool', $cachePool); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter); - $this->assertFalse($adapter->isAbstract()); - $this->assertSame('cache.adapter.doctrine_adapter', $adapter->getParent()); + $this->cachePoolPass->process($container); } - public function testAdapterIsInjectIntoProperties() + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Services tagged with "cache.pool" must have a parent service tagged with "cache.adapter" but "app.cache_pool" has none. + */ + public function testThrowsExceptionWhenCachePoolIsNotBasedOnAdapter() { - $container = $this->initializeContainer(); - $this->cachePoolPass->process($container); - $properties = $container->getDefinition('baz')->getProperties(); - $adapter = $properties['cache']; + $container = new ContainerBuilder(); + $container->register('app.cache_adapter'); + $cachePool = new DefinitionDecorator('app.cache_adapter'); + $cachePool->addTag('cache.pool'); + $container->setDefinition('app.cache_pool', $cachePool); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter); - $this->assertFalse($adapter->isAbstract()); - $this->assertSame('cache.adapter.fs_adapter', $adapter->getParent()); + $this->cachePoolPass->process($container); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The cache adapter "bar" is not configured + * @expectedExceptionMessage Invalid "cache.adapter" tag for service "app.cache_adapter": attribute "namespace_arg_index" is missing. */ - public function testThrowsExceptionWhenReferencedAdapterIsNotConfigured() + public function testThrowsExceptionWhenCacheAdapterDefinesNoNamespaceArgument() { $container = new ContainerBuilder(); - $container->setDefinition('foo', new Definition('Foo', array(new Reference('cache.adapter.bar')))); + $adapter = new Definition(); + $adapter->setAbstract(true); + $adapter->addTag('cache.adapter'); + $container->setDefinition('app.cache_adapter', $adapter); + $cachePool = new DefinitionDecorator('app.cache_adapter'); + $cachePool->addTag('cache.pool'); + $container->setDefinition('app.cache_pool', $cachePool); + $this->cachePoolPass->process($container); } - private function initializeContainer() + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Services tagged as "cache.adapter" must be abstract: "app.cache_adapter" is not. + */ + public function testThrowsExceptionWhenCacheAdapterIsNotAbstract() { $container = new ContainerBuilder(); + $adapter = new Definition(); + $adapter->addTag('cache.adapter', array('namespace_arg_index' => 0)); + $container->setDefinition('app.cache_adapter', $adapter); + $cachePool = new DefinitionDecorator('app.cache_adapter'); + $cachePool->addTag('cache.pool'); + $container->setDefinition('app.cache_pool', $cachePool); - $apcuAdapter = new Definition('Symfony\Component\Cache\Adapter\ApcuAdapter'); - $apcuAdapter->setAbstract(true); - $apcuAdapter->addTag('cache.adapter', array('id' => 'adapter1', 'namespace-arg-index' => 0)); - $container->setDefinition('cache.adapter.apcu_adapter', $apcuAdapter); - - $doctrineAdapter = new Definition('Symfony\Component\Cache\Adapter\DoctrineAdapter'); - $doctrineAdapter->setAbstract(true); - $doctrineAdapter->addTag('cache.adapter', array('id' => 'adapter2')); - $container->setDefinition('cache.adapter.doctrine_adapter', $doctrineAdapter); - - $filesystemAdapter = new Definition('Symfony\Component\Cache\Adapter\FilesystemAdapter'); - $filesystemAdapter->setAbstract(true); - $filesystemAdapter->addTag('cache.adapter', array('id' => 'adapter3')); - $container->setDefinition('cache.adapter.fs_adapter', $filesystemAdapter); - - $foo = new Definition(); - $foo->setArguments(array(new Reference('cache.adapter.adapter1'))); - $container->setDefinition('foo', $foo); - - $bar = new Definition(); - $bar->addMethodCall('setCache', array(new Reference('cache.adapter.adapter2'))); - $container->setDefinition('bar', $bar); - - $baz = new Definition(); - $baz->setProperty('cache', new Reference('cache.adapter.adapter3')); - $container->setDefinition('baz', $baz); - - return $container; + $this->cachePoolPass->process($container); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php index a5c375ab3b740..c35d3106998be 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php @@ -2,26 +2,20 @@ $container->loadFromExtension('framework', array( 'cache' => array( - 'adapters' => array( + 'pools' => array( 'foo' => array( 'type' => 'apcu', - 'options' => array( - 'default_lifetime' => 30, - ), + 'default_lifetime' => 30, ), 'bar' => array( 'type' => 'doctrine', - 'options' => array( - 'default_lifetime' => 5, - 'cache_provider_service' => 'app.doctrine_cache_provider', - ), + 'default_lifetime' => 5, + 'cache_provider_service' => 'app.doctrine_cache_provider', ), 'baz' => array( 'type' => 'filesystem', - 'options' => array( - 'default_lifetime' => 7, - 'directory' => 'app/cache/psr', - ), + 'default_lifetime' => 7, + 'directory' => 'app/cache/psr', ), ), ), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml index 086ea63a017af..1bdc9f2375cdb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml @@ -7,9 +7,9 @@ - - - + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml index 9d0de6ad89ff1..c146b6741d182 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml @@ -1,17 +1,14 @@ framework: cache: - adapters: + pools: foo: type: apcu - options: - default_lifetime: 30 + default_lifetime: 30 bar: type: doctrine - options: - default_lifetime: 5 - cache_provider_service: app.doctrine_cache_provider + default_lifetime: 5 + cache_provider_service: app.doctrine_cache_provider baz: type: filesystem - options: - default_lifetime: 7 - directory: app/cache/psr + default_lifetime: 7 + directory: app/cache/psr diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 6643e7f1f73c8..c303c8797b153 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -571,13 +571,13 @@ public function testPropertyInfoEnabled() $this->assertTrue($container->has('property_info')); } - public function testCacheAdaptersAbstractServices() + public function testCachePoolServices() { $container = $this->createContainerFromFile('cache'); - $this->assertCacheAdapterIsRegistered($container, 'foo', 'apcu', array(null, 30), 0); - $this->assertCacheAdapterIsRegistered($container, 'bar', 'doctrine', array(new Reference('app.doctrine_cache_provider'), 5)); - $this->assertCacheAdapterIsRegistered($container, 'baz', 'filesystem', array('app/cache/psr', 7)); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'foo', 'apcu', array('index_1' => 30), 0); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'bar', 'doctrine', array('index_0' => new Reference('app.doctrine_cache_provider'), 'index_1' => 5)); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'baz', 'filesystem', array('index_0' => 'app/cache/psr', 'index_1' => 7)); } protected function createContainer(array $data = array()) @@ -649,13 +649,18 @@ private function assertVersionStrategy(ContainerBuilder $container, Reference $r } } - private function assertCacheAdapterIsRegistered(ContainerBuilder $container, $name, $type, array $arguments, $namespaceArgumentIndex = null) + private function assertCachePoolServiceDefinitionIsCreated(ContainerBuilder $container, $name, $type, array $arguments, $namespaceArgumentIndex = null) { - $id = 'cache.adapter.'.$name; + $id = 'cache.pool.'.$name; - $this->assertTrue($container->has($id), sprintf('Service definition "%s" for cache adapter of type "%s" is registered', $id, $type)); + $this->assertTrue($container->has($id), sprintf('Service definition "%s" for cache pool of type "%s" is registered', $id, $type)); - $adapterDefinition = $container->getDefinition($id); + $poolDefinition = $container->getDefinition($id); + + $this->assertInstanceOf(DefinitionDecorator::class, $poolDefinition, sprintf('Cache pool "%s" is based on an abstract cache adapter.', $name)); + $this->assertEquals($arguments, $poolDefinition->getArguments()); + + $adapterDefinition = $container->getDefinition($poolDefinition->getParent()); switch ($type) { case 'apcu': @@ -669,15 +674,10 @@ private function assertCacheAdapterIsRegistered(ContainerBuilder $container, $na break; } - $this->assertTrue($adapterDefinition->isAbstract(), sprintf('Service definition "%s" for cache adapter "%s" is abstract', $id, $name)); - $this->assertEquals($arguments, $adapterDefinition->getArguments()); $this->assertTrue($adapterDefinition->hasTag('cache.adapter'), sprintf('Service definition "%s" is tagged with the "cache.adapter" tag.', $id)); $tag = $adapterDefinition->getTag('cache.adapter'); - $this->assertTrue(isset($tag[0]['id']), 'The adapter name is the "id" attribute of the "cache.adapter" tag.'); - $this->assertSame($name, $tag[0]['id'], 'The adapter name is the "id" attribute of the "cache.adapter" tag.'); - if (null !== $namespaceArgumentIndex) { $this->assertTrue(isset($tag[0]['namespace-arg-index']), 'The namespace argument index is given by the "namespace-arg-index" attribute of the "cache.adapter" tag.'); $this->assertSame($namespaceArgumentIndex, $tag[0]['namespace-arg-index'], 'The namespace argument index is given by the "namespace-arg-index" attribute of the "cache.adapter" tag.'); From 5e927b4c3539e72c367a8cdc52459bac95d42037 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 11 Mar 2016 18:32:55 +0100 Subject: [PATCH 6/6] add PSR-6 type tests --- .../Tests/DependencyInjection/Fixtures/php/cache.php | 5 +++++ .../Tests/DependencyInjection/Fixtures/xml/cache.xml | 1 + .../Tests/DependencyInjection/Fixtures/yml/cache.yml | 4 ++++ .../Tests/DependencyInjection/FrameworkExtensionTest.php | 1 + 4 files changed, 11 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php index c35d3106998be..63e5441293f60 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php @@ -17,6 +17,11 @@ 'default_lifetime' => 7, 'directory' => 'app/cache/psr', ), + 'foobar' => array( + 'type' => 'psr6', + 'default_lifetime' => 10, + 'cache_provider_service' => 'app.cache_pool', + ), ), ), )); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml index 1bdc9f2375cdb..f3d26f7380290 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml @@ -10,6 +10,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml index c146b6741d182..0d45b13527161 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml @@ -12,3 +12,7 @@ framework: type: filesystem default_lifetime: 7 directory: app/cache/psr + foobar: + type: psr6 + default_lifetime: 10 + cache_provider_service: app.cache_pool diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index c303c8797b153..93478df449b99 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -578,6 +578,7 @@ public function testCachePoolServices() $this->assertCachePoolServiceDefinitionIsCreated($container, 'foo', 'apcu', array('index_1' => 30), 0); $this->assertCachePoolServiceDefinitionIsCreated($container, 'bar', 'doctrine', array('index_0' => new Reference('app.doctrine_cache_provider'), 'index_1' => 5)); $this->assertCachePoolServiceDefinitionIsCreated($container, 'baz', 'filesystem', array('index_0' => 'app/cache/psr', 'index_1' => 7)); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'foobar', 'psr6', array('index_0' => new Reference('app.cache_pool'), 'index_1' => 10)); } protected function createContainer(array $data = array())