Skip to content

Commit 281eafa

Browse files
xabbuhnicolas-grekas
authored andcommitted
[FrameworkBundle] Integrate the Cache component
1 parent bc51fde commit 281eafa

File tree

10 files changed

+418
-0
lines changed

10 files changed

+418
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\DefinitionDecorator;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
/**
20+
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
21+
*/
22+
class CacheAdapterPass implements CompilerPassInterface
23+
{
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function process(ContainerBuilder $container)
28+
{
29+
$adapters = array();
30+
31+
foreach ($container->findTaggedServiceIds('cache.adapter') as $id => $tags) {
32+
foreach ($tags as $attributes) {
33+
$adapters[$attributes['id']] = array(
34+
'definition_id' => $id,
35+
'namespace_argument_index' => isset($attributes['namespace-arg-index']) ? $attributes['namespace-arg-index'] : null,
36+
);
37+
}
38+
}
39+
40+
foreach ($container->getDefinitions() as $id => $definition) {
41+
$definition->setArguments($this->resolveArguments($adapters, $id, $definition->getArguments()));
42+
43+
$calls = $definition->getMethodCalls();
44+
45+
foreach ($calls as $index => $call) {
46+
$calls[$index] = array($call[0], $this->resolveArguments($adapters, $id, $call[1]));
47+
}
48+
49+
$definition->setMethodCalls($calls);
50+
51+
$definition->setProperties($this->resolveArguments($adapters, $id, $definition->getProperties()));
52+
}
53+
}
54+
55+
private function resolveArguments(array $adapters, $id, array $arguments)
56+
{
57+
foreach ($arguments as $index => $argument) {
58+
if ($argument instanceof Reference) {
59+
$arguments[$index] = $this->createCacheAdapter($adapters, $id, $argument);
60+
}
61+
}
62+
63+
return $arguments;
64+
}
65+
66+
private function createCacheAdapter(array $adapters, $serviceId, Reference $argument)
67+
{
68+
$adapterId = (string) $argument;
69+
70+
if (0 !== strpos($adapterId, 'cache.adapter.')) {
71+
return $argument;
72+
}
73+
74+
$name = substr($adapterId, 14);
75+
76+
if (!isset($adapters[$name])) {
77+
throw new \InvalidArgumentException(sprintf('The cache adapter "%s" is not configured.', $name));
78+
}
79+
80+
$adapter = new DefinitionDecorator($adapters[$name]['definition_id']);
81+
82+
if (null !== $adapters[$name]['namespace_argument_index']) {
83+
$adapter->replaceArgument($adapters[$name]['namespace_argument_index'], sha1($serviceId));
84+
}
85+
86+
return $adapter;
87+
}
88+
}

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

+50
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public function getConfigTreeBuilder()
114114
$this->addSerializerSection($rootNode);
115115
$this->addPropertyAccessSection($rootNode);
116116
$this->addPropertyInfoSection($rootNode);
117+
$this->addCacheSection($rootNode);
117118

118119
return $treeBuilder;
119120
}
@@ -547,4 +548,53 @@ private function addPropertyInfoSection(ArrayNodeDefinition $rootNode)
547548
->end()
548549
;
549550
}
551+
552+
private function addCacheSection(ArrayNodeDefinition $rootNode)
553+
{
554+
$rootNode
555+
->children()
556+
->arrayNode('cache')
557+
->info('Cache configuration')
558+
->fixXmlConfig('adapter')
559+
->children()
560+
->arrayNode('adapters')
561+
->useAttributeAsKey('name')
562+
->prototype('array')
563+
->beforeNormalization()
564+
->always(function ($v) {
565+
if (!isset($v['options'])) {
566+
$v['options'] = array();
567+
}
568+
569+
foreach ($v as $key => $value) {
570+
if (!in_array($key, array('type', 'name', 'options'))) {
571+
$v['options'][$key] = $value;
572+
unset($v[$key]);
573+
}
574+
}
575+
576+
return $v;
577+
})
578+
->end()
579+
->children()
580+
->enumNode('type')
581+
->info('The cache adapter type (one of "apcu", "doctrine", "filesystem")')
582+
->isRequired()
583+
->values(array('apcu', 'doctrine', 'filesystem'))
584+
->end()
585+
->arrayNode('options')
586+
->children()
587+
->integerNode('default_lifetime')->end()
588+
->scalarNode('cache_provider_service')->end()
589+
->scalarNode('directory')->end()
590+
->end()
591+
->end()
592+
->end()
593+
->end()
594+
->end()
595+
->end()
596+
->end()
597+
->end()
598+
;
599+
}
550600
}

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

+47
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
1313

1414
use Doctrine\Common\Annotations\Reader;
15+
use Symfony\Component\Cache\Adapter\ApcuAdapter;
16+
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
17+
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
1518
use Symfony\Component\DependencyInjection\ContainerBuilder;
1619
use Symfony\Component\DependencyInjection\ContainerInterface;
1720
use Symfony\Component\DependencyInjection\Definition;
@@ -138,6 +141,10 @@ public function load(array $configs, ContainerBuilder $container)
138141
$this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader);
139142
}
140143

144+
if (isset($config['cache'])) {
145+
$this->registerCacheConfiguration($config['cache'], $container);
146+
}
147+
141148
$loader->load('debug_prod.xml');
142149
$definition = $container->findDefinition('debug.debug_handlers_listener');
143150

@@ -1016,6 +1023,46 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild
10161023
}
10171024
}
10181025

1026+
private function registerCacheConfiguration(array $config, ContainerBuilder $container)
1027+
{
1028+
foreach ($config['adapters'] as $name => $adapter) {
1029+
$class = null;
1030+
$arguments = array();
1031+
$namespaceArgumentIndex = null;
1032+
1033+
switch ($adapter['type']) {
1034+
case 'apcu':
1035+
$class = ApcuAdapter::class;
1036+
$arguments[] = null;
1037+
$arguments[] = isset($adapter['options']['default_lifetime']) ? $adapter['options']['default_lifetime'] : 0;
1038+
$namespaceArgumentIndex = 0;
1039+
break;
1040+
case 'doctrine':
1041+
$class = DoctrineAdapter::class;
1042+
$arguments[] = isset($adapter['options']['cache_provider_service']) ? new Reference($adapter['options']['cache_provider_service']) : null;
1043+
$arguments[] = isset($adapter['options']['default_lifetime']) ? $adapter['options']['default_lifetime'] : null;
1044+
break;
1045+
case 'filesystem':
1046+
$class = FilesystemAdapter::class;
1047+
$arguments[] = isset($adapter['options']['directory']) ? $adapter['options']['directory'] : null;
1048+
$arguments[] = isset($adapter['options']['default_lifetime']) ? $adapter['options']['default_lifetime'] : null;
1049+
break;
1050+
}
1051+
1052+
$tagAttributes = array('id' => $name);
1053+
1054+
if (null !== $namespaceArgumentIndex) {
1055+
$tagAttributes['namespace-arg-index'] = $namespaceArgumentIndex;
1056+
}
1057+
1058+
$adapterDefinition = new Definition($class);
1059+
$adapterDefinition->setArguments($arguments);
1060+
$adapterDefinition->setAbstract(true);
1061+
$adapterDefinition->addTag('cache.adapter', $tagAttributes);
1062+
$container->setDefinition('cache.adapter.'.$name, $adapterDefinition);
1063+
}
1064+
}
1065+
10191066
/**
10201067
* Gets a hash of the kernel root directory.
10211068
*

src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
1515
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass;
1616
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass;
17+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheAdapterPass;
1718
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass;
1819
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass;
1920
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass;
@@ -89,6 +90,7 @@ public function build(ContainerBuilder $container)
8990
$container->addCompilerPass(new SerializerPass());
9091
$container->addCompilerPass(new PropertyInfoPass());
9192
$container->addCompilerPass(new ControllerArgumentValueResolverPass());
93+
$container->addCompilerPass(new CacheAdapterPass());
9294

9395
if ($container->getParameter('kernel.debug')) {
9496
$container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING);

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

+15
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<xsd:element name="property-access" type="property_access" minOccurs="0" maxOccurs="1" />
2626
<xsd:element name="serializer" type="serializer" minOccurs="0" maxOccurs="1" />
2727
<xsd:element name="property-info" type="property_info" minOccurs="0" maxOccurs="1" />
28+
<xsd:element name="cache" type="cache" minOccurs="0" maxOccurs="1" />
2829
</xsd:all>
2930

3031
<xsd:attribute name="http-method-override" type="xsd:boolean" />
@@ -202,4 +203,18 @@
202203
<xsd:complexType name="property_info">
203204
<xsd:attribute name="enabled" type="xsd:boolean" />
204205
</xsd:complexType>
206+
207+
<xsd:complexType name="cache">
208+
<xsd:choice minOccurs="1" maxOccurs="unbounded">
209+
<xsd:element name="adapter" type="cache_adapter" />
210+
</xsd:choice>
211+
</xsd:complexType>
212+
213+
<xsd:complexType name="cache_adapter">
214+
<xsd:attribute name="name" type="xsd:string" use="required" />
215+
<xsd:attribute name="type" type="xsd:string" use="required" />
216+
<xsd:attribute name="default-lifetime" type="xsd:integer" />
217+
<xsd:attribute name="cache-provider-service" type="xsd:string" />
218+
<xsd:attribute name="directory" type="xsd:string" />
219+
</xsd:complexType>
205220
</xsd:schema>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
13+
14+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheAdapterPass;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Definition;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
class CacheAdapterPassTest extends \PHPUnit_Framework_TestCase
20+
{
21+
private $cacheAdapterPass;
22+
23+
protected function setUp()
24+
{
25+
$this->cacheAdapterPass = new CacheAdapterPass();
26+
}
27+
28+
public function testAdapterIsInjectedIntoConstructorArguments()
29+
{
30+
$container = $this->initializeContainer();
31+
$this->cacheAdapterPass->process($container);
32+
$adapter = $container->getDefinition('foo')->getArgument(0);
33+
34+
$this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter);
35+
$this->assertFalse($adapter->isAbstract());
36+
$this->assertSame('cache.adapter.apcu_adapter', $adapter->getParent());
37+
$this->assertSame('0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', $adapter->getArgument(0));
38+
}
39+
40+
public function testAdapterIsInjectedIntoMethodArguments()
41+
{
42+
$container = $this->initializeContainer();
43+
$this->cacheAdapterPass->process($container);
44+
$methodCalls = $container->getDefinition('bar')->getMethodCalls();
45+
$arguments = $methodCalls[0][1];
46+
$adapter = $arguments[0];
47+
48+
$this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter);
49+
$this->assertFalse($adapter->isAbstract());
50+
$this->assertSame('cache.adapter.doctrine_adapter', $adapter->getParent());
51+
}
52+
53+
public function testAdapterIsInjectIntoProperties()
54+
{
55+
$container = $this->initializeContainer();
56+
$this->cacheAdapterPass->process($container);
57+
$properties = $container->getDefinition('baz')->getProperties();
58+
$adapter = $properties['cache'];
59+
60+
$this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $adapter);
61+
$this->assertFalse($adapter->isAbstract());
62+
$this->assertSame('cache.adapter.fs_adapter', $adapter->getParent());
63+
}
64+
65+
/**
66+
* @expectedException \InvalidArgumentException
67+
* @expectedExceptionMessage The cache adapter "bar" is not configured
68+
*/
69+
public function testThrowsExceptionWhenReferencedAdapterIsNotConfigured()
70+
{
71+
$container = new ContainerBuilder();
72+
$container->setDefinition('foo', new Definition('Foo', array(new Reference('cache.adapter.bar'))));
73+
$this->cacheAdapterPass->process($container);
74+
}
75+
76+
private function initializeContainer()
77+
{
78+
$container = new ContainerBuilder();
79+
80+
$apcuAdapter = new Definition('Symfony\Component\Cache\Adapter\ApcuAdapter');
81+
$apcuAdapter->setAbstract(true);
82+
$apcuAdapter->addTag('cache.adapter', array('id' => 'adapter1', 'namespace-arg-index' => 0));
83+
$container->setDefinition('cache.adapter.apcu_adapter', $apcuAdapter);
84+
85+
$doctrineAdapter = new Definition('Symfony\Component\Cache\Adapter\DoctrineAdapter');
86+
$doctrineAdapter->setAbstract(true);
87+
$doctrineAdapter->addTag('cache.adapter', array('id' => 'adapter2'));
88+
$container->setDefinition('cache.adapter.doctrine_adapter', $doctrineAdapter);
89+
90+
$filesystemAdapter = new Definition('Symfony\Component\Cache\Adapter\FilesystemAdapter');
91+
$filesystemAdapter->setAbstract(true);
92+
$filesystemAdapter->addTag('cache.adapter', array('id' => 'adapter3'));
93+
$container->setDefinition('cache.adapter.fs_adapter', $filesystemAdapter);
94+
95+
$foo = new Definition();
96+
$foo->setArguments(array(new Reference('cache.adapter.adapter1')));
97+
$container->setDefinition('foo', $foo);
98+
99+
$bar = new Definition();
100+
$bar->addMethodCall('setCache', array(new Reference('cache.adapter.adapter2')));
101+
$container->setDefinition('bar', $bar);
102+
103+
$baz = new Definition();
104+
$baz->setProperty('cache', new Reference('cache.adapter.adapter3'));
105+
$container->setDefinition('baz', $baz);
106+
107+
return $container;
108+
}
109+
}

0 commit comments

Comments
 (0)