Skip to content

Commit e24f21a

Browse files
committed
perf: add DI container caching for faster CLI startup
Cache the compiled Symfony DI container to /tmp/guides-container-cache/. On subsequent runs, loads pre-compiled PHP class instead of rebuilding the entire container (config parsing, service registration, compilation). Cache key includes vendor dir, working dir, extensions, and configs to ensure cache invalidation when configuration changes. See https://cybottm.github.io/render-guides/ for benchmark data.
1 parent 8acd95a commit e24f21a

File tree

1 file changed

+65
-2
lines changed

1 file changed

+65
-2
lines changed

packages/guides-cli/src/DependencyInjection/ContainerFactory.php

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,33 @@
2222
use Symfony\Component\Config\FileLocator;
2323
use Symfony\Component\DependencyInjection\Container;
2424
use Symfony\Component\DependencyInjection\ContainerBuilder;
25+
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
2526
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
2627

28+
use function array_keys;
2729
use function array_merge;
2830
use function class_exists;
31+
use function file_exists;
32+
use function file_put_contents;
33+
use function function_exists;
2934
use function getcwd;
3035
use function implode;
3136
use function is_a;
37+
use function is_dir;
38+
use function md5;
39+
use function mkdir;
40+
use function opcache_invalidate;
3241
use function rtrim;
42+
use function serialize;
3343
use function sprintf;
3444
use function strrchr;
3545
use function substr;
3646

3747
final class ContainerFactory
3848
{
49+
private const CACHE_DIR = '/tmp/guides-container-cache';
50+
private const CACHE_CLASS = 'CachedGuidesContainer';
51+
3952
private readonly ContainerBuilder $container;
4053
private readonly XmlFileLoader $configLoader;
4154

@@ -78,16 +91,66 @@ public function addConfigFile(string $filePath): void
7891

7992
public function create(string $vendorDir): Container
8093
{
81-
$this->processConfig();
94+
$cacheKey = $this->generateCacheKey($vendorDir);
95+
$cacheFile = self::CACHE_DIR . '/' . self::CACHE_CLASS . '_' . $cacheKey . '.php';
96+
$cacheClass = self::CACHE_CLASS . '_' . $cacheKey;
97+
98+
// Try to load cached container
99+
if (file_exists($cacheFile)) {
100+
require_once $cacheFile;
101+
if (class_exists($cacheClass, false)) {
102+
/** @var Container $container */
103+
$container = new $cacheClass();
104+
105+
return $container;
106+
}
107+
}
82108

109+
// Build container
110+
$this->processConfig();
83111
$this->container->setParameter('vendor_dir', $vendorDir);
84112
$this->container->setParameter('working_directory', rtrim(getcwd(), '/'));
85-
86113
$this->container->compile(true);
87114

115+
// Cache the compiled container
116+
$this->cacheContainer($cacheFile, $cacheClass);
117+
88118
return $this->container;
89119
}
90120

121+
private function generateCacheKey(string $vendorDir): string
122+
{
123+
$workingDir = getcwd();
124+
$configData = [
125+
'vendor_dir' => $vendorDir,
126+
'working_dir' => $workingDir !== false ? rtrim($workingDir, '/') : '',
127+
'extensions' => array_keys($this->registeredExtensions),
128+
'configs' => serialize($this->configs),
129+
];
130+
131+
return substr(md5(serialize($configData)), 0, 12);
132+
}
133+
134+
private function cacheContainer(string $cacheFile, string $cacheClass): void
135+
{
136+
if (!is_dir(self::CACHE_DIR)) {
137+
@mkdir(self::CACHE_DIR, 0755, true);
138+
}
139+
140+
$dumper = new PhpDumper($this->container);
141+
$code = $dumper->dump([
142+
'class' => $cacheClass,
143+
'base_class' => Container::class,
144+
]);
145+
146+
file_put_contents($cacheFile, $code);
147+
148+
// Invalidate opcache for the new file
149+
if (function_exists('opcache_invalidate')) {
150+
opcache_invalidate($cacheFile, true);
151+
}
152+
}
153+
91154
/** @param array<mixed> $config */
92155
private function registerExtension(ExtensionInterface $extension, array $config): void
93156
{

0 commit comments

Comments
 (0)