Skip to content

Commit 42d28da

Browse files
committed
bug #1497 Build reproductible TemplateMap (smnandre)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- Build reproductible TemplateMap | Q | A | ------------- | --- | Bug fix? | yes | New feature? | yes | Issues | Fix #1493 | License | MIT (prevent crashes when a LiveComponent has to work before/after a cache clear) See #1493 Commits ------- 5849a6c Build reproductible TemplateMap
2 parents aec04ad + 5849a6c commit 42d28da

File tree

4 files changed

+90
-5
lines changed

4 files changed

+90
-5
lines changed

src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,11 @@ function (ChildDefinition $definition, AsLiveComponent $attribute) {
256256
->setArguments(['%kernel.cache_dir%/'.self::TEMPLATES_MAP_FILENAME]);
257257

258258
$container->register('ux.live_component.twig.cache_warmer', TemplateCacheWarmer::class)
259-
->setArguments([new Reference('twig.template_iterator'), self::TEMPLATES_MAP_FILENAME])
259+
->setArguments([
260+
new Reference('twig.template_iterator'),
261+
self::TEMPLATES_MAP_FILENAME,
262+
'%kernel.secret%',
263+
])
260264
->addTag('kernel.cache_warmer');
261265
}
262266

src/LiveComponent/src/Twig/TemplateCacheWarmer.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@
2222
*/
2323
final class TemplateCacheWarmer implements CacheWarmerInterface
2424
{
25-
public function __construct(private \IteratorAggregate $templateIterator, private readonly string $cacheFilename)
26-
{
25+
public function __construct(
26+
private readonly \IteratorAggregate $templateIterator,
27+
private readonly string $cacheFilename,
28+
private readonly string $secret,
29+
) {
2730
}
2831

2932
public function warmUp(string $cacheDir, ?string $buildDir = null): array
3033
{
3134
$map = [];
3235
foreach ($this->templateIterator as $item) {
33-
$map[bin2hex(random_bytes(16))] = $item;
36+
$map[hash('xxh128', $item.$this->secret)] = $item;
3437
}
3538

3639
(new PhpArrayAdapter($cacheDir.'/'.$this->cacheFilename, new NullAdapter()))->warmUp(['map' => $map]);

src/LiveComponent/src/Twig/TemplateMap.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function __construct(string $cacheFile)
2828
$this->map = (new PhpArrayAdapter($cacheFile, new NullAdapter()))->getItem('map')->get();
2929
}
3030

31-
public function resolve(string $obscuredName)
31+
public function resolve(string $obscuredName): string
3232
{
3333
return $this->map[$obscuredName] ?? throw new \RuntimeException(sprintf('Cannot find a template matching "%s". Cache may be corrupt.', $obscuredName));
3434
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\UX\LiveComponent\Tests\Unit\Twig;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Cache\Adapter\NullAdapter;
16+
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
17+
use Symfony\UX\LiveComponent\Twig\TemplateCacheWarmer;
18+
19+
/**
20+
* @author Simon André <[email protected]>
21+
*/
22+
final class TemplateCacheWarmerTest extends TestCase
23+
{
24+
private string $cacheDir;
25+
private string $cacheFile;
26+
private TemplateCacheWarmer $templateCacheWarmer;
27+
28+
protected function setUp(): void
29+
{
30+
$this->cacheDir ??= sys_get_temp_dir();
31+
$this->cacheFile ??= $this->cacheDir.'/cache_file';
32+
if (file_exists($this->cacheFile)) {
33+
unlink($this->cacheFile);
34+
}
35+
$this->templateCacheWarmer ??= new TemplateCacheWarmer(
36+
new \ArrayObject(['template1', 'template2']),
37+
'cache_file',
38+
'secret'
39+
);
40+
}
41+
42+
public function testWarmUpCreatesCacheFile(): void
43+
{
44+
$this->assertFileDoesNotExist($this->cacheFile);
45+
46+
$this->templateCacheWarmer->warmUp($this->cacheDir);
47+
48+
$this->assertFileExists($this->cacheFile);
49+
}
50+
51+
public function testWarmUpCreatesCorrectCacheContent(): void
52+
{
53+
$this->templateCacheWarmer->warmUp($this->cacheDir);
54+
$adapter = new PhpArrayAdapter($this->cacheFile, new NullAdapter());
55+
$item = $adapter->getItem('map');
56+
57+
$this->assertSame(
58+
[
59+
hash('xxh128', 'template1secret') => 'template1',
60+
hash('xxh128', 'template2secret') => 'template2',
61+
],
62+
$item->get()
63+
);
64+
}
65+
66+
public function testWarmUpCreatesReproductibleTemplateMap(): void
67+
{
68+
$this->templateCacheWarmer->warmUp($this->cacheDir);
69+
$adapter = new PhpArrayAdapter($this->cacheFile, new NullAdapter());
70+
$map1 = $adapter->getItem('map')->get();
71+
72+
$this->templateCacheWarmer->warmUp($this->cacheDir);
73+
$adapter = new PhpArrayAdapter($this->cacheFile, new NullAdapter());
74+
$map2 = $adapter->getItem('map')->get();
75+
76+
$this->assertSame($map1, $map2);
77+
}
78+
}

0 commit comments

Comments
 (0)