10000 feature #32294 [FrameworkBundle] Allow creating chained cache pools b… · symfony/symfony@a672bbc · GitHub
[go: up one dir, main page]

Skip to content

Commit a672bbc

Browse files
committed
feature #32294 [FrameworkBundle] Allow creating chained cache pools by providing several adapters (nicolas-grekas)
This PR was merged into the 4.4 branch. Discussion ---------- [FrameworkBundle] Allow creating chained cache pools by providing several adapters | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Replaces #30984, follows symfony/symfony-docs#11813 This PR allows defining several adapters for one pool. When doing so, this defines a chain pool. The benefit is that all chained pools get automatic namespace and lifetime, so things are transparent: ```yaml pools: my_chained_pool: default_lifetime: 12 adapters: - cache.adapter.array - cache.adapter.filesystem - {name: cache.adapter.redis, provider: 'redis://foo'} ``` (see fixtures for example of PHP/XML config) /cc @Nyholm @pborreli FYI Commits ------- 29ba091 [FrameworkBundle] Allow creating chained cache pools by providing several adapters
2 parents 08aa16f + 29ba091 commit a672bbc

File tree

10 files changed

+152
-10
lines changed

10 files changed

+152
-10
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Deprecated the `$parser` argument of `ControllerResolver::__construct()` and `DelegatingLoader::__construct()`
99
* Deprecated the `controller_name_converter` and `resolve_controller_name_subscriber` services
1010
* The `ControllerResolver` and `DelegatingLoader` classes have been marked as `final`
11+
* Added support for configuring chained cache pools
1112

1213
4.3.0
1314
-----

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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -978,8 +978,38 @@ private function addCacheSection(ArrayNodeDefinition $rootNode)
978978
->arrayNode('pools')
979979
->useAttributeAsKey('name')
980980
->prototype('array')
981+
->fixXmlConfig('adapter')
982+
->beforeNormalization()
983+
->ifTrue(function ($v) { return (isset($v['adapters']) || \is_array($v['adapter'] ?? null)) && isset($v['provider']); })
984+
->thenInvalid('Pool cannot have a "provider" while "adapter" is set to a map')
985+
->end()
981986
->children()
982-
->scalarNode('adapter')->defaultValue('cache.app')->end()
987+
->arrayNode('adapters')
988+
->info('One or more adapters to chain for creating the pool, defaults to "cache.app".')
989+
->beforeNormalization()
990+
->always()->then(function ($values) {
991+
if ([0] === array_keys($values) && \is_array($values[0])) {
992+
return $values[0];
993+
}
994+
$adapters = [];
995+
996+
foreach ($values as $k => $v) {
997+
if (\is_int($k) && \is_string($v)) {
998+
$adapters[] = $v;
999+
} elseif (!\is_array($v)) {
1000+
$adapters[$k] = $v;
1001+
} elseif (isset($v['provider'])) {
1002+
$adapters[$v['provider']] = $v['name'] ?? $v;
1003+
} else {
1004+
$adapters[] = $v['name'] ?? $v;
1005+
}
1006+
}
1007+
1008+
return $adapters;
1009+
})
1010+
->end()
1011+
->prototype('scalar')->end()
1012+
->end()
9831013
->scalarNode('tags')->defaultNull()->end()
9841014
->booleanNode('public')->defaultFalse()->end()
9851015
->integerNode('default_lifetime')->end()

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Symfony\Component\Cache\Adapter\AbstractAdapter;
3030
use Symfony\Component\Cache\Adapter\AdapterInterface;
3131
use Symfony\Component\Cache\Adapter\ArrayAdapter;
32+
use Symfony\Component\Cache\Adapter\ChainAdapter;
3233
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
3334
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
3435
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
@@ -1809,16 +1810,29 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con
18091810
}
18101811
foreach (['app', 'system'] as $name) {
18111812
$config['pools']['cache.'.$name] = [
1812-
'adapter' => $config[$name],
1813+
'adapters' => [$config[$name]],
18131814
F438 'public' => true,
18141815
'tags' => false,
18151816
];
18161817
}
18171818
foreach ($config['pools'] as $name => $pool) {
1818-
if ($config['pools'][$pool['adapter']]['tags'] ?? false) {
1819-
$pool['adapter'] = '.'.$pool['adapter'].'.inner';
1819+
$pool['adapters'] = $pool['adapters'] ?: ['cache.app'];
1820+
1821+
foreach ($pool['adapters'] as $provider => $adapter) {
1822+
if ($config['pools'][$adapter]['tags'] ?? false) {
1823+
$pool['adapters'][$provider] = $adapter = '.'.$adapter.'.inner';
1824+
}
1825+
}
1826+
1827+
if (1 === \count($pool['adapters'])) {
1828+
if (!isset($pool['provider']) && !\is_int($provider)) {
1829+
$pool['provider'] = $provider;
1830+
}
1831+
$definition = new ChildDefinition($adapter);
1832+
} else {
1833+
$definition = new Definition(ChainAdapter::class, [$pool['adapters'], 0]);
1834+
$pool['reset'] = 'reset';
18201835
}
1821-
$definition = new ChildDefinition($pool['adapter']);
18221836

18231837
if ($pool['tags']) {
18241838
if (true !== $pool['tags'] && ($config['pools'][$pool['tags']]['tags'] ?? false)) {
@@ -1849,7 +1863,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con
18491863
}
18501864

18511865
$definition->setPublic($pool['public']);
1852-
unset($pool['adapter'], $pool['public'], $pool['tags']);
1866+
unset($pool['adapters'], $pool['public'], $pool['tags']);
18531867

18541868
$definition->addTag('cache.pool', $pool);
18551869
$container->setDefinition($name, $definition);

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@
269269
</xsd:complexType>
270270

271271
<xsd:complexType name="cache_pool">
272+
<xsd:sequence>
273+
<xsd:element name="adapter" type="cache_pool_adapter" minOccurs="0" maxOccurs="unbounded" />
274+
</xsd:sequence>
275+
272276
<xsd:attribute name="name" type="xsd:string" use="required" />
273277
<xsd:attribute name="adapter" type="xsd:string" />
274278
<xsd:attribute name="tags" type="xsd:string" />
@@ -278,6 +282,11 @@
278282
<xsd:attribute name="clearer" type="xsd:string" />
279283
</xsd:complexType>
280284

285+
<xsd:complexType name="cache_pool_adapter">
286+
<xsd:attribute name="name" type="xsd:string" use="required" />
287+
<xsd:attribute name="provider" type="xsd:string" />
288+
</xsd:complexType>
289+
281290
<xsd:complexType name="workflow">
282291
<xsd:sequence>
283292
<xsd:element name="initial-marking" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@
2424
'cache.def' => [
2525
'default_lifetime' => 11,
2626
],
27+
'cache.chain' => [
28+
'default_lifetime' => 12,
29+
'adapter' => [
30+
'cache.adapter.array',
31+
'cache.adapter.filesystem',
32+
'redis://foo' => 'cache.adapter.redis',
33+
],
34+
],
2735
],
2836
],
2937
]);

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
<framework:pool name="cache.baz" adapter="cache.adapter.filesystem" default-lifetime="7" />
1313
<framework:pool name="cache.foobar" adapter="cache.adapter.psr6" default-lifetime="10" provider="app.cache_pool" />
1414
<framework:pool name="cache.def" default-lifetime="11" />
15+
<framework:pool name="cache.chain" default-lifetime="12">
16+
<framework:adapter name="cache.adapter.array" />
17+
<framework:adapter name="cache.adapter.filesystem" />
18+
<framework:adapter name="cache.adapter.redis" provider="redis://foo" />
19+
</framework:pool>
1520
</framework:cache>
1621
</framework:config>
1722
</container>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@ framework:
1717
provider: app.cache_pool
1818
cache.def:
1919
default_lifetime: 11
20+
cache.chain:
21+
default_lifetime: 12
22+
adapter:
23+
- cache.adapter.array
24+
- cache.adapter.filesystem
25+
- {name: cache.adapter.redis, provider: 'redis://foo'}

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
use Symfony\Component\Cache\Adapter\AdapterInterface;
2121
use Symfony\Component\Cache\Adapter\ApcuAdapter;
2222
use Symfony\Component\Cache\Adapter\ArrayAdapter;
23+
use Symfony\Component\Cache\Adapter\ChainAdapter;
2324
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
2425
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
2526
use Symfony\Component\Cache\Adapter\ProxyAdapter;
2627
use Symfony\Component\Cache\Adapter\RedisAdapter;
28+
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
2729
use Symfony\Component\Config\Resource\DirectoryResource;
2830
use Symfony\Component\Config\Resource\FileExistenceResource;
2931
use Symfony\Component\DependencyInjection\ChildDefinition;
@@ -1423,13 +1425,36 @@ public function testCacheDefaultRedisProviderWithEnvVar()
14231425

14241426
public function testCachePoolServices()
14251427
{
1426-
$container = $this->createContainerFromFile('cache');
1428+
$container = $this->createContainerFromFile('cache', [], true, false);
1429+
$container->setParameter('cache.prefix.seed', 'test');
1430+
$container->addCompilerPass(new CachePoolPass());
1431+
$container->compile();
14271432

14281433
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.foo', 'cache.adapter.apcu', 30);
14291434
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.bar', 'cache.adapter.doctrine', 5);
14301435
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.baz', 'cache.adapter.filesystem', 7);
14311436
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.foobar', 'cache.adapter.psr6', 10);
14321437
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.def', 'cache.app', 11);
1438+
1439+
$chain = $container->getDefinition('cache.chain');
1440+
1441+
$this->assertSame(ChainAdapter::class, $chain->getClass());
1442+
1443+
$expected = [
1444+
[
1445+
(new ChildDefinition('cache.adapter.array'))
1446+
->replaceArgument(0, 12),
1447+
(new ChildDefinition('cache.adapter.filesystem'))
1448+
->replaceArgument(0, 'x5nX4TVTWn')
1449+
->replaceArgument(1, 12),
1450+
(new ChildDefinition('cache.adapter.redis'))
1451+
->replaceArgument(0, new Reference('.cache_connection.kYdiLgf'))
1452+
->replaceArgument(1, 'x5nX4TVTWn')
1453+
->replaceArgument(2, 12),
1454+
],
1455+
12,
1456+
];
1457+
$this->assertEquals($expected, $chain->getArguments());
14331458
}
14341459

14351460
public function testRemovesResourceCheckerConfigCacheFactoryArgumentOnlyIfNoDebug()

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"require": {
1919
"php": "^7.1.3",
2020
"ext-xml": "*",
21-
"symfony/cache": "^4.3|^5.0",
21+
"symfony/cache": "^4.4|^5.0",
2222
"symfony/config": "^4.2|^5.0",
2323
"symfony/dependency-injection": "^4.4|^5.0",
2424
"symfony/error-catcher": "^4.4|^5.0",

src/Symfony/Component/Cache/DependencyInjection/CachePoolPass.php

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Cache\Adapter\AbstractAdapter;
1515
use Symfony\Component\Cache\Adapter\ArrayAdapter;
16+
use Symfony\Component\Cache\Adapter\ChainAdapter;
1617
use Symfony\Component\DependencyInjection\ChildDefinition;
1718
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1819
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -97,15 +98,58 @@ public function process(ContainerBuilder $container)
9798
if (isset($tags[0]['provider'])) {
9899
$tags[0]['provider'] = new Reference(static::getServiceProvider($container, $tags[0]['provider']));
99100
}
100-
$i = 0;
101+
102+
if (ChainAdapter::class === $class) {
103+
$adapters = [];
104+
foreach ($adapter->getArgument(0) as $provider => $adapter) {
105+
$chainedPool = $adapter = new ChildDefinition($adapter);
106+
$chainedTags = [\is_int($provider) ? [] : ['provider' => $provider]];
107+
$chainedClass = '';
108+
109+
while ($adapter instanceof ChildDefinition) {
110+
$adapter = $container->findDefinition($adapter->getParent());
111+
$chainedClass = $chainedClass ?: $adapter->getClass();
112+
if ($t = $adapter->getTag($this->cachePoolTag)) {
113+
$chainedTags[0] += $t[0];
114+
}
115+
}
116+
117+
if (ChainAdapter::class === $chainedClass) {
118+
throw new InvalidArgumentException(sprintf('Invalid service "%s": chain of adapters cannot reference another chain, found "%s".', $id, $chainedPool->getParent()));
119+
}
120+
121+
$i = 0;
122+
123+
if (isset($chainedTags[0]['provider'])) {
124+
$chainedPool->replaceArgument($i++, new Reference(static::getServiceProvider($container, $chainedTags[0]['provider'])));
125+
}
126+
127+
if (isset($tags[0]['namespace']) && ArrayAdapter::class !== $adapter->getClass()) {
128+
$chainedPool->replaceArgument($i++, $tags[0]['namespace']);
129+
}
130+
131+
if (isset($tags[0]['default_lifetime'])) {
132+
$chainedPool->replaceArgument($i++, $tags[0]['default_lifetime']);
133+
}
134+
135+
$adapters[] = $chainedPool;
136+
}
137+
138+
$pool->replaceArgument(0, $adapters);
139+
unset($tags[0]['provider'], $tags[0]['namespace']);
140+
$i = 1;
141+
} else {
142+
$i = 0;
143+
}
144+
101145
foreach ($attributes as $attr) {
102146
if (!isset($tags[0][$attr])) {
103147
// no-op
104148
} elseif ('reset' === $attr) {
105149
if ($tags[0][$attr]) {
106150
$pool->addTag($this->kernelResetTag, ['method' => $tags[0][$attr]]);
107151
}
108-
} elseif ('namespace' !== $attr || ArrayAdapter::class !== $adapter->getClass()) {
152+
} elseif ('namespace' !== $attr || ArrayAdapter::class !== $class) {
109153
$pool->replaceArgument($i++, $tags[0][$attr]);
110154
}
111155
unset($tags[0][$attr]);

0 commit comments

Comments
 (0)
0