|
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; |
| 26 | +use Symfony\Component\DependencyInjection\Exception\RuntimeException as DIRuntimeException; |
25 | 27 | use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; |
26 | 28 |
|
| 29 | +use function array_keys; |
27 | 30 | use function array_merge; |
| 31 | +use function assert; |
28 | 32 | use function class_exists; |
| 33 | +use function file_exists; |
| 34 | +use function file_put_contents; |
| 35 | +use function function_exists; |
29 | 36 | use function getcwd; |
30 | 37 | use function implode; |
31 | 38 | use function is_a; |
| 39 | +use function is_dir; |
| 40 | +use function md5; |
| 41 | +use function mkdir; |
| 42 | +use function opcache_invalidate; |
32 | 43 | use function rtrim; |
| 44 | +use function serialize; |
33 | 45 | use function sprintf; |
34 | 46 | use function strrchr; |
35 | 47 | use function substr; |
36 | 48 |
|
37 | 49 | final class ContainerFactory |
38 | 50 | { |
| 51 | + private const CACHE_DIR = '/tmp/guides-container-cache'; |
| 52 | + private const CACHE_CLASS = 'CachedGuidesContainer'; |
| 53 | + |
39 | 54 | private readonly ContainerBuilder $container; |
40 | 55 | private readonly XmlFileLoader $configLoader; |
41 | 56 |
|
@@ -78,16 +93,72 @@ public function addConfigFile(string $filePath): void |
78 | 93 |
|
79 | 94 | public function create(string $vendorDir): Container |
80 | 95 | { |
81 | | - $this->processConfig(); |
| 96 | + $cacheKey = $this->generateCacheKey($vendorDir); |
| 97 | + $cacheFile = self::CACHE_DIR . '/' . self::CACHE_CLASS . '_' . $cacheKey . '.php'; |
| 98 | + $cacheClass = self::CACHE_CLASS . '_' . $cacheKey; |
| 99 | + |
| 100 | + // Try to load cached container |
| 101 | + if (file_exists($cacheFile)) { |
| 102 | + require_once $cacheFile; |
| 103 | + if (class_exists($cacheClass, false)) { |
| 104 | + $container = new $cacheClass(); |
| 105 | + assert($container instanceof Container); |
| 106 | + |
| 107 | + return $container; |
| 108 | + } |
| 109 | + } |
82 | 110 |
|
| 111 | + // Build container |
| 112 | + $this->processConfig(); |
83 | 113 | $this->container->setParameter('vendor_dir', $vendorDir); |
84 | 114 | $this->container->setParameter('working_directory', rtrim(getcwd(), '/')); |
85 | | - |
86 | 115 | $this->container->compile(true); |
87 | 116 |
|
| 117 | + // Try to cache the compiled container (may fail if container has object parameters) |
| 118 | + try { |
| 119 | + $this->cacheContainer($cacheFile, $cacheClass); |
| 120 | + } catch (DIRuntimeException) { |
| 121 | + // Container cannot be cached (has object/resource parameters), continue without caching |
| 122 | + } |
| 123 | + |
88 | 124 | return $this->container; |
89 | 125 | } |
90 | 126 |
|
| 127 | + private function generateCacheKey(string $vendorDir): string |
| 128 | + { |
| 129 | + $workingDir = getcwd(); |
| 130 | + $configData = [ |
| 131 | + 'vendor_dir' => $vendorDir, |
| 132 | + 'working_dir' => $workingDir !== false ? rtrim($workingDir, '/') : '', |
| 133 | + 'extensions' => array_keys($this->registeredExtensions), |
| 134 | + 'configs' => serialize($this->configs), |
| 135 | + ]; |
| 136 | + |
| 137 | + return substr(md5(serialize($configData)), 0, 12); |
| 138 | + } |
| 139 | + |
| 140 | + private function cacheContainer(string $cacheFile, string $cacheClass): void |
| 141 | + { |
| 142 | + if (!is_dir(self::CACHE_DIR)) { |
| 143 | + @mkdir(self::CACHE_DIR, 0755, true); |
| 144 | + } |
| 145 | + |
| 146 | + $dumper = new PhpDumper($this->container); |
| 147 | + $code = $dumper->dump([ |
| 148 | + 'class' => $cacheClass, |
| 149 | + 'base_class' => Container::class, |
| 150 | + ]); |
| 151 | + |
| 152 | + file_put_contents($cacheFile, $code); |
| 153 | + |
| 154 | + // Invalidate opcache for the new file |
| 155 | + if (!function_exists('opcache_invalidate')) { |
| 156 | + return; |
| 157 | + } |
| 158 | + |
| 159 | + opcache_invalidate($cacheFile, true); |
| 160 | + } |
| 161 | + |
91 | 162 | /** @param array<mixed> $config */ |
92 | 163 | private function registerExtension(ExtensionInterface $extension, array $config): void |
93 | 164 | { |
|
0 commit comments