Skip to content

Commit a9f217c

Browse files
committed
[FrameworkBundle] integrate the Cache component
1 parent 0813705 commit a9f217c

File tree

10 files changed

+390
-0
lines changed

10 files changed

+390
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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\Cache\Adapter\ApcuAdapter;
15+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\DefinitionDecorator;
18+
use Symfony\Component\DependencyInjection\Reference;
19+
20+
/**
21+
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
22+
*/
23+
class CacheAdapterPass implements CompilerPassInterface
24+
{
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
public function process(ContainerBuilder $container)
29+
{
30+
foreach ($container->getDefinitions() as $id => $definition) {
31+
$definition->setArguments($this->resolveArguments($container, $id, $definition->getArguments()));
32+
33+
$calls = $definition->getMethodCalls();
34+
35+
foreach ($calls as $index => $call) {
36+
$calls[$index] = array($call[0], $this->resolveArguments($container, $id, $call[1]));
37+
}
38+
39+
$definition->setMethodCalls($calls);
40+
41+
$definition->setProperties($this->resolveArguments($container, $id, $definition->getProperties()));
42+
}
43+
}
44+
45+
private function resolveArguments($container, $id, array $arguments)
46+
{
47+
foreach ($arguments as $index => $argument) {
48+
$arguments[$index] = $this->createCacheAdapter($container, $id, $argument);
49+
}
50+
51+
return $arguments;
52+
}
53+
54+
private function createCacheAdapter(ContainerBuilder $container, $serviceId, $argument)
55+
{
56+
if (!$argument instanceof Reference) {
57+
return $argument;
58+
}
59+
60+
$adapterId = (string) $argument;
61+
62+
if ('cache.adapter.' !== substr($adapterId, 0, 14)) {
63+
return $argument;
64+
}
65+
66+
$name = substr($adapterId, 14);
67+
68+
if (!$container->hasDefinition($adapterId)) {
69+
throw new \InvalidArgumentException(sprintf('The cache adapter "%s" is not configured.', $name));
70+
}
71+
72+
$abstractAdapter = $container->getDefinition($adapterId);
73+
$adapter = new DefinitionDecorator($adapterId);
74+
75+
switch ($abstractAdapter->getClass()) {
76+
case ApcuAdapter::class:
77+
$adapter->replaceArgument(0, sha1($serviceId));
78+
break;
79+
}
80+
81+
return $adapter;
82+
}
83+
}

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

+38
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
1313

14+
use Symfony\Component\Cache\Adapter\ApcuAdapter;
15+
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
16+
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
1417
use Symfony\Component\DependencyInjection\ContainerBuilder;
1518
use Symfony\Component\DependencyInjection\ContainerInterface;
1619
use Symfony\Component\DependencyInjection\Definition;
@@ -134,6 +137,10 @@ public function load(array $configs, ContainerBuilder $container)
134137
$this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader);
135138
}
136139

140+
if (isset($config['cache'])) {
141+
$this->registerCacheConfiguration($config['cache'], $container);
142+
}
143+
137144
$loader->load('debug_prod.xml');
138145
$definition = $container->findDefinition('debug.debug_handlers_listener');
139146

@@ -987,6 +994,37 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild
987994
}
988995
}
989996

997+
private function registerCacheConfiguration(array $config, ContainerBuilder $container)
998+
{
999+
foreach ($config['adapters'] as $name => $adapter) {
1000+
$class = null;
1001+
$arguments = array();
1002+
1003+
switch ($adapter['type']) {
1004+
case 'apcu':
1005+
$class = ApcuAdapter::class;
1006+
$arguments[] = null;
1007+
$arguments[] = isset($adapter['options']['default_lifetime']) ? $adapter['options']['default_lifetime'] : 0;
1008+
break;
1009+
case 'doctrine':
1010+
$class = DoctrineAdapter::class;
1011+
$arguments[] = isset($adapter['options']['cache_provider_service']) ? new Reference($adapter['options']['cache_provider_service']) : null;
1012+
$arguments[] = isset($adapter['options']['default_lifetime']) ? $adapter['options']['default_lifetime'] : null;
1013+
break;
1014+
case 'filesystem':
1015+
$class = FilesystemAdapter::class;
1016+
$arguments[] = isset($adapter['options']['directory']) ? $adapter['options']['directory'] : null;
1017+
$arguments[] = isset($adapter['options']['default_lifetime']) ? $adapter['options']['default_lifetime'] : null;
1018+
break;
1019+
}
1020+
1021+
$adapterDefinition = new Definition($class);
1022+
$adapterDefinition->setArguments($arguments);
1023+
$adapterDefinition->setAbstract(true);
1024+
$container->setDefinition('cache.adapter.'.$name, $adapterDefinition);
1025+
}
1026+
}
1027+
9901028
/**
9911029
* Gets a hash of the kernel root directory.
9921030
*

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\FormPass;
1819
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass;
1920
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
@@ -87,6 +88,7 @@ public function build(ContainerBuilder $container)
8788
$container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING);
8889
$container->addCompilerPass(new SerializerPass());
8990
$container->addCompilerPass(new PropertyInfoPass());
91+
$container->addCompilerPass(new CacheAdapterPass());
9092

9193
if ($container->getParameter('kernel.debug')) {
9294
$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,106 @@
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+
$container->setDefinition('cache.adapter.apcu_adapter', $apcuAdapter);
83+
84+
$doctrineAdapter = new Definition('Symfony\Component\Cache\Adapter\DoctrineAdapter');
85+
$doctrineAdapter->setAbstract(true);
86+
$container->setDefinition('cache.adapter.doctrine_adapter', $doctrineAdapter);
87+
88+
$filesystemAdapter = new Definition('Symfony\Component\Cache\Adapter\FilesystemAdapter');
89+
$filesystemAdapter->setAbstract(true);
90+
$container->setDefinition('cache.adapter.fs_adapter', $filesystemAdapter);
91+
92+
$foo = new Definition();
93+
$foo->setArguments(array(new Reference('cache.adapter.apcu_adapter')));
94+
$container->setDefinition('foo', $foo);
95+
96+
$bar = new Definition();
97+
$bar->addMethodCall('setCache', array(new Reference('cache.adapter.doctrine_adapter')));
98+
$container->setDefinition('bar', $bar);
99+
100+
$baz = new Definition();
101+
$baz->setProperty('cache', new Reference('cache.adapter.fs_adapter'));
102+
$container->setDefinition('baz', $baz);
103+
104+
return $container;
105+
}
106+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
$container->loadFromExtension('framework', array(
4+
'cache' => array(
5+
'adapters' => array(
6+
'foo' => array(
7+
'type' => 'apcu',
8+
'options' => array(
9+
'default_lifetime' => 30,
10+
),
11+
),
12+
'bar' => array(
13+
'type' => 'doctrine',
14+
'options' => array(
15+
'default_lifetime' => 5,
16+
'cache_provider_service' => 'app.doctrine_cache_provider',
17+
),
18+
),
19+
'baz' => array(
20+
'type' => 'filesystem',
21+
'options' => array(
22+
'default_lifetime' => 7,
23+
'directory' => 'app/cache/psr',
24+
),
25+
),
26+
),
27+
),
28+
));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" ?>
2+
<container xmlns="http://symfony.com/schema/dic/services"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:framework="http://symfony.com/schema/dic/symfony"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
6+
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
7+
8+
<framework:config>
9+
<framework:cache>
10+
<framework:adapter name="foo" type="apcu" default-lifetime="30" />
11+
<framework:adapter name="bar" type="doctrine" default-lifetime="5" cache-provider-service="app.doctrine_cache_provider" />
12+
<framework:adapter name="baz" type="filesystem" default-lifetime="7" directory="app/cache/psr" />
13+
</framework:cache>
14+
</framework:config>
15+
</container>

0 commit comments

Comments
 (0)