diff --git a/composer.json b/composer.json index 3f791a9740c71e0966519e09b0715da37a2ea5e9..2e8cec9d3205207d437e7a807ba929dc924d369b 100644 --- a/composer.json +++ b/composer.json @@ -75,7 +75,7 @@ "symfony/var-dumper": "^5.2", "symfony/yaml": "^5.2", "typo3/class-alias-loader": "^1.0", - "typo3/cms-cli": "^2.0", + "typo3/cms-cli": "^3.0", "typo3/cms-composer-installers": "^2.0 || ^3.0", "typo3/phar-stream-wrapper": "^3.1.6", "typo3/symfony-psr-event-dispatcher-adapter": "^1.0 || ^2.0", diff --git a/composer.lock b/composer.lock index ecdaeff89dacc3ba9ee4785b7502bd428fa8f223..22157f65aa3725a3e361f0d511b58868719416f2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b4c3074ccc16c2e62a19f8ccba3f0998", + "content-hash": "ecf8365aa1212f8dec9609b56adab0f2", "packages": [ { "name": "bacon/bacon-qr-code", @@ -4373,16 +4373,16 @@ }, { "name": "typo3/cms-cli", - "version": "2.0.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/TYPO3/cms-cli.git", - "reference": "215a0bf5c446b4e0b20f4562bbaf3d6215a5ee82" + "reference": "bfb13f4ab6a505104662b79c18108f41c48e9288" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3/cms-cli/zipball/215a0bf5c446b4e0b20f4562bbaf3d6215a5ee82", - "reference": "215a0bf5c446b4e0b20f4562bbaf3d6215a5ee82", + "url": "https://api.github.com/repos/TYPO3/cms-cli/zipball/bfb13f4ab6a505104662b79c18108f41c48e9288", + "reference": "bfb13f4ab6a505104662b79c18108f41c48e9288", "shasum": "" }, "require": { @@ -4400,9 +4400,9 @@ "homepage": "https://typo3.org", "support": { "issues": "https://github.com/TYPO3/cms-cli/issues", - "source": "https://github.com/TYPO3/cms-cli/tree/master" + "source": "https://github.com/TYPO3/cms-cli/tree/3.0.0" }, - "time": "2018-03-08T20:16:43+00:00" + "time": "2021-02-09T12:45:27+00:00" }, { "name": "typo3/cms-composer-installers", @@ -4622,12 +4622,12 @@ "version": "1.9.1", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", + "url": "https://github.com/webmozarts/assert.git", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, @@ -4665,8 +4665,8 @@ "validate" ], "support": { - "issues": "https://github.com/webmozart/assert/issues", - "source": "https://github.com/webmozart/assert/tree/master" + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.9.1" }, "time": "2020-07-08T17:02:28+00:00" } diff --git a/typo3/sysext/core/Classes/Command/Descriptor/TextDescriptor.php b/typo3/sysext/core/Classes/Command/Descriptor/TextDescriptor.php index 8d56392e76e3d295bd37bd670776d6991da3a5f8..62b9d8ed2c73890e821945e772326e490ddc0e9a 100644 --- a/typo3/sysext/core/Classes/Command/Descriptor/TextDescriptor.php +++ b/typo3/sysext/core/Classes/Command/Descriptor/TextDescriptor.php @@ -32,10 +32,12 @@ use TYPO3\CMS\Core\Console\CommandRegistry; class TextDescriptor extends SymfonyTextDescriptor { private CommandRegistry $commandRegistry; + private bool $degraded; - public function __construct(CommandRegistry $commandRegistry) + public function __construct(CommandRegistry $commandRegistry, bool $degraded) { $this->commandRegistry = $commandRegistry; + $this->degraded = $degraded; } /** @@ -57,6 +59,10 @@ class TextDescriptor extends SymfonyTextDescriptor return; } + if ($this->degraded) { + $this->write("<error>Failed to boot dependency injection, only lowlevel commands are available.</error>\n\n", true); + } + $namespaces = $this->commandRegistry->getNamespaces(); $help = $application->getHelp(); if ($help !== '') { @@ -89,6 +95,10 @@ class TextDescriptor extends SymfonyTextDescriptor } $this->write("\n"); + + if ($this->degraded) { + $this->write("\n<error>Failed to boot dependency injection, only lowlevel commands are available.</error>\n", true); + } } private function describeNamespace(array $namespace, array $commands, int $width): void diff --git a/typo3/sysext/core/Classes/Command/ListCommand.php b/typo3/sysext/core/Classes/Command/ListCommand.php index 193afc6ca7bfbafa67204d30293e041bbc311293..887305c5f1f26d132865e2d7cfee0b315e5f2a28 100644 --- a/typo3/sysext/core/Classes/Command/ListCommand.php +++ b/typo3/sysext/core/Classes/Command/ListCommand.php @@ -17,23 +17,27 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Command; +use Psr\Container\ContainerInterface; use Symfony\Component\Console\Command\ListCommand as SymfonyListCommand; use Symfony\Component\Console\Helper\DescriptorHelper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use TYPO3\CMS\Core\Command\Descriptor\TextDescriptor; use TYPO3\CMS\Core\Console\CommandRegistry; +use TYPO3\CMS\Core\Core\BootService; /** * ListCommand displays the list of all available commands for the application. */ class ListCommand extends SymfonyListCommand { - protected CommandRegistry $commandRegistry; + protected ContainerInterface $failsafeContainer; + protected BootService $bootService; - public function __construct(CommandRegistry $commandRegistry) + public function __construct(ContainerInterface $failsafeContainer, BootService $bootService) { - $this->commandRegistry = $commandRegistry; + $this->failsafeContainer = $failsafeContainer; + $this->bootService = $bootService; parent::__construct(); } @@ -42,8 +46,18 @@ class ListCommand extends SymfonyListCommand */ protected function execute(InputInterface $input, OutputInterface $output) { + $degraded = false; + try { + $container = $this->bootService->getContainer(); + } catch (\Throwable $e) { + $container = $this->failsafeContainer; + $degraded = true; + } + + $commandRegistry = $container->get(CommandRegistry::class); + $helper = new DescriptorHelper(); - $helper->register('txt', new TextDescriptor($this->commandRegistry)); + $helper->register('txt', new TextDescriptor($commandRegistry, $degraded)); $helper->describe($output, $this->getApplication(), [ 'format' => $input->getOption('format'), 'raw_text' => $input->getOption('raw'), diff --git a/typo3/sysext/core/Classes/Console/CommandApplication.php b/typo3/sysext/core/Classes/Console/CommandApplication.php index 3c93507f10b5f56d75fa565a743a91c81816bf35..0a7ea3f43e1036aba98531abe6d4e8bee1a89d58 100644 --- a/typo3/sysext/core/Classes/Console/CommandApplication.php +++ b/typo3/sysext/core/Classes/Console/CommandApplication.php @@ -17,15 +17,18 @@ namespace TYPO3\CMS\Core\Console; use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\ExceptionInterface; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Output\ConsoleOutput; use TYPO3\CMS\Core\Authentication\CommandLineUserAuthentication; +use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\DateTimeAspect; use TYPO3\CMS\Core\Context\UserAspect; use TYPO3\CMS\Core\Context\VisibilityAspect; use TYPO3\CMS\Core\Context\WorkspaceAspect; use TYPO3\CMS\Core\Core\ApplicationInterface; +use TYPO3\CMS\Core\Core\BootService; use TYPO3\CMS\Core\Core\Bootstrap; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Information\Typo3Version; @@ -37,26 +40,27 @@ use TYPO3\CMS\Core\Localization\LanguageService; */ class CommandApplication implements ApplicationInterface { - /** - * @var Context - */ - protected $context; + protected Context $context; - /** - * @var CommandRegistry - */ - protected $commandRegistry; + protected CommandRegistry $commandRegistry; - /** - * Instance of the symfony application - * @var Application - */ - protected $application; + protected ConfigurationManager $configurationManager; - public function __construct(Context $context, CommandRegistry $commandRegistry) - { + protected BootService $bootService; + + protected Application $application; + + public function __construct( + Context $context, + CommandRegistry $commandRegistry, + ConfigurationManager $configurationMananger, + BootService $bootService + ) { $this->context = $context; $this->commandRegistry = $commandRegistry; + $this->configurationManager = $configurationMananger; + $this->bootService = $bootService; + $this->checkEnvironmentOrDie(); $this->application = new Application('TYPO3 CMS', sprintf( '%s (Application Context: <comment>%s</comment>)', @@ -76,12 +80,31 @@ class CommandApplication implements ApplicationInterface */ public function run(callable $execute = null) { - $this->initializeContext(); - $input = new ArgvInput(); $output = new ConsoleOutput(); - Bootstrap::loadExtTables(); + $commandName = $this->getCommandName($input); + if ($this->wantsFullBoot($commandName)) { + // Do a full boot if command is not a low-level command + $container = $this->bootService->getContainer(); + $this->application->setCommandLoader($container->get(CommandRegistry::class)); + $this->context = $container->get(Context::class); + + $isLowLevelCommandShortcut = false; + try { + $realName = $this->application->find($commandName)->getName(); + // Do not load ext_localconf if a low level command was found + // due to using a shortcut + $isLowLevelCommandShortcut = !$this->wantsFullBoot($realName); + } catch (ExceptionInterface $e) { + // Errors must be ignored, full binding/validation happens later when the console application runs. + } + if (!$isLowLevelCommandShortcut && $this->essentialConfigurationExists()) { + $this->bootService->loadExtLocalconfDatabaseAndExtTables(); + } + } + + $this->initializeContext(); // create the BE_USER object (not logged in yet) Bootstrap::initializeBackendUser(CommandLineUserAuthentication::class); $GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']); @@ -97,6 +120,36 @@ class CommandApplication implements ApplicationInterface exit($exitCode); } + protected function wantsFullBoot(string $commandName): bool + { + if ($commandName === 'help') { + return true; + } + return !$this->commandRegistry->has($commandName); + } + + protected function getCommandName(ArgvInput $input): string + { + try { + $input->bind($this->application->getDefinition()); + } catch (ExceptionInterface $e) { + // Errors must be ignored, full binding/validation happens later when the console application runs. + } + + return $input->getFirstArgument() ?? 'list'; + } + + /** + * Check if LocalConfiguration.php and PackageStates.php exist + * + * @return bool TRUE when the essential configuration is available, otherwise FALSE + */ + protected function essentialConfigurationExists(): bool + { + return file_exists($this->configurationManager->getLocalConfigurationFileLocation()) + && file_exists(Environment::getLegacyConfigPath() . '/PackageStates.php'); + } + /** * Check the script is called from a cli environment. */ diff --git a/typo3/sysext/core/Classes/Core/BootService.php b/typo3/sysext/core/Classes/Core/BootService.php new file mode 100644 index 0000000000000000000000000000000000000000..2501940781e1d1990f91d37ecfb9f3565db52a23 --- /dev/null +++ b/typo3/sysext/core/Classes/Core/BootService.php @@ -0,0 +1,155 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Core; + +use Psr\Container\ContainerInterface; +use Psr\EventDispatcher\EventDispatcherInterface; +use TYPO3\CMS\Core\DependencyInjection\ContainerBuilder; +use TYPO3\CMS\Core\Imaging\IconRegistry; +use TYPO3\CMS\Core\Package\PackageManager; +use TYPO3\CMS\Core\Page\PageRenderer; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * @internal This is NOT an API class, it is for internal use in TYPO3 core only. + */ +class BootService +{ + private ContainerBuilder $containerBuilder; + + private ContainerInterface $failsafeContainer; + + private ?ContainerInterface $container = null; + + public function __construct(ContainerBuilder $containerBuilder, ContainerInterface $failsafeContainer) + { + $this->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); + $this->container->set('_early.cache.assets', Bootstrap::createCache('assets')); + $this->container->get(PackageManager::class)->injectCoreCache($coreCache); + } + + return $this->container; + } + + /** + * Switch global context to a new context, or revert + * to the original booting container if no container + * is specified + * + * @param ContainerInterface $container + * @param array $backup + * @return array + */ + 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 + * + * 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 or ext_tables code! Use only if really needed. + * + * @param bool $resetContainer + * @param bool $allowCaching + * @return ContainerInterface + */ + public function loadExtLocalconfDatabaseAndExtTables(bool $resetContainer = false, bool $allowCaching = true): ContainerInterface + { + $container = $this->getContainer($allowCaching); + + $backup = $this->makeCurrent($container); + $beUserBackup = $GLOBALS['BE_USER'] ?? null; + + $container->get('boot.state')->done = false; + $assetsCache = $container->get('cache.assets'); + IconRegistry::setCache($assetsCache); + PageRenderer::setCache($assetsCache); + $eventDispatcher = $container->get(EventDispatcherInterface::class); + ExtensionManagementUtility::setEventDispatcher($eventDispatcher); + Bootstrap::loadTypo3LoadedExtAndExtLocalconf($allowCaching, $container->get('cache.core')); + Bootstrap::unsetReservedGlobalVariables(); + $GLOBALS['BE_USER'] = $beUserBackup; + $container->get('boot.state')->done = true; + Bootstrap::loadBaseTca($allowCaching); + Bootstrap::loadExtTables($allowCaching); + + if ($resetContainer) { + $this->makeCurrent(null, $backup); + } + + return $container; + } + + public function resetGlobalContainer(): void + { + $this->makeCurrent(null, []); + } + + public function getFailsafeContainer(): ContainerInterface + { + return $this->failsafeContainer; + } +} diff --git a/typo3/sysext/core/Classes/DependencyInjection/ContainerBuilder.php b/typo3/sysext/core/Classes/DependencyInjection/ContainerBuilder.php index 8df0e0ae494d96c652288e2cd80f966d6d482d08..040f679bce1043c7fc759d57c4ef8169520a5f14 100644 --- a/typo3/sysext/core/Classes/DependencyInjection/ContainerBuilder.php +++ b/typo3/sysext/core/Classes/DependencyInjection/ContainerBuilder.php @@ -127,6 +127,9 @@ class ContainerBuilder $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; diff --git a/typo3/sysext/core/Classes/ServiceProvider.php b/typo3/sysext/core/Classes/ServiceProvider.php index 08fafac75df186b2936afb6b0692a6fd0c7d2ab2..6c8e53c76ff064cda8be878b23c7a5858b1c2142 100644 --- a/typo3/sysext/core/Classes/ServiceProvider.php +++ b/typo3/sysext/core/Classes/ServiceProvider.php @@ -23,6 +23,7 @@ use Psr\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Console\Command\HelpCommand; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as SymfonyEventDispatcherInterface; use TYPO3\CMS\Core\Core\Environment; +use TYPO3\CMS\Core\DependencyInjection\ContainerBuilder; use TYPO3\CMS\Core\Package\AbstractServiceProvider; use TYPO3\SymfonyPsrEventDispatcherAdapter\EventDispatcherAdapter as SymfonyEventDispatcher; @@ -49,6 +50,7 @@ class ServiceProvider extends AbstractServiceProvider Console\CommandApplication::class => [ static::class, 'getConsoleCommandApplication' ], Console\CommandRegistry::class => [ static::class, 'getConsoleCommandRegistry' ], Context\Context::class => [ static::class, 'getContext' ], + Core\BootService::class => [ static::class, 'getBootService' ], Crypto\PasswordHashing\PasswordHashFactory::class => [ static::class, 'getPasswordHashFactory' ], EventDispatcher\EventDispatcher::class => [ static::class, 'getEventDispatcher' ], EventDispatcher\ListenerProvider::class => [ static::class, 'getEventListenerProvider' ], @@ -137,7 +139,8 @@ class ServiceProvider extends AbstractServiceProvider public static function getListCommand(ContainerInterface $container): Command\ListCommand { return new Command\ListCommand( - $container->get(Console\CommandRegistry::class) + $container, + $container->get(Core\BootService::class) ); } @@ -155,7 +158,9 @@ class ServiceProvider extends AbstractServiceProvider { return new Console\CommandApplication( $container->get(Context\Context::class), - $container->get(Console\CommandRegistry::class) + $container->get(Console\CommandRegistry::class), + $container->get(Configuration\ConfigurationManager::class), + $container->get(Core\BootService::class) ); } @@ -193,6 +198,17 @@ class ServiceProvider extends AbstractServiceProvider return new Context\Context(); } + public static function getBootService(ContainerInterface $container): Core\BootService + { + if ($container->has('_early.boot-service')) { + return $container->get('_early.boot-service'); + } + return new Core\BootService( + $container->get(ContainerBuilder::class), + $container + ); + } + public static function getPasswordHashFactory(ContainerInterface $container): Crypto\PasswordHashing\PasswordHashFactory { return new Crypto\PasswordHashing\PasswordHashFactory(); diff --git a/typo3/sysext/core/Resources/Private/Php/cli.php b/typo3/sysext/core/Resources/Private/Php/cli.php index 7b9b55ee2e6c682596276738f9b8810b46d4b38f..d96be4e832a40e8d31b0381eb0808ee1b95d9b70 100644 --- a/typo3/sysext/core/Resources/Private/Php/cli.php +++ b/typo3/sysext/core/Resources/Private/Php/cli.php @@ -20,5 +20,5 @@ call_user_func(function () { $classLoader = require __DIR__ . '/../../../../../../vendor/autoload.php'; \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::run(4, \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_CLI); - \TYPO3\CMS\Core\Core\Bootstrap::init($classLoader)->get(\TYPO3\CMS\Core\Console\CommandApplication::class)->run(); + \TYPO3\CMS\Core\Core\Bootstrap::init($classLoader, true)->get(\TYPO3\CMS\Core\Console\CommandApplication::class)->run(); }); diff --git a/typo3/sysext/core/composer.json b/typo3/sysext/core/composer.json index ae5f12cb3d89bce0bded420f5cf9edfdc329aa27..bee5276c52847385a4f5a3e3543b9e10d77f7e5c 100644 --- a/typo3/sysext/core/composer.json +++ b/typo3/sysext/core/composer.json @@ -59,7 +59,7 @@ "symfony/routing": "^5.2", "symfony/yaml": "^5.2", "typo3/class-alias-loader": "^1.0", - "typo3/cms-cli": "^2.0", + "typo3/cms-cli": "^3.0", "typo3/cms-composer-installers": "^2.0 || ^3.0", "typo3/phar-stream-wrapper": "^3.1.6", "typo3/symfony-psr-event-dispatcher-adapter": "^1.0 || ^2.0", diff --git a/typo3/sysext/install/Classes/Command/UpgradeWizardListCommand.php b/typo3/sysext/install/Classes/Command/UpgradeWizardListCommand.php index f3e1089accad61da54c6adc6cf0f2d7dcb207072..6bc57dfe62c2f1386e17d498a6253d854292a5db 100644 --- a/typo3/sysext/install/Classes/Command/UpgradeWizardListCommand.php +++ b/typo3/sysext/install/Classes/Command/UpgradeWizardListCommand.php @@ -72,7 +72,7 @@ class UpgradeWizardListCommand extends Command */ protected function bootstrap(): void { - $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(); + $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(false); Bootstrap::initializeBackendUser(CommandLineUserAuthentication::class); Bootstrap::initializeBackendAuthentication(); } diff --git a/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php b/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php index 93ceba4d9393d7e193c87ca61fb9ecebbde58bc1..9e6280637dc55ff71abf2fa7de1b08667cf33e3c 100644 --- a/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php +++ b/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php @@ -77,7 +77,7 @@ class UpgradeWizardRunCommand extends Command */ protected function bootstrap(): void { - $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(); + $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(false); Bootstrap::initializeBackendUser(CommandLineUserAuthentication::class); Bootstrap::initializeBackendAuthentication(); $this->upgradeWizardsService->isDatabaseCharsetUtf8() ?: $this->upgradeWizardsService->setDatabaseCharsetUtf8(); diff --git a/typo3/sysext/install/Classes/Service/LateBootService.php b/typo3/sysext/install/Classes/Service/LateBootService.php index ac779dfa7ff823a2c93a0b7c78834d0e68a1a6fe..dd44754b6f5c6fc1420d04d19fc35c8b7398ce69 100644 --- a/typo3/sysext/install/Classes/Service/LateBootService.php +++ b/typo3/sysext/install/Classes/Service/LateBootService.php @@ -18,138 +18,20 @@ declare(strict_types=1); namespace TYPO3\CMS\Install\Service; use Psr\Container\ContainerInterface; -use Psr\EventDispatcher\EventDispatcherInterface; -use TYPO3\CMS\Core\Core\Bootstrap; -use TYPO3\CMS\Core\DependencyInjection\ContainerBuilder; -use TYPO3\CMS\Core\Imaging\IconRegistry; -use TYPO3\CMS\Core\Package\PackageManager; -use TYPO3\CMS\Core\Page\PageRenderer; -use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; -use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Core\BootService; /** * @internal This is NOT an API class, it is for internal use in the install tool only. */ -class LateBootService +class LateBootService extends BootService { - /** - * @var ContainerBuilder - */ - private $containerBuilder; - - /** - * @var ContainerInterface - */ - private $failsafeContainer; - - /** - * @var ContainerInterface - */ - private $container; - - /** - * @param ContainerBuilder $containerBuilder - * @param ContainerInterface $failsafeContainer - */ - public function __construct(ContainerBuilder $containerBuilder, ContainerInterface $failsafeContainer) - { - $this->containerBuilder = $containerBuilder; - $this->failsafeContainer = $failsafeContainer; - } - - /** - * @return ContainerInterface - */ - public function getContainer(): ContainerInterface - { - return $this->container ?? $this->prepareContainer(); - } - - /** - * @return ContainerInterface - */ - private function prepareContainer(): 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 - return $this->container = $this->containerBuilder->createDependencyInjectionContainer($packageManager, $dependencyInjectionContainerCache, $failsafe); - } - - /** - * Switch global context to a new context, or revert - * to the original booting container if no container - * is specified - * - * @param ContainerInterface $container - * @param array $backup - * @return array - */ - public function makeCurrent(ContainerInterface $container = null, array $backup = []): array + public function getContainer(bool $allowCaching = false): ContainerInterface { - $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 - * - * 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 or ext_tables code! Use only if really needed. - * - * @param bool $resetContainer - * @return ContainerInterface - */ - public function loadExtLocalconfDatabaseAndExtTables(bool $resetContainer = true): ContainerInterface - { - $container = $this->getContainer(); - - $backup = $this->makeCurrent($container); - - $container->get('boot.state')->done = false; - $assetsCache = $container->get('cache.assets'); - IconRegistry::setCache($assetsCache); - PageRenderer::setCache($assetsCache); - $eventDispatcher = $container->get(EventDispatcherInterface::class); - ExtensionManagementUtility::setEventDispatcher($eventDispatcher); - Bootstrap::loadTypo3LoadedExtAndExtLocalconf(false); - Bootstrap::unsetReservedGlobalVariables(); - $container->get('boot.state')->done = true; - Bootstrap::loadBaseTca(false); - Bootstrap::loadExtTables(false); - - if ($resetContainer) { - $this->makeCurrent(null, $backup); - } - - return $container; + return parent::getContainer($allowCaching); } - public function resetGlobalContainer(): void + public function loadExtLocalconfDatabaseAndExtTables(bool $resetContainer = true, bool $allowCaching = false): ContainerInterface { - $this->makeCurrent(null, []); + return parent::loadExtLocalconfDatabaseAndExtTables($resetContainer, $allowCaching); } }