Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 40e485c

Browse filesBrowse 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 b5ee977 commit 40e485c
Copy full SHA for 40e485c

File tree

16 files changed

+202
-17
lines changed
Filter options

16 files changed

+202
-17
lines changed

‎src/Symfony/Bundle/TwigBundle/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/CHANGELOG.md
+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ CHANGELOG
55
---
66

77
* Mark class `TemplateCacheWarmer` as `final`
8+
* Create `ChainCache` to store warmed-up cache in `kernel.build_dir` and runtime cache in `cache_dir`
9+
* Make `TemplateCacheWarmer` use `kernel.build_dir` instead of `cache_dir`
810

911
7.0
1012
---
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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\TwigBundle\Cache;
13+
14+
use Twig\Cache\CacheInterface;
15+
16+
/**
17+
* Chains several caches together.
18+
*
19+
* Cached items are fetched from the first cache having them in its data store.
20+
* They are saved and deleted in all adapters at once.
21+
*/
22+
class ChainCache implements CacheInterface
23+
{
24+
/**
25+
* @param CacheInterface[] $caches The ordered list of caches used to store and fetch cached items
26+
*/
27+
public function __construct(private array $caches)
28+
{
29+
if ([] === $caches) {
30+
throw new \InvalidArgumentException('At least one cache must be specified.');
31+
}
32+
33+
foreach ($caches as $cache) {
34+
if (!$cache instanceof CacheInterface) {
35+
throw new \InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', get_debug_type($cache), CacheInterface::class));
36+
}
37+
}
38+
}
39+
40+
public function generateKey(string $name, string $className): string
41+
{
42+
return $name.'#'.$className;
43+
}
44+
45+
public function write(string $key, string $content): void
46+
{
47+
[$name, $className] = explode('#', $key, 2);
48+
49+
foreach ($this->caches as $cache) {
50+
$cache->write($cache->generateKey($name, $className), $content);
51+
}
52+
}
53+
54+
public function load(string $key): void
55+
{
56+
[$name, $className] = explode('#', $key, 2);
57+
58+
foreach ($this->caches as $cache) {
59+
$cache->load($cache->generateKey($name, $className));
60+
61+
if (class_exists($className, false)) {
62+
break;
63+
}
64+
}
65+
}
66+
67+
public function getTimestamp(string $key): int
68+
{
69+
[$name, $className] = explode('#', $key, 2);
70+
71+
foreach ($this->caches as $cache) {
72+
$timestamp = $cache->getTimestamp($cache->generateKey($name, $className));
73+
if ($timestamp > 0) {
74+
return $timestamp;
75+
}
76+
}
77+
78+
return 0;
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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\TwigBundle\Cache;
13+
14+
use Twig\Cache\FilesystemCache;
15+
16+
/**
17+
* Implements a cache on the filesystem that can only be read, not written to.
18+
*/
19+
class ReadOnlyFilesystemCache extends FilesystemCache
20+
{
21+
public function write(string $key, string $content): void
22+
{
23+
// Do nothing with the content, it's a read-only filesystem.
24+
}
25+
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php
+13-6
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,26 @@
2626
*/
2727
class TemplateCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface
2828
{
29-
private ContainerInterface $container;
3029
private Environment $twig;
31-
private iterable $iterator;
3230

33-
public function __construct(ContainerInterface $container, iterable $iterator)
31+
public function __construct(private ContainerInterface $container, private iterable $iterator, private string $cacheFolder = 'twig')
3432
{
35-
// As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected.
36-
$this->container = $container;
37-
$this->iterator = $iterator;
33+
if (\func_num_args() < 3) {
34+
trigger_deprecation('symfony/twig-bundle', '7.1', 'The "string $cacheFolder" argument of "%s()" method will not be optional anymore in version 8.0, not defining it is deprecated.', __METHOD__);
35+
}
3836
}
3937

4038
public function warmUp(string $cacheDir, ?string $buildDir = null): array
4139
{
40+
if (!$buildDir) {
41+
return [];
42+
}
43+
4244
$this->twig ??= $this->container->get('twig');
4345

46+
$originalCache = $this->twig->getCache();
47+
$this->twig->setCache($buildDir.\DIRECTORY_SEPARATOR.$this->cacheFolder);
48+
4449
foreach ($this->iterator as $template) {
4550
try {
4651
$this->twig->load($template);
@@ -56,6 +61,8 @@ public function warmUp(string $cacheDir, ?string $buildDir = null): array
5661
}
5762
}
5863

64+
$this->twig->setCache($originalCache);
65+
5966
return [];
6067
}
6168

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php
+9-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,15 @@ private function addTwigOptions(ArrayNodeDefinition $rootNode): void
130130
->scalarNode('autoescape_service')->defaultNull()->end()
131131
->scalarNode('autoescape_service_method')->defaultNull()->end()
132132
->scalarNode('base_template_class')->example('Twig\Template')->cannotBeEmpty()->end()
133-
->scalarNode('cache')->defaultValue('%kernel.cache_dir%/twig')->end()
133+
->scalarNode('cache')
134+
->validate()
135+
->ifTrue(fn ($v) => !is_bool($v))
136+
->then(function ($v) {
137+
trigger_deprecation('symfony/twig-bundle', '7.1', 'Setting the "twig.cache" configuration option to a string is deprecated. It will only accept boolean values to enable or disable the cache in version 8.0.');
138+
return $v;
139+
})
140+
->end()
141+
->end()
134142
->scalarNode('charset')->defaultValue('%kernel.charset%')->end()
135143
->booleanNode('debug')->defaultValue('%kernel.debug%')->end()
136144
->booleanNode('strict_variables')->defaultValue('%kernel.debug%')->end()

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php
+16-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Symfony\Component\Translation\LocaleSwitcher;
2626
use Symfony\Component\Translation\Translator;
2727
use Symfony\Contracts\Service\ResetInterface;
28+
use Twig\Cache\FilesystemCache;
2829
use Twig\Extension\ExtensionInterface;
2930
use Twig\Extension\RuntimeExtensionInterface;
3031
use Twig\Loader\LoaderInterface;
@@ -153,6 +154,21 @@ public function load(array $configs, ContainerBuilder $container): void
153154
}
154155
}
155156

157+
if (!isset($config['cache']) || true === $config['cache']) {
158+
$config['cache'] = new Reference('twig.template_cache.chain');
159+
160+
$cacheOptions = ($config['auto_reload'] ?? true) ? FilesystemCache::FORCE_BYTECODE_INVALIDATION : 0;
161+
$container->getDefinition('twig.template_cache.runtime_cache')->replaceArgument(1, $cacheOptions);
162+
} else {
163+
$container->removeDefinition('twig.template_cache.chain');
164+
$container->removeDefinition('twig.template_cache.runtime_cache');
165+
$container->removeDefinition('twig.template_cache.readonly_cache');
166+
167+
if (false === $config['cache']) {
168+
$container->removeDefinition('twig.template_cache_warmer');
169+
}
170+
}
171+
156172
if (isset($config['autoescape_service'])) {
157173
$config['autoescape'] = [new Reference($config['autoescape_service']), $config['autoescape_service_method'] ?? '__invoke'];
158174
} else {
@@ -174,9 +190,6 @@ public function load(array $configs, ContainerBuilder $container): void
174190
$container->registerForAutoconfiguration(LoaderInterface::class)->addTag('twig.loader');
175191
$container->registerForAutoconfiguration(RuntimeExtensionInterface::class)->addTag('twig.runtime');
176192

177-
if (false === $config['cache']) {
178-
$container->removeDefinition('twig.template_cache_warmer');
179-
}
180193
}
181194

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

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/Resources/config/twig.php
+12-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
use Symfony\Bridge\Twig\Extension\WorkflowExtension;
3333
use Symfony\Bridge\Twig\Extension\YamlExtension;
3434
use Symfony\Bridge\Twig\Translation\TwigExtractor;
35+
use Symfony\Bundle\TwigBundle\Cache\ChainCache;
36+
use Symfony\Bundle\TwigBundle\Cache\ReadOnlyFilesystemCache;
3537
use Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheWarmer;
3638
use Symfony\Bundle\TwigBundle\DependencyInjection\Configurator\EnvironmentConfigurator;
3739
use Symfony\Bundle\TwigBundle\TemplateIterator;
@@ -78,8 +80,17 @@
7880
->set('twig.template_iterator', TemplateIterator::class)
7981
->args([service('kernel'), abstract_arg('Twig paths'), param('twig.default_path'), abstract_arg('File name pattern')])
8082

83+
->set('twig.template_cache.runtime_cache', FilesystemCache::class)
84+
->args([param('kernel.cache_dir').'/twig', abstract_arg('Twig filesystem cache options')])
85+
86+
->set('twig.template_cache.readonly_cache', ReadOnlyFilesystemCache::class)
87+
->args([param('kernel.build_dir').'/twig'])
88+
89+
->set('twig.template_cache.chain', ChainCache::class)
90+
->args([[service('twig.template_cache.runtime_cache'), service('twig.template_cache.readonly_cache')]])
91+
8192
->set('twig.template_cache_warmer', TemplateCacheWarmer::class)
82-
->args([service(ContainerInterface::class), service('twig.template_iterator')])
93+
->args([service(ContainerInterface::class), service('twig.template_iterator'), 'twig'])
8394
->tag('kernel.cache_warmer')
8495
->tag('container.service_subscriber', ['id' => 'twig'])
8596

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/full.php
-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
],
1313
'auto_reload' => true,
1414
'base_template_class' => 'stdClass',
15-
'cache' => '/tmp',
1615
'charset' => 'ISO-8859-1',
1716
'debug' => true,
1817
'strict_variables' => true,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
$container->loadFromExtension('twig', [
4+
'cache' => false,
5+
]);

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/extra.xml
+1-1
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" base-template-class="stdClass" cache="/tmp" charset="ISO-8859-1" debug="true" strict-variables="true">
9+
<twig:config auto-reload="true" base-template-class="stdClass" 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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/full.xml
+1-1
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" base-template-class="stdClass" cache="/tmp" charset="ISO-8859-1" debug="true" strict-variables="true" default-path="%kernel.project_dir%/Fixtures/templates">
9+
<twig:config auto-reload="true" base-template-class="stdClass" 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>
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>

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/full.yml
-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ twig:
88
bad: {key: foo}
99
auto_reload: true
1010
base_template_class: stdClass
11-
cache: /tmp
1211
charset: ISO-8859-1
1312
debug: true
1413
strict_variables: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
twig:
2+
cache: false

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php
+25-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function testLoadEmptyConfiguration()
4444

4545
// Twig options
4646
$options = $container->getDefinition('twig')->getArgument(1);
47-
$this->assertEquals('%kernel.cache_dir%/twig', $options['cache'], '->load() sets default value for cache option');
47+
$this->assertEquals(new Reference('twig.template_cache.chain'), $options['cache'], '->load() sets default value for cache option');
4848
$this->assertEquals('%kernel.charset%', $options['charset'], '->load() sets default value for charset option');
4949
$this->assertEquals('%kernel.debug%', $options['debug'], '->load() sets default value for debug option');
5050

@@ -53,6 +53,29 @@ public function testLoadEmptyConfiguration()
5353
}
5454
}
5555

56+
/**
57+
* @dataProvider getFormats
58+
*/
59+
public function testLoadNoCacheConfiguration($format)
60+
{
61+
$container = $this->createContainer();
62+
$container->registerExtension(new TwigExtension());
63+
$this->loadFromFile($container, 'full', $format);
64+
$this->loadFromFile($container, 'no-cache', $format);
65+
$this->compileContainer($container);
66+
67+
$this->assertEquals(Environment::class, $container->getDefinition('twig')->getClass(), '->load() loads the twig.xml file');
68+
69+
$this->assertFalse($container->hasDefinition('twig.template_cache_warmer'), '->load() removes the CacheWarmer');
70+
$this->assertFalse($container->hasDefinition('twig.template_cache.chain'), '->load() removes the CacheChain');
71+
$this->assertFalse($container->hasDefinition('twig.template_cache.readonly_cache'), '->load() removes the read-only cache');
72+
$this->assertFalse($container->hasDefinition('twig.template_cache.runtime_cache'), '->load() removes the runtime cache');
73+
74+
// Twig options
75+
$options = $container->getDefinition('twig')->getArgument(1);
76+
$this->assertFalse($options['cache'], '->load() sets default value for cache option');
77+
}
78+
5679
/**
5780
* @dataProvider getFormats
5881
*/
@@ -92,7 +115,7 @@ public function testLoadFullConfiguration($format)
92115
$this->assertTrue($options['auto_reload'], '->load() sets the auto_reload option');
93116
$this->assertSame('name', $options['autoescape'], '->load() sets the autoescape option');
94117
$this->assertEquals('stdClass', $options['base_template_class'], '->load() sets the base_template_class option');
95-
$this->assertEquals('/tmp', $options['cache'], '->load() sets the cache option');
118+
$this->assertEquals(new Reference('twig.template_cache.chain'), $options['cache'], '->load() sets the cache option');
96119
$this->assertEquals('ISO-8859-1', $options['charset'], '->load() sets the charset option');
97120
$this->assertTrue($options['debug'], '->load() sets the debug option');
98121
$this->assertTrue($options['strict_variables'], '->load() sets the strict_variables option');

‎src/Symfony/Bundle/TwigBundle/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/composer.json
+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"composer-runtime-api": ">=2.1",
2121
"symfony/config": "^6.4|^7.0",
2222
"symfony/dependency-injection": "^6.4|^7.0",
23+
"symfony/deprecation-contracts": "^2.5|^3",
2324
"symfony/twig-bridge": "^6.4|^7.0",
2425
"symfony/http-foundation": "^6.4|^7.0",
2526
"symfony/http-kernel": "^6.4|^7.0",

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.