containerBuilder = $containerBuilder; $this->failsafeContainer = $failsafeContainer; } public function getContainer(bool $allowCaching = true): ContainerInterface { return $this->container ?? $this->prepareContainer($allowCaching); } private function prepareContainer(bool $allowCaching = true): ContainerInterface { $packageManager = $this->failsafeContainer->get(PackageManager::class); $dependencyInjectionContainerCache = $this->failsafeContainer->get('cache.di'); $failsafe = false; // Build a non-failsafe container which is required for loading ext_localconf $this->container = $this->containerBuilder->createDependencyInjectionContainer($packageManager, $dependencyInjectionContainerCache, $failsafe); $this->container->set('_early.boot-service', $this); if ($allowCaching) { $this->container->get('boot.state')->cacheDisabled = false; $coreCache = Bootstrap::createCache('core'); // Core cache is initialized with a NullBackend in failsafe mode. // Replace it with a new cache that uses the real backend. $this->container->set('_early.cache.core', $coreCache); if (!Environment::isComposerMode()) { $this->container->get(PackageManager::class)->setPackageCache(Bootstrap::createPackageCache($coreCache)); } } return $this->container; } /** * Switch global context to a new context, or revert * to the original booting container if no container * is specified */ public function makeCurrent(?ContainerInterface $container = null, array $backup = []): array { $container = $container ?? $backup['container'] ?? $this->failsafeContainer; $newBackup = [ 'singletonInstances' => GeneralUtility::getSingletonInstances(), 'container' => GeneralUtility::getContainer(), ]; GeneralUtility::purgeInstances(); // Set global state to the non-failsafe container and it's instances GeneralUtility::setContainer($container); ExtensionManagementUtility::setPackageManager($container->get(PackageManager::class)); $backupSingletonInstances = $backup['singletonInstances'] ?? []; foreach ($backupSingletonInstances as $className => $instance) { GeneralUtility::setSingletonInstance($className, $instance); } return $newBackup; } /** * Bootstrap a non-failsafe container and load ext_localconf.phps of all extensions. * * Use by actions like the database analyzer and the upgrade wizards which * need additional bootstrap actions performed. * * Those actions can potentially fatal if some old extension is loaded that triggers * a fatal in ext_localconf code! Use only if really needed. */ public function loadExtLocalconfDatabase(bool $resetContainer = false, bool $allowCaching = true): ContainerInterface { $container = $this->getContainer($allowCaching); $backup = $this->makeCurrent($container); $beUserBackup = $GLOBALS['BE_USER'] ?? null; $container->get('boot.state')->complete = false; $eventDispatcher = $container->get(EventDispatcherInterface::class); $tcaFactory = $container->get(TcaFactory::class); if ($allowCaching) { $container->get(ExtLocalconfFactory::class)->load(); } else { $container->get(ExtLocalconfFactory::class)->loadUncached(); } $GLOBALS['BE_USER'] = $beUserBackup; if ($allowCaching) { $GLOBALS['TCA'] = $tcaFactory->get(); } else { $GLOBALS['TCA'] = $tcaFactory->create(); } $container->get('boot.state')->complete = true; if ($allowCaching) { $container->get(TcaSchemaFactory::class)->load($GLOBALS['TCA']); } else { $container->get(TcaSchemaFactory::class)->rebuild($GLOBALS['TCA']); } $eventDispatcher->dispatch(new BootCompletedEvent($allowCaching)); if ($resetContainer) { $this->makeCurrent(null, $backup); } return $container; } public function resetGlobalContainer(): void { $this->makeCurrent(null, []); } public function getFailsafeContainer(): ContainerInterface { return $this->failsafeContainer; } public function unsetInternalContainerInstance(): void { $this->container = null; } }