Skip to content

Commit aa73e1d

Browse files
committed
feature #22 [Cache] Cache the parsed entrypoints.json (Kocal)
This PR was squashed before being merged into the main branch. Discussion ---------- [Cache] Cache the parsed entrypoints.json | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT Adds an opt-in `cache` config key to `RepriseBundle` (default `false`), mirroring the option WebpackEncoreBundle already has. Right now, under PHP-FPM, `EntrypointsLookup` re-reads and `json_decode`s `entrypoints.json` on every single request before it can resolve one URL. With `cache: true`, the file is parsed once during `cache:warmup` and the resulting `Entrypoints` object is compiled into a `PhpArrayAdapter` file (`%kernel.build_dir%/reprise.cache.php`). At runtime the bundle just reads that object back instead of decoding JSON on each request. A few implementation details worth flagging for review: - `EntrypointsLookup` now goes through an optional PSR-6 cache: hit returns the cached object, miss reads the file and saves it. With `cache: false` (the default), nothing changes. - `EntrypointsCacheWarmer` writes the compiled file and never throws: a missing or malformed `entrypoints.json` at deploy time shouldn't be able to break `cache:warmup`. - What gets cached is the built `Entrypoints` object, not the raw array. `PhpArrayAdapter` exports it via `symfony/var-exporter`, which handles the bundle's `final`/`readonly` classes without needing `__set_state`. Nice side effect: validation runs once at warmup instead of on every request. - The cache pool and warmer are only wired when `cache: true`, behind a `class_exists()` guard that gives a clear error if the Cache component isn't there. `symfony/cache` and `symfony/var-exporter` stay `require-dev`, so projects that don't enable caching don't pull them in. - The cache is keyed once (Reprise is mono-build), and it's refreshed by your deploy's `cache:clear` + `cache:warmup`. So after rebuilding assets, run `cache:clear`. This is a production optimization, not something you'd want in dev. ## Usage ```yaml # config/packages/reprise.yaml reprise: cache: true ``` Enabling it requires the Cache component: ``` $ composer require symfony/cache ``` ## Testing Unit tests cover the cache read path (`ArrayAdapter`) and the warmer (round-tripping a real compiled file), plus a functional test booting a `cache: true` kernel. Added a `cache` entry to the Configuration section of `doc/index.rst`. Commits ------- f34968e [Cache] Cache the parsed entrypoints.json
2 parents 95fdf56 + f34968e commit aa73e1d

23 files changed

Lines changed: 1110 additions & 18 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ node_modules/
2323

2424
# PHP-CS-Fixer
2525
/.php-cs-fixer.cache
26+
27+
# Symfony config-builder reference, regenerated by the test kernel's cache warmer.
28+
/config/reference.php

.php-cs-fixer.dist.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function getRuleCustomisers(): array
5959
$relativePathname = $file->getRelativePathname();
6060

6161
if (
62-
str_contains($relativePathname, '/tests/') // don't touch test files, as massive change with little benefit - as outside of public contract anyway
62+
str_contains($relativePathname, 'tests/') // don't touch test files, as massive change with little benefit - as outside of public contract anyway
6363
|| str_contains($relativePathname, '/Test/') // public namespace not following the rule, do not mistake it with `/Tests/`
6464
) {
6565
return false;

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
"phpunit/phpunit": "^13.2",
2525
"phpstan/phpstan": "^2.2",
2626
"friendsofphp/php-cs-fixer": "^3.95",
27-
"symfony/web-link": "^7.4|^8.0"
27+
"symfony/web-link": "^7.4|^8.0",
28+
"symfony/cache": "^7.4|^8.0",
29+
"symfony/var-exporter": "^7.4|^8.0"
2830
},
2931
"autoload": {
3032
"psr-4": { "Symfony\\Reprise\\": "src/" }

config/cache.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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\Component\DependencyInjection\Loader\Configurator;
13+
14+
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
15+
use Symfony\Reprise\CacheWarmer\EntrypointsCacheWarmer;
16+
17+
return static function (ContainerConfigurator $container): void {
18+
$services = $container->services();
19+
20+
$services->set('cache.reprise')
21+
->parent('cache.system')
22+
->tag('cache.pool')
23+
;
24+
25+
$services->set('reprise.cache', PhpArrayAdapter::class)
26+
->args([
27+
'%kernel.build_dir%/reprise.cache.php',
28+
service('cache.reprise'),
29+
])
30+
;
31+
32+
$services->set('reprise.entrypoints_cache_warmer', EntrypointsCacheWarmer::class)
33+
->args([
34+
param('reprise.entrypoints_path'),
35+
'reprise.entrypoints',
36+
service('reprise.cache'),
37+
])
38+
->tag('kernel.cache_warmer')
39+
;
40+
};

doc/index.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ Reprise exposes a few optional settings under its own configuration, all shown h
154154
# Throw when entrypoints.json or a requested entry is missing, instead of rendering nothing.
155155
strict_mode: true
156156
157+
# Cache the parsed entrypoints.json in a compiled PHP file, warmed at cache:warmup (needs symfony/cache).
158+
cache: false
159+
157160
# A framework.assets package name used to resolve entry URLs. null uses the default package.
158161
asset_package: null
159162
@@ -171,6 +174,9 @@ Reprise exposes a few optional settings under its own configuration, all shown h
171174
own ``outputPath``.
172175
- ``strict_mode``: when ``true`` (the default), throws a clear exception on a missing file or an unknown entry; when
173176
``false``, renders nothing instead.
177+
- ``cache``: when ``true``, parse ``entrypoints.json`` once at ``cache:warmup`` and read it from a compiled PHP file
178+
at runtime instead of decoding the JSON on every request. Enable it in production and run ``cache:clear`` after
179+
rebuilding your assets; it needs ``symfony/cache`` (``composer require symfony/cache``).
174180
- ``asset_package``: resolve entry URLs through a specific ``framework.assets`` package instead of the default one.
175181
You only need this if your default package applies a version strategy, which would re-hash files Reprise already
176182
content-hashed and break the URLs. Point it at a package with ``version: false`` (see below).

0 commit comments

Comments
 (0)