Skip to content

Commit 0f841d2

Browse files
committed
[TwigBundle] Use kernel.build_dir to store the templates known at build time
Signed-off-by: Quentin Devos <4972091+Okhoshi@users.noreply.github.com>
1 parent 58a14ab commit 0f841d2

File tree

19 files changed

+210
-28
lines changed

19 files changed

+210
-28
lines changed

src/Symfony/Bundle/TwigBundle/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ CHANGELOG
77
* Enable `#[AsTwigFilter]`, `#[AsTwigFunction]` and `#[AsTwigTest]` attributes
88
to configure extensions on runtime classes
99
* Add support for a `twig` validator
10+
* Use `ChainCache` to store warmed-up cache in `kernel.build_dir` and runtime cache in `kernel.cache_dir`
11+
* Make `TemplateCacheWarmer` use `kernel.build_dir` instead of `kernel.cache_dir`
1012

1113
7.1
1214
---

src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use Psr\Container\ContainerInterface;
1515
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
1616
use Symfony\Contracts\Service\ServiceSubscriberInterface;
17+
use Twig\Cache\CacheInterface;
18+
use Twig\Cache\NullCache;
1719
use Twig\Environment;
1820
use Twig\Error\Error;
1921

@@ -34,26 +36,48 @@ class TemplateCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInte
3436
public function __construct(
3537
private ContainerInterface $container,
3638
private iterable $iterator,
39+
private ?CacheInterface $cache = null,
3740
) {
3841
}
3942

4043
public function warmUp(string $cacheDir, ?string $buildDir = null): array
4144
{
4245
$this->twig ??= $this->container->get('twig');
4346

44-
foreach ($this->iterator as $template) {
45-
try {
46-
$this->twig->load($template);
47-
} catch (Error) {
47+
$originalCache = $this->twig->getCache();
48+
if ($originalCache instanceof NullCache) {
49+
// There's no point to warm up a cache that won't be used afterward
50+
return [];
51+
}
52+
53+
if (null !== $this->cache) {
54+
if (!$buildDir) {
4855
/*
49-
* Problem during compilation, give up for this template (e.g. syntax errors).
50-
* Failing silently here allows to ignore templates that rely on functions that aren't available in
51-
* the current environment. For example, the WebProfilerBundle shouldn't be available in the prod
52-
* environment, but some templates that are never used in prod might rely on functions the bundle provides.
53-
* As we can't detect which templates are "really" important, we try to load all of them and ignore
54-
* errors. Error checks may be performed by calling the lint:twig command.
56+
* The cache has already been warmup during the build of the container, when $buildDir was set.
5557
*/
58+
return [];
59+
}
60+
// Swap the cache for the warmup as the Twig Environment has the ChainCache injected
61+
$this->twig->setCache($this->cache);
62+
}
63+
64+
try {
65+
foreach ($this->iterator as $template) {
66+
try {
67+
$this->twig->load($template);
68+
} catch (Error) {
69+
/*
70+
* Problem during compilation, give up for this template (e.g. syntax errors).
71+
* Failing silently here allows to ignore templates that rely on functions that aren't available in
72+
* the current environment. For example, the WebProfilerBundle shouldn't be available in the prod
73+
* environment, but some templates that are never used in prod might rely on functions the bundle provides.
74+
* As we can't detect which templates are "really" important, we try to load all of them and ignore
75+
* errors. Error checks may be performed by calling the lint:twig command.
76+
*/
77+
}
5678
}
79+
} finally {
80+
$this->twig->setCache($originalCache);
5781
}
5882

5983
return [];

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ private function addTwigOptions(ArrayNodeDefinition $rootNode): void
134134
->example('Twig\Template')
135135
->cannotBeEmpty()
136136
->end()
137-
->scalarNode('cache')->defaultValue('%kernel.cache_dir%/twig')->end()
137+
->scalarNode('cache')->defaultTrue()->end()
138138
->scalarNode('charset')->defaultValue('%kernel.charset%')->end()
139139
->booleanNode('debug')->defaultValue('%kernel.debug%')->end()
140140
->booleanNode('strict_variables')->defaultValue('%kernel.debug%')->end()

src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use Twig\Attribute\AsTwigFilter;
3131
use Twig\Attribute\AsTwigFunction;
3232
use Twig\Attribute\AsTwigTest;
33+
use Twig\Cache\FilesystemCache;
3334
use Twig\Environment;
3435
use Twig\Extension\ExtensionInterface;
3536
use Twig\Extension\RuntimeExtensionInterface;
@@ -167,6 +168,31 @@ public function load(array $configs, ContainerBuilder $container): void
167168
}
168169
}
169170

171+
if (true === $config['cache']) {
172+
$autoReloadOrDefault = $container->getParameterBag()->resolveValue($config['auto_reload'] ?? $config['debug']);
173+
$buildDir = $container->getParameter('kernel.build_dir');
174+
$cacheDir = $container->getParameter('kernel.cache_dir');
175+
176+
if ($autoReloadOrDefault || $cacheDir === $buildDir) {
177+
$config['cache'] = '%kernel.cache_dir%/twig';
178+
}
179+
}
180+
181+
if (true === $config['cache']) {
182+
$config['cache'] = new Reference('twig.template_cache.chain');
183+
} else {
184+
$container->removeDefinition('twig.template_cache.chain');
185+
$container->removeDefinition('twig.template_cache.runtime_cache');
186+
$container->removeDefinition('twig.template_cache.readonly_cache');
187+
$container->removeDefinition('twig.template_cache.warmup_cache');
188+
189+
if (false === $config['cache']) {
190+
$container->removeDefinition('twig.template_cache_warmer');
191+
} else {
192+
$container->getDefinition('twig.template_cache_warmer')->replaceArgument(2, null);
193+
}
194+
}
195+
170196
if (isset($config['autoescape_service'])) {
171197
$config['autoescape'] = [new Reference($config['autoescape_service']), $config['autoescape_service_method'] ?? '__invoke'];
172198
} else {
@@ -191,10 +217,6 @@ public function load(array $configs, ContainerBuilder $container): void
191217
$container->registerAttributeForAutoconfiguration(AsTwigFilter::class, AttributeExtensionPass::autoconfigureFromAttribute(...));
192218
$container->registerAttributeForAutoconfiguration(AsTwigFunction::class, AttributeExtensionPass::autoconfigureFromAttribute(...));
193219
$container->registerAttributeForAutoconfiguration(AsTwigTest::class, AttributeExtensionPass::autoconfigureFromAttribute(...));
194-
195-
if (false === $config['cache']) {
196-
$container->removeDefinition('twig.template_cache_warmer');
197-
}
198220
}
199221

200222
private function getBundleTemplatePaths(ContainerBuilder $container, array $config): array

src/Symfony/Bundle/TwigBundle/Resources/config/twig.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
use Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheWarmer;
3737
use Symfony\Bundle\TwigBundle\DependencyInjection\Configurator\EnvironmentConfigurator;
3838
use Symfony\Bundle\TwigBundle\TemplateIterator;
39+
use Twig\Cache\ChainCache;
3940
use Twig\Cache\FilesystemCache;
41+
use Twig\Cache\ReadOnlyFilesystemCache;
4042
use Twig\Environment;
4143
use Twig\Extension\CoreExtension;
4244
use Twig\Extension\DebugExtension;
@@ -79,8 +81,24 @@
7981
->set('twig.template_iterator', TemplateIterator::class)
8082
->args([service('kernel'), abstract_arg('Twig paths'), param('twig.default_path'), abstract_arg('File name pattern')])
8183

84+
->set('twig.template_cache.runtime_cache', FilesystemCache::class)
85+
->args([param('kernel.cache_dir').'/twig'])
86+
87+
->set('twig.template_cache.readonly_cache', ReadOnlyFilesystemCache::class)
88+
->args([param('kernel.build_dir').'/twig'])
89+
90+
->set('twig.template_cache.warmup_cache', FilesystemCache::class)
91+
->args([param('kernel.build_dir').'/twig'])
92+
93+
->set('twig.template_cache.chain', ChainCache::class)
94+
->args([[service('twig.template_cache.readonly_cache'), service('twig.template_cache.runtime_cache')]])
95+
8296
->set('twig.template_cache_warmer', TemplateCacheWarmer::class)
83-
->args([service(ContainerInterface::class), service('twig.template_iterator')])
97+
->args([
98+
service(ContainerInterface::class),
99+
service('twig.template_iterator'),
100+
service('twig.template_cache.warmup_cache'),
101+
])
84102
->tag('kernel.cache_warmer')
85103
->tag('container.service_subscriber', ['id' => 'twig'])
86104

src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/full.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
'pi' => 3.14,
1111
'bad' => ['key' => 'foo'],
1212
],
13-
'auto_reload' => true,
14-
'cache' => '/tmp',
13+
'auto_reload' => false,
1514
'charset' => 'ISO-8859-1',
1615
'debug' => true,
1716
'strict_variables' => true,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
$container->loadFromExtension('twig', [
4+
'cache' => false,
5+
]);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
$container->loadFromExtension('twig', [
4+
'cache' => 'random-path',
5+
]);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
$container->loadFromExtension('twig', [
4+
'cache' => true,
5+
'auto_reload' => false,
6+
]);

src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/extra.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
77
http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
88

9-
<twig:config auto-reload="true" cache="/tmp" charset="ISO-8859-1" debug="true" strict-variables="true">
9+
<twig:config auto-reload="true" charset="ISO-8859-1" debug="true" strict-variables="true">
1010
<twig:path namespace="namespace3">namespaced_path3</twig:path>
1111
</twig:config>
1212
</container>

src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/full.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
77
http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
88

9-
<twig:config auto-reload="true" cache="/tmp" charset="ISO-8859-1" debug="true" strict-variables="true" default-path="%kernel.project_dir%/Fixtures/templates">
9+
<twig:config auto-reload="false" charset="ISO-8859-1" debug="true" strict-variables="true" default-path="%kernel.project_dir%/Fixtures/templates">
1010
<twig:form-theme>MyBundle::form.html.twig</twig:form-theme>
1111
<twig:global key="foo" id="bar" type="service" />
1212
<twig:global key="baz">@@qux</twig:global>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:twig="http://symfony.com/schema/dic/twig"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
7+
http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
8+
9+
<twig:config cache="false" />
10+
</container>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:twig="http://symfony.com/schema/dic/twig"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
7+
http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
8+
9+
<twig:config cache="random-path" />
10+
</container>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:twig="http://symfony.com/schema/dic/twig"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
7+
http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
8+
9+
<twig:config cache="true" auto-reload="false" />
10+
</container>

src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/full.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ twig:
66
baz: "@@qux"
77
pi: 3.14
88
bad: {key: foo}
9-
auto_reload: true
10-
cache: /tmp
9+
auto_reload: false
1110
charset: ISO-8859-1
1211
debug: true
1312
strict_variables: true
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
twig:
2+
cache: false
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
twig:
2+
cache: random-path
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
twig:
2+
cache: true
3+
auto_reload: false

src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ public function testLoadEmptyConfiguration()
5757
}
5858

5959
/**
60-
* @dataProvider getFormats
60+
* @dataProvider getFormatsAndBuildDir
6161
*/
62-
public function testLoadFullConfiguration(string $format)
62+
public function testLoadFullConfiguration(string $format, ?string $buildDir)
6363
{
64-
$container = $this->createContainer();
64+
$container = $this->createContainer($buildDir);
6565
$container->registerExtension(new TwigExtension());
6666
$this->loadFromFile($container, 'full', $format);
6767
$this->compileContainer($container);
@@ -92,13 +92,64 @@ public function testLoadFullConfiguration(string $format)
9292

9393
// Twig options
9494
$options = $container->getDefinition('twig')->getArgument(1);
95-
$this->assertTrue($options['auto_reload'], '->load() sets the auto_reload option');
95+
$this->assertFalse($options['auto_reload'], '->load() sets the auto_reload option');
9696
$this->assertSame('name', $options['autoescape'], '->load() sets the autoescape option');
9797
$this->assertArrayNotHasKey('base_template_class', $options, '->load() does not set the base_template_class if none is provided');
98-
$this->assertEquals('/tmp', $options['cache'], '->load() sets the cache option');
9998
$this->assertEquals('ISO-8859-1', $options['charset'], '->load() sets the charset option');
10099
$this->assertTrue($options['debug'], '->load() sets the debug option');
101100
$this->assertTrue($options['strict_variables'], '->load() sets the strict_variables option');
101+
$this->assertEquals($buildDir !== null ? new Reference('twig.template_cache.chain') : '%kernel.cache_dir%/twig', $options['cache'], '->load() sets the cache option');
102+
}
103+
104+
/**
105+
* @dataProvider getFormatsAndBuildDir
106+
*/
107+
public function testLoadNoCacheConfiguration(string $format, ?string $buildDir)
108+
{
109+
$container = $this->createContainer($buildDir);
110+
$container->registerExtension(new TwigExtension());
111+
$this->loadFromFile($container, 'no-cache', $format);
112+
$this->compileContainer($container);
113+
114+
$this->assertEquals(Environment::class, $container->getDefinition('twig')->getClass(), '->load() loads the twig.xml file');
115+
116+
// Twig options
117+
$options = $container->getDefinition('twig')->getArgument(1);
118+
$this->assertFalse($options['cache'], '->load() sets cache option to false');
119+
}
120+
121+
/**
122+
* @dataProvider getFormatsAndBuildDir
123+
*/
124+
public function testLoadPathCacheConfiguration(string $format, ?string $buildDir)
125+
{
126+
$container = $this->createContainer($buildDir);
127+
$container->registerExtension(new TwigExtension());
128+
$this->loadFromFile($container, 'path-cache', $format);
129+
$this->compileContainer($container);
130+
131+
$this->assertEquals(Environment::class, $container->getDefinition('twig')->getClass(), '->load() loads the twig.xml file');
132+
133+
// Twig options
134+
$options = $container->getDefinition('twig')->getArgument(1);
135+
$this->assertSame('random-path', $options['cache'], '->load() sets cache option to string path');
136+
}
137+
138+
/**
139+
* @dataProvider getFormatsAndBuildDir
140+
*/
141+
public function testLoadProdCacheConfiguration(string $format, ?string $buildDir)
142+
{
143+
$container = $this->createContainer($buildDir);
144+
$container->registerExtension(new TwigExtension());
145+
$this->loadFromFile($container, 'prod-cache', $format);
146+
$this->compileContainer($container);
147+
148+
$this->assertEquals(Environment::class, $container->getDefinition('twig')->getClass(), '->load() loads the twig.xml file');
149+
150+
// Twig options
151+
$options = $container->getDefinition('twig')->getArgument(1);
152+
$this->assertEquals($buildDir !== null ? new Reference('twig.template_cache.chain') : '%kernel.cache_dir%/twig', $options['cache'], '->load() sets cache option to CacheChain reference');
102153
}
103154

104155
/**
@@ -238,6 +289,19 @@ public static function getFormats(): array
238289
];
239290
}
240291

292+
public static function getFormatsAndBuildDir(): array
293+
{
294+
return [
295+
['php', null],
296+
['php', __DIR__.'/build'],
297+
['yml', null],
298+
['yml', __DIR__.'/build'],
299+
['xml', null],
300+
['xml', __DIR__.'/build'],
301+
];
302+
}
303+
304+
241305
/**
242306
* @dataProvider stopwatchExtensionAvailabilityProvider
243307
*/
@@ -312,10 +376,11 @@ public function testCustomHtmlToTextConverterService(string $format)
312376
$this->assertEquals(new Reference('my_converter'), $bodyRenderer->getArgument('$converter'));
313377
}
314378

315-
private function createContainer(): ContainerBuilder
379+
private function createContainer(?string $buildDir = null): ContainerBuilder
316380
{
317381
$container = new ContainerBuilder(new ParameterBag([
318382
'kernel.cache_dir' => __DIR__,
383+
'kernel.build_dir' => $buildDir ?? __DIR__,
319384
'kernel.project_dir' => __DIR__,
320385
'kernel.charset' => 'UTF-8',
321386
'kernel.debug' => false,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy