defaultServices = $earlyInstances + [ self::class => $this ]; } /** * @internal */ public function warmupCache(PackageManager $packageManager, FrontendInterface $cache): void { $registry = new ServiceProviderRegistry($packageManager); $containerBuilder = $this->buildContainer($packageManager, $registry); $cacheIdentifier = $this->getCacheIdentifier($packageManager); $this->dumpContainer($containerBuilder, $cache, $cacheIdentifier); } public function createDependencyInjectionContainer(PackageManager $packageManager, FrontendInterface $cache, bool $failsafe = false): ContainerInterface { if (!$cache instanceof PhpFrontend) { throw new \RuntimeException('Cache must be instance of PhpFrontend', 1582022226); } $serviceProviderRegistry = new ServiceProviderRegistry($packageManager, $failsafe); if ($failsafe) { return new FailsafeContainer($serviceProviderRegistry, $this->defaultServices); } $cacheIdentifier = $this->getCacheIdentifier($packageManager); $containerClassName = $cacheIdentifier; $hasCache = $cache->requireOnce($cacheIdentifier) !== false; if (!$hasCache) { $containerBuilder = $this->buildContainer($packageManager, $serviceProviderRegistry); $this->dumpContainer($containerBuilder, $cache, $cacheIdentifier); $cache->requireOnce($cacheIdentifier); } $container = new $containerClassName(); foreach ($this->defaultServices as $id => $service) { $container->set('_early.' . $id, $service); } $container->set($this->serviceProviderRegistryServiceName, $serviceProviderRegistry); return $container; } protected function buildContainer(PackageManager $packageManager, ServiceProviderRegistry $registry): SymfonyContainerBuilder { $containerBuilder = new SymfonyContainerBuilder(); $containerBuilder->addCompilerPass(new ResolveClassPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1000); $containerBuilder->addCompilerPass(new ServiceProviderCompilationPass($registry, $this->serviceProviderRegistryServiceName)); $globalConfigDir = Environment::getConfigPath(); // If the config folder is outside of the document root, we allow further services per-project // This is usually the case in composer-based installations if (Environment::getPublicPath() !== Environment::getProjectPath()) { if (file_exists($globalConfigDir . '/system/services.php')) { $phpFileLoader = new PhpFileLoader($containerBuilder, new FileLocator($globalConfigDir . '/system')); $phpFileLoader->load('services.php'); } if (file_exists($globalConfigDir . '/system/services.yaml')) { $yamlFileLoader = new YamlFileLoader($containerBuilder, new FileLocator($globalConfigDir . '/system')); $yamlFileLoader->load('services.yaml'); } } $packages = $packageManager->getActivePackages(); foreach ($packages as $package) { $diConfigDir = $package->getPackagePath() . 'Configuration/'; if (file_exists($diConfigDir . 'Services.php')) { $phpFileLoader = new PhpFileLoader($containerBuilder, new FileLocator($diConfigDir)); $phpFileLoader->load('Services.php'); } if (file_exists($diConfigDir . 'Services.yaml')) { $yamlFileLoader = new YamlFileLoader($containerBuilder, new FileLocator($diConfigDir)); $yamlFileLoader->load('Services.yaml'); } } // Store defaults entries in the DIC container // We need to use a workaround using aliases for synthetic services // But that's common in symfony (same technique is used to provide the // Symfony container interface as well). foreach (array_keys($this->defaultServices) as $id) { $syntheticId = '_early.' . $id; $containerBuilder->register($syntheticId)->setSynthetic(true)->setPublic(true); $containerBuilder->setAlias($id, $syntheticId)->setPublic(true); } // Optional service, set by BootService as back reference to the original bootService $containerBuilder->register('_early.boot-service')->setSynthetic(true)->setPublic(true); $containerBuilder->compile(); return $containerBuilder; } protected function dumpContainer(SymfonyContainerBuilder $containerBuilder, FrontendInterface $cache, string $cacheIdentifier): string { $containerClassName = $cacheIdentifier; $phpDumper = new PhpDumper($containerBuilder); $code = $phpDumper->dump(['class' => $containerClassName]); $code = str_replace('set($cacheIdentifier, $code); return $code; } /** * @internal may only be used in this class or in functional tests */ public function getCacheIdentifier(PackageManager $packageManager): string { $packageManagerCacheIdentifier = $packageManager->getCacheIdentifier() ?? ''; return $this->cacheIdentifiers[$packageManagerCacheIdentifier] ?? $this->createCacheIdentifier($packageManager, $packageManagerCacheIdentifier); } protected function createCacheIdentifier(PackageManager $packageManager, string $additionalIdentifier): string { return $this->cacheIdentifiers[$additionalIdentifier] = (new PackageDependentCacheIdentifier($packageManager)) ->withPrefix('DependencyInjectionContainer') ->withAdditionalHashedIdentifier('PHP-' . PHP_MAJOR_VERSION . '-' . PHP_MINOR_VERSION) ->toString(); } }