|
22 | 22 | use Symfony\Component\Config\FileLocator; |
23 | 23 | use Symfony\Component\DependencyInjection\Container; |
24 | 24 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
| 25 | +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; |
25 | 26 | use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; |
26 | 27 |
|
| 28 | +use function array_keys; |
27 | 29 | use function array_merge; |
28 | 30 | use function class_exists; |
| 31 | +use function file_exists; |
| 32 | +use function file_put_contents; |
| 33 | +use function function_exists; |
29 | 34 | use function getcwd; |
30 | 35 | use function implode; |
31 | 36 | use function is_a; |
| 37 | +use function is_dir; |
| 38 | +use function md5; |
| 39 | +use function mkdir; |
| 40 | +use function opcache_invalidate; |
32 | 41 | use function rtrim; |
| 42 | +use function serialize; |
33 | 43 | use function sprintf; |
34 | 44 | use function strrchr; |
35 | 45 | use function substr; |
36 | 46 |
|
37 | 47 | final class ContainerFactory |
38 | 48 | { |
| 49 | + private const CACHE_DIR = '/tmp/guides-container-cache'; |
| 50 | + private const CACHE_CLASS = 'CachedGuidesContainer'; |
| 51 | + |
39 | 52 | private readonly ContainerBuilder $container; |
40 | 53 | private readonly XmlFileLoader $configLoader; |
41 | 54 |
|
@@ -78,16 +91,66 @@ public function addConfigFile(string $filePath): void |
78 | 91 |
|
79 | 92 | public function create(string $vendorDir): Container |
80 | 93 | { |
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 | + } |
82 | 108 |
|
| 109 | + // Build container |
| 110 | + $this->processConfig(); |
83 | 111 | $this->container->setParameter('vendor_dir', $vendorDir); |
84 | 112 | $this->container->setParameter('working_directory', rtrim(getcwd(), '/')); |
85 | | - |
86 | 113 | $this->container->compile(true); |
87 | 114 |
|
| 115 | + // Cache the compiled container |
| 116 | + $this->cacheContainer($cacheFile, $cacheClass); |
| 117 | + |
88 | 118 | return $this->container; |
89 | 119 | } |
90 | 120 |
|
| 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 | + |
91 | 154 | /** @param array<mixed> $config */ |
92 | 155 | private function registerExtension(ExtensionInterface $extension, array $config): void |
93 | 156 | { |
|
0 commit comments