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 fb68b3c

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 fb68b3c
Copy full SHA for fb68b3c

File tree

12 files changed

+163
-12
lines changed
Filter options

12 files changed

+163
-12
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,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\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+
foreach ($this->caches as $cache) {
48+
[$name, $className] = explode('#', $key, 2);
49+
50+
$cacheKey = $cache->generateKey($name, $className);
51+
$cache->write($cacheKey, $content);
52+
}
53+
}
54+
55+
public function load(string $key): void
56+
{
57+
foreach ($this->caches as $cache) {
58+
[$name, $className] = explode('#', $key, 2);
59+
60+
$cacheKey = $cache->generateKey($name, $className);
61+
$cache->load($cacheKey);
62+
63+
if (class_exists($className, false)) {
64+
break;
65+
}
66+
}
67+
}
68+
69+
public function getTimestamp(string $key): int
70+
{
71+
foreach ($this->caches as $cache) {
72+
[$name, $className] = explode('#', $key, 2);
73+
74+
$cacheKey = $cache->generateKey($name, $className);
75+
$timestamp = $cache->getTimestamp($cacheKey);
76+
if ($timestamp > 0) {
77+
return $timestamp;
78+
}
79+
}
80+
81+
return 0;
82+
}
83+
}
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
+22-5
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,36 @@
2626
*/
2727
class TemplateCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface
2828
{
29-
private ContainerInterface $container;
3029
private Environment $twig;
31-
private iterable $iterator;
30+
private string $cacheFolder;
3231

33-
public function __construct(ContainerInterface $container, iterable $iterator)
32+
/**
33+
* @param string $cacheFolder
34+
*/
35+
public function __construct(private ContainerInterface $container, private iterable $iterator /* , private string $cacheFolder */)
3436
{
3537
// 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;
38+
if (\func_num_args() < 3) {
39+
trigger_deprecation('symfony/twig-bundle', '7.1', 'The "%s()" method will have a new "string $cacheFolder" argument in version 8.0, not defining it is deprecated.', __METHOD__);
40+
41+
$cacheFolder = 'twig';
42+
} else {
43+
$cacheFolder = func_get_arg(2);
44+
}
45+
$this->cacheFolder = $cacheFolder;
3846
}
3947

4048
public function warmUp(string $cacheDir, ?string $buildDir = null): array
4149
{
50+
if (!$buildDir) {
51+
return [];
52+
}
53+
4254
$this->twig ??= $this->container->get('twig');
4355

56+
$originalCache = $this->twig->getCache();
57+
$this->twig->setCache($buildDir.\DIRECTORY_SEPARATOR.$this->cacheFolder);
58+
4459
foreach ($this->iterator as $template) {
4560
try {
4661
$this->twig->load($template);
@@ -56,6 +71,8 @@ public function warmUp(string $cacheDir, ?string $buildDir = null): array
5671
}
5772
}
5873

74+
$this->twig->setCache($originalCache);
75+
5976
return [];
6077
}
6178

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php
+3-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ 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+
->setDeprecated('symfony/twig-bundle', '7.1', 'Setting the "%path%.%node%" configuration option is deprecated. It will be removed in version 8.0.')
135+
->end()
134136
->scalarNode('charset')->defaultValue('%kernel.charset%')->end()
135137
->booleanNode('debug')->defaultValue('%kernel.debug%')->end()
136138
->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
+12
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,17 @@ public function load(array $configs, ContainerBuilder $container): void
153154
}
154155
}
155156

157+
if (!isset($config['cache'])) {
158+
$config['cache'] = new Reference('twig.template_cache.chain');
159+
160+
$cacheOptions = ($config['auto_reload'] ?? false) ? 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+
156168
if (isset($config['autoescape_service'])) {
157169
$config['autoescape'] = [new Reference($config['autoescape_service']), $config['autoescape_service_method'] ?? '__invoke'];
158170
} else {

‎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,

‎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>

‎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

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php
+2-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

@@ -92,7 +92,7 @@ public function testLoadFullConfiguration($format)
9292
$this->assertTrue($options['auto_reload'], '->load() sets the auto_reload option');
9393
$this->assertSame('name', $options['autoescape'], '->load() sets the autoescape option');
9494
$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');
95+
$this->assertEquals(new Reference('twig.template_cache.chain'), $options['cache'], '->load() sets the cache option');
9696
$this->assertEquals('ISO-8859-1', $options['charset'], '->load() sets the charset option');
9797
$this->assertTrue($options['debug'], '->load() sets the debug option');
9898
$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.