From a388232d1de9ec571d2bcb10459f1fd5fefdf58b Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Thu, 8 Feb 2018 21:21:05 +0100 Subject: [PATCH] [TASK] Decouple Bootstrap and Application engaging a PSR-11 container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to avoid global state and to support full-application subrequests (later on), bootstraping, application initialization and application execution needs to be decoupled. (To be able to initialize a frontend Application in backend Application context, the frontend Application may not re-execute bootstraping code.) That means from now on: * Bootstrap is limited to stateless bootstrapping => setting up package manager and configuration * A (new) Container (PSR-11) performs class initialization => e.g. `new Frontend/Http/Application()` * Application performs execution => checking possible (application specific) redirects => offloading work to the request handler This commit transforms Bootstrap into a (static) bootstrap utility that returns a container (minimal, static PSR-11 implementation). The entry-point scripts execute the bootstraper and use the returned container to initialize and run the application. This commit acts as a starting point for a broader PSR-11 container support in TYPO3. We do – on purpose – use an own, very limited, anonymous and static there is no configuration) PSR-11 implemententation for now. This interim container implementation will be replaced by whatever PSR-11 supporting container solution we use later on. That keeps the necessary Bootstrap refactoring seperate from the introduction of a full dependency injection container implementation. All existing bootstrap methods keep working as before but the non-static method invocation should be deprecated at some point. typo3/cms-cli is adapted for the changed entry point script with: https://github.com/TYPO3/cms-cli/pull/1 typo3/testing-framework is adapted in https://github.com/TYPO3/testing-framework/pull/55 This patch brings one important behavioral change: The install tool redirect (if essential configuration is missing) is perfomed during application execution – after the configuration has been loaded (falling back to failsafe mode if missing) – now. Previously the application performed the redirect before it would call Bootstraps configure() method. Now that the Application is decoupled from bootstrap, the bootstrapper ensures it can always create an Application class, in order for the application to be in charge of the decision what should happen if essential configuration is missing. Dependency changes: composer require psr/container:^1.0 composer require typo3/cms-cli:^2.0 composer require typo3/testing-framework:^3.2 --dev Change-Id: Idc59665dfcf7250a8a42b3d908a5a2376067700c Releases: master Resolves: #83951 Reviewed-on: https://review.typo3.org/55773 Tested-by: TYPO3com Reviewed-by: Christian Kuhn Tested-by: Christian Kuhn Reviewed-by: Benni Mack Tested-by: Benni Mack --- composer.json | 7 +- composer.lock | 93 +++- .../backend/Classes/Http/Application.php | 50 +- .../backend/Resources/Private/Php/backend.php | 4 +- .../Classes/Console/CommandApplication.php | 32 +- typo3/sysext/core/Classes/Core/Bootstrap.php | 428 +++++++++++++----- .../Classes/Core/SystemEnvironmentBuilder.php | 59 ++- .../core/Classes/Localization/Locales.php | 4 +- typo3/sysext/core/bin/typo3 | 3 +- typo3/sysext/core/composer.json | 5 +- .../frontend/Classes/Http/Application.php | 47 +- .../Resources/Private/Php/frontend.php | 5 +- .../install/Classes/Http/Application.php | 55 +-- .../install/Classes/Http/RequestHandler.php | 27 +- .../install/Resources/Private/Php/install.php | 3 +- 15 files changed, 545 insertions(+), 277 deletions(-) diff --git a/composer.json b/composer.json index 58efd20066dd..c0deb34aae4b 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,7 @@ "symfony/polyfill-mbstring": "^1.2", "doctrine/instantiator": "~1.0.4", "doctrine/annotations": "^1.3", - "typo3/cms-cli": "^1.0", + "typo3/cms-cli": "^2.0", "typo3/class-alias-loader": "^1.0", "typo3/cms-composer-installers": "^2.0", "psr/http-message": "~1.0", @@ -54,7 +54,8 @@ "doctrine/dbal": "^2.6", "nikic/php-parser": "^3.1", "symfony/polyfill-intl-icu": "^1.6", - "psr/http-server-middleware": "^1.0" + "psr/http-server-middleware": "^1.0", + "psr/container": "^1.0" }, "require-dev": { "codeception/codeception": "^2.3", @@ -62,7 +63,7 @@ "friendsofphp/php-cs-fixer": "^2.0", "fiunchinho/phpunit-randomizer": "~3.0.0", "typo3/cms-styleguide": "~9.0.1", - "typo3/testing-framework": "^3.0" + "typo3/testing-framework": "^3.2" }, "suggest": { "ext-gd": "GDlib/Freetype is required for building images with text (GIFBUILDER) and can also be used to scale images", diff --git a/composer.lock b/composer.lock index 795a96b2fa3f..b80845c9a839 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "7a5e50e93405a4bc5c093fa6904bf5b2", + "content-hash": "c2fe666b163be8ccee13df7d63c794cc", "packages": [ { "name": "cogpowered/finediff", @@ -864,6 +864,55 @@ ], "time": "2017-09-02T17:10:46+00:00" }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -1597,38 +1646,32 @@ }, { "name": "typo3/cms-cli", - "version": "1.0.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/TYPO3/cms-cli.git", - "reference": "713666d40ee56c1a362d1ce7c3f929613fc033b9" + "reference": "215a0bf5c446b4e0b20f4562bbaf3d6215a5ee82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3/cms-cli/zipball/713666d40ee56c1a362d1ce7c3f929613fc033b9", - "reference": "713666d40ee56c1a362d1ce7c3f929613fc033b9", + "url": "https://api.github.com/repos/TYPO3/cms-cli/zipball/215a0bf5c446b4e0b20f4562bbaf3d6215a5ee82", + "reference": "215a0bf5c446b4e0b20f4562bbaf3d6215a5ee82", "shasum": "" }, "require": { - "php": "^7.0", - "typo3/cms-core": "^8.7 || ^9.0@dev" + "php": "^7.0" }, "bin": [ "typo3" ], "type": "library", - "autoload": { - "psr-4": { - "TYPO3\\CMS\\Cli\\": "src" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ - "GPL-2.0+" + "GPL-2.0-or-later" ], "description": "TYPO3 command line binary", "homepage": "https://typo3.org", - "time": "2017-07-28T09:15:35+00:00" + "time": "2018-03-08T20:16:43+00:00" }, { "name": "typo3/cms-composer-installers", @@ -4414,31 +4457,31 @@ }, { "name": "typo3/testing-framework", - "version": "3.1.3", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/TYPO3/testing-framework.git", - "reference": "732597d356306d3b2dbcbfa73c46a34498b404b2" + "reference": "da30d1b49d60b4e4150d4eae5b128c449f2347ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3/testing-framework/zipball/732597d356306d3b2dbcbfa73c46a34498b404b2", - "reference": "732597d356306d3b2dbcbfa73c46a34498b404b2", + "url": "https://api.github.com/repos/TYPO3/testing-framework/zipball/da30d1b49d60b4e4150d4eae5b128c449f2347ea", + "reference": "da30d1b49d60b4e4150d4eae5b128c449f2347ea", "shasum": "" }, "require": { "mikey179/vfsstream": "~1.6.0", "phpunit/phpunit": "^6.2", - "typo3/cms-backend": "^9", - "typo3/cms-core": "^9", - "typo3/cms-extbase": "^9", - "typo3/cms-fluid": "^9", - "typo3/cms-frontend": "^9", + "typo3/cms-backend": "^9.1", + "typo3/cms-core": "^9.1", + "typo3/cms-extbase": "^9.1", + "typo3/cms-fluid": "^9.1", + "typo3/cms-frontend": "^9.1", "typo3fluid/fluid": "^2.2" }, "suggest": { "codeception/codeception": "^2.2", - "typo3/cms-saltedpasswords": "^9", + "typo3/cms-saltedpasswords": "^9.1", "typo3/cms-styleguide": "~8.0.8" }, "type": "library", @@ -4472,7 +4515,7 @@ "tests", "typo3" ], - "time": "2018-02-27T15:30:06+00:00" + "time": "2018-03-08T20:33:54+00:00" }, { "name": "webmozart/assert", diff --git a/typo3/sysext/backend/Classes/Http/Application.php b/typo3/sysext/backend/Classes/Http/Application.php index 5e4c744fe6b8..ab79a68f66f3 100644 --- a/typo3/sysext/backend/Classes/Http/Application.php +++ b/typo3/sysext/backend/Classes/Http/Application.php @@ -16,7 +16,8 @@ */ use Psr\Http\Message\ResponseInterface; -use TYPO3\CMS\Core\Core\Bootstrap; +use Psr\Http\Message\ServerRequestInterface; +use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\Http\AbstractApplication; use TYPO3\CMS\Core\Http\RedirectResponse; @@ -36,46 +37,38 @@ class Application extends AbstractApplication protected $middlewareStack = 'backend'; /** - * @var Bootstrap + * @var ConfigurationManager */ - protected $bootstrap; + protected $configurationManager; /** - * Number of subdirectories where the entry script is located, relative to PATH_site - * Usually this is equal to PATH_site = 0 - * @var int + * @param ConfigurationManager $configurationManager */ - protected $entryPointLevel = 1; + public function __construct(ConfigurationManager $configurationManager) + { + $this->configurationManager = $configurationManager; + } /** - * Constructor setting up legacy constant and register available Request Handlers - * - * @param \Composer\Autoload\ClassLoader $classLoader an instance of the class loader + * @param ServerRequestInterface $request + * @return ResponseInterface */ - public function __construct($classLoader) + protected function handle(ServerRequestInterface $request): ResponseInterface { - $this->defineLegacyConstants(); - - $this->bootstrap = Bootstrap::getInstance() - ->initializeClassLoader($classLoader) - ->setRequestType(TYPO3_REQUESTTYPE_BE | (isset($_REQUEST['route']) && strpos($_REQUEST['route'], '/ajax/') === 0 ? TYPO3_REQUESTTYPE_AJAX : 0)) - ->baseSetup($this->entryPointLevel); - - // Redirect to install tool if base configuration is not found - if (!$this->bootstrap->checkIfEssentialConfigurationExists()) { - $this->sendResponse($this->installToolRedirect()); - exit; + if (!$this->checkIfEssentialConfigurationExists()) { + return $this->installToolRedirect(); } - - $this->bootstrap->configure(); + return parent::handle($request); } /** - * Define constants and variables + * Check if LocalConfiguration.php and PackageStates.php exist + * + * @return bool TRUE when the essential configuration is available, otherwise FALSE */ - protected function defineLegacyConstants() + protected function checkIfEssentialConfigurationExists(): bool { - define('TYPO3_MODE', 'BE'); + return file_exists($this->configurationManager->getLocalConfigurationFileLocation()) && file_exists(PATH_typo3conf . 'PackageStates.php'); } /** @@ -85,7 +78,6 @@ protected function defineLegacyConstants() */ protected function installToolRedirect(): ResponseInterface { - $path = '../' . TYPO3_mainDir . 'install.php'; - return new RedirectResponse($path, 302); + return new RedirectResponse('./install.php', 302); } } diff --git a/typo3/sysext/backend/Resources/Private/Php/backend.php b/typo3/sysext/backend/Resources/Private/Php/backend.php index 83abb2284bc1..352dbc9512bf 100644 --- a/typo3/sysext/backend/Resources/Private/Php/backend.php +++ b/typo3/sysext/backend/Resources/Private/Php/backend.php @@ -20,6 +20,6 @@ // Set up the application for the backend call_user_func(function () { $classLoader = require __DIR__ . '/../../../../../../vendor/autoload.php'; - - (new \TYPO3\CMS\Backend\Http\Application($classLoader))->run(); + \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::run(1, \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_BE); + \TYPO3\CMS\Core\Core\Bootstrap::init($classLoader)->get(\TYPO3\CMS\Backend\Http\Application::class)->run(); }); diff --git a/typo3/sysext/core/Classes/Console/CommandApplication.php b/typo3/sysext/core/Classes/Console/CommandApplication.php index 93d6a2c0502e..6e0d6c5c629e 100644 --- a/typo3/sysext/core/Classes/Console/CommandApplication.php +++ b/typo3/sysext/core/Classes/Console/CommandApplication.php @@ -15,7 +15,6 @@ */ use Symfony\Component\Console\Input\ArgvInput; use TYPO3\CMS\Core\Core\ApplicationInterface; -use TYPO3\CMS\Core\Core\Bootstrap; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -26,31 +25,10 @@ class CommandApplication implements ApplicationInterface { /** - * @var Bootstrap */ - protected $bootstrap; - - /** - * Number of subdirectories where the entry script is located, relative to PATH_site - * @var int - */ - protected $entryPointLevel = 4; - - /** - * Constructor setting up legacy constants and register available Request Handlers - * - * @param \Composer\Autoload\ClassLoader $classLoader an instance of the class loader - */ - public function __construct($classLoader) + public function __construct() { $this->checkEnvironmentOrDie(); - $this->defineLegacyConstants(); - $this->bootstrap = Bootstrap::getInstance() - ->initializeClassLoader($classLoader) - ->setRequestType(TYPO3_REQUESTTYPE_CLI) - ->baseSetup($this->entryPointLevel); - - $this->bootstrap->configure(); } /** @@ -68,14 +46,6 @@ public function run(callable $execute = null) } } - /** - * Define constants and variables - */ - protected function defineLegacyConstants() - { - define('TYPO3_MODE', 'BE'); - } - /** * Check the script is called from a cli environment. */ diff --git a/typo3/sysext/core/Classes/Core/Bootstrap.php b/typo3/sysext/core/Classes/Core/Bootstrap.php index 837fe3666547..b09cd7f9d2ac 100644 --- a/typo3/sysext/core/Classes/Core/Bootstrap.php +++ b/typo3/sysext/core/Classes/Core/Bootstrap.php @@ -14,9 +14,18 @@ * The TYPO3 project - inspiring people to share! */ +use Composer\Autoload\ClassLoader; use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\AnnotationRegistry; +use Psr\Container\ContainerInterface; +use Psr\Container\NotFoundExceptionInterface; +use TYPO3\CMS\Core\Cache\CacheManager; +use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; +use TYPO3\CMS\Core\Configuration\ConfigurationManager; +use TYPO3\CMS\Core\Localization\Locales; use TYPO3\CMS\Core\Log\LogManager; +use TYPO3\CMS\Core\Package\FailsafePackageManager; +use TYPO3\CMS\Core\Package\PackageManager; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -38,20 +47,6 @@ class Bootstrap */ protected static $instance = null; - /** - * Unique Request ID - * - * @var string - */ - protected $requestId; - - /** - * The application context - * - * @var \TYPO3\CMS\Core\Core\ApplicationContext - */ - protected $applicationContext; - /** * @var array List of early instances */ @@ -59,14 +54,164 @@ class Bootstrap /** * Disable direct creation of this object. - * Set unique requestId and the application context - * - * @var string Application context */ - protected function __construct($applicationContext) + protected function __construct() { - $this->requestId = substr(md5(uniqid('', true)), 0, 13); - $this->applicationContext = new ApplicationContext($applicationContext); + } + + /** + * Bootstrap TYPO3 and return a Container that may be used + * to initialize an Application class. + * + * @param ClassLoader $classLoader an instance of the class loader + * @param bool $failsafe true if no caching and a failsaife package manager should be used + * @return ContainerInterface + */ + public static function init( + ClassLoader $classLoader, + bool $failsafe = false + ): ContainerInterface { + $requestId = substr(md5(uniqid('', true)), 0, 13); + $applicationContext = static::createApplicationContext(); + SystemEnvironmentBuilder::initializeEnvironment($applicationContext); + GeneralUtility::presetApplicationContext($applicationContext); + + static::initializeClassLoader($classLoader); + static::baseSetup(); + + static::startOutputBuffering(); + + $configurationManager = static::createConfigurationManager(); + if (!static::checkIfessentialConfigurationExists($configurationManager)) { + $failsafe = true; + } + static::populateLocalConfiguration($configurationManager); + static::initializeErrorHandling(); + + $logManager = new LogManager($requestId); + $cacheManager = static::createCacheManager($failsafe ? true : false); + $packageManager = static::createPackageManager( + $failsafe ? FailsafePackageManager::class : PackageManager::class, + $cacheManager->getCache('cache_core') + ); + + // Push singleton instances to GeneralUtility and ExtensionManagementUtility + // They should be fetched through a container (later) but currently a PackageManager + // singleton instance is required by PackageManager->activePackageDuringRuntime + GeneralUtility::setSingletonInstance(LogManager::class, $logManager); + GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManager); + GeneralUtility::setSingletonInstance(PackageManager::class, $packageManager); + ExtensionManagementUtility::setPackageManager($packageManager); + + static::initializeRuntimeActivatedPackagesFromConfiguration($packageManager); + + static::setDefaultTimeZone(); + $locales = Locales::initialize(); + static::setMemoryLimit(); + + // Create (to be deprecated) bootstrap instance with (to be deprecated) early instances + static::$instance = new static(); + static::$instance->earlyInstances = [ + ClassLoader::class => $classLoader, + ConfigurationManager::class => $configurationManager, + CacheManager::class => $cacheManager, + PackageManager::class => $packageManager, + ]; + + if (!$failsafe) { + static::loadTypo3LoadedExtAndExtLocalconf(true); + static::setFinalCachingFrameworkCacheConfiguration($cacheManager); + static::unsetReservedGlobalVariables(); + static::loadBaseTca(); + static::checkEncryptionKey(); + } + + $defaultContainerEntries = [ + ClassLoader::class => $classLoader, + 'request.id' => $requestId, + ApplicationContext::class => $applicationContext, + ConfigurationManager::class => $configurationManager, + LogManager::class => $logManager, + CacheManager::class => $cacheManager, + PackageManager::class => $packageManager, + Locales::class => $locales, + ]; + + return new class($defaultContainerEntries) implements ContainerInterface { + /** + * @var array + */ + private $entries; + + /** + * @param array $entries + */ + public function __construct(array $entries) + { + $this->entries = $entries; + } + + /** + * @param string $id Identifier of the entry to look for. + * @return bool + */ + public function has($id) + { + if (isset($this->entries[$id])) { + return true; + } + + switch ($id) { + case \TYPO3\CMS\Frontend\Http\Application::class: + case \TYPO3\CMS\Backend\Http\Application::class: + case \TYPO3\CMS\Install\Http\Application::class: + case \TYPO3\CMS\Core\Console\CommandApplication::class: + return true; + } + + return false; + } + + /** + * Method get() as specified in ContainerInterface + * + * @param string $id + * @return mixed + * @throws NotFoundExceptionInterface + */ + public function get($id) + { + $entry = null; + + if (isset($this->entries[$id])) { + return $this->entries[$id]; + } + + switch ($id) { + case \TYPO3\CMS\Frontend\Http\Application::class: + case \TYPO3\CMS\Backend\Http\Application::class: + $entry = new $id($this->get(ConfigurationManager::class)); + break; + case \TYPO3\CMS\Install\Http\Application::class: + $entry = new $id( + GeneralUtility::makeInstance(\TYPO3\CMS\Install\Http\RequestHandler::class, $this->get(ConfigurationManager::class)), + GeneralUtility::makeInstance(\TYPO3\CMS\Install\Http\InstallerRequestHandler::class) + ); + break; + case \TYPO3\CMS\Core\Console\CommandApplication::class: + $entry = new $id; + break; + default: + throw new class($id . ' not found', 1518638338) extends \Exception implements NotFoundExceptionInterface { + }; + break; + } + + $this->entries[$id] = $entry; + + return $entry; + } + }; } /** @@ -89,18 +234,30 @@ protected function __clone() * * @return Bootstrap * @internal This is not a public API method, do not use in own extensions + * @todo deprecate */ public static function getInstance() { if (static::$instance === null) { - $applicationContext = getenv('TYPO3_CONTEXT') ?: (getenv('REDIRECT_TYPO3_CONTEXT') ?: 'Production'); - self::$instance = new static($applicationContext); + self::$instance = new static(); self::$instance->defineTypo3RequestTypes(); - GeneralUtility::setSingletonInstance(LogManager::class, new LogManager(self::$instance->requestId)); + $requestId = substr(md5(uniqid('', true)), 0, 13); + GeneralUtility::setSingletonInstance(LogManager::class, new LogManager($requestId)); } return static::$instance; } + /** + * @return ApplicationContext + * @internal This is not a public API method, do not use in own extensions + */ + public static function createApplicationContext(): ApplicationContext + { + $applicationContext = getenv('TYPO3_CONTEXT') ?: (getenv('REDIRECT_TYPO3_CONTEXT') ?: 'Production'); + + return new ApplicationContext($applicationContext); + } + /** * Prevent any unwanted output that may corrupt AJAX/compression. * This does not interfere with "die()" or "echo"+"exit()" messages! @@ -121,6 +278,7 @@ public static function startOutputBuffering() * Make sure that the baseSetup() is called before and the class loader is present * * @return Bootstrap + * @todo deprecate */ public function configure() { @@ -129,13 +287,8 @@ public function configure() ->loadTypo3LoadedExtAndExtLocalconf(true) ->setFinalCachingFrameworkCacheConfiguration() ->unsetReservedGlobalVariables() - ->loadBaseTca(); - if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) { - throw new \RuntimeException( - 'TYPO3 Encryption is empty. $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'encryptionKey\'] needs to be set for TYPO3 to work securely', - 1502987245 - ); - } + ->loadBaseTca() + ->checkEncryptionKey(); return $this; } @@ -149,31 +302,34 @@ public function configure() * @return Bootstrap * @throws \RuntimeException when TYPO3_REQUESTTYPE was not set before, setRequestType() needs to be called before * @internal This is not a public API method, do not use in own extensions + * @todo deprecate entryPointLevel parameter */ - public function baseSetup($entryPointLevel = 0) + public static function baseSetup($entryPointLevel = 0) { if (!defined('TYPO3_REQUESTTYPE')) { throw new \RuntimeException('No Request Type was set, TYPO3 does not know in which context it is run.', 1450561838); } - GeneralUtility::presetApplicationContext($this->applicationContext); - SystemEnvironmentBuilder::run($entryPointLevel); - SystemEnvironmentBuilder::initializeEnvironment($this->applicationContext); + if (GeneralUtility::getApplicationContext() === null) { // @todo deprecate + $applicationContext = static::createApplicationContext(); + SystemEnvironmentBuilder::run($entryPointLevel); + SystemEnvironmentBuilder::initializeEnvironment($applicationContext); + GeneralUtility::presetApplicationContext($applicationContext); + } if (!Environment::isComposerMode() && ClassLoadingInformation::isClassLoadingInformationAvailable()) { ClassLoadingInformation::registerClassLoadingInformation(); } - return $this; + return static::$instance; } /** * Sets the class loader to the bootstrap * - * @param \Composer\Autoload\ClassLoader $classLoader an instance of the class loader + * @param ClassLoader $classLoader an instance of the class loader * @return Bootstrap * @internal This is not a public API method, do not use in own extensions */ - public function initializeClassLoader($classLoader) + public static function initializeClassLoader(ClassLoader $classLoader) { - $this->setEarlyInstance(\Composer\Autoload\ClassLoader::class, $classLoader); ClassLoadingInformation::setClassLoader($classLoader); /** @see initializeAnnotationRegistry */ @@ -202,20 +358,23 @@ public function initializeClassLoader($classLoader) AnnotationReader::addGlobalIgnoredName('extensionScannerIgnoreFile'); AnnotationReader::addGlobalIgnoredName('extensionScannerIgnoreLine'); - return $this; + return static::$instance; } /** * checks if LocalConfiguration.php or PackageStates.php is missing, * used to see if a redirect to the install tool is needed * + * @param ConfigurationManager $configurationManager * @return bool TRUE when the essential configuration is available, otherwise FALSE * @internal This is not a public API method, do not use in own extensions */ - public function checkIfEssentialConfigurationExists() + public static function checkIfEssentialConfigurationExists(ConfigurationManager $configurationManager = null): bool { - $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager; - $this->setEarlyInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class, $configurationManager); + if ($configurationManager === null) { // @todo deprecate + $configurationManager = new ConfigurationManager; + static::$instance->setEarlyInstance(ConfigurationManager::class, $configurationManager); + } return file_exists($configurationManager->getLocalConfigurationFileLocation()) && file_exists(PATH_typo3conf . 'PackageStates.php'); } @@ -227,6 +386,7 @@ public function checkIfEssentialConfigurationExists() * @param string $objectName Object name, as later used by the Object Manager * @param object $instance The instance to register * @internal This is not a public API method, do not use in own extensions + * @todo deprecate */ public function setEarlyInstance($objectName, $instance) { @@ -240,6 +400,7 @@ public function setEarlyInstance($objectName, $instance) * @return object * @throws \TYPO3\CMS\Core\Exception * @internal This is not a public API method, do not use in own extensions + * @todo deprecate */ public function getEarlyInstance($objectName) { @@ -254,6 +415,7 @@ public function getEarlyInstance($objectName) * * @return array * @internal This is not a public API method, do not use in own extensions + * @todo deprecate */ public function getEarlyInstances() { @@ -263,23 +425,31 @@ public function getEarlyInstances() /** * Includes LocalConfiguration.php and sets several * global settings depending on configuration. + * For functional and acceptance tests. * * @param bool $allowCaching Whether to allow caching - affects cache_core (autoloader) * @param string $packageManagerClassName Define an alternative package manager implementation (usually for the installer) - * @return Bootstrap + * @return Bootstrap|null * @internal This is not a public API method, do not use in own extensions */ - public function loadConfigurationAndInitialize($allowCaching = true, $packageManagerClassName = \TYPO3\CMS\Core\Package\PackageManager::class) + public static function loadConfigurationAndInitialize($allowCaching = true, $packageManagerClassName = \TYPO3\CMS\Core\Package\PackageManager::class) { - $this->populateLocalConfiguration() - ->initializeErrorHandling() - ->initializeCachingFramework($allowCaching) - ->initializePackageManagement($packageManagerClassName) - ->initializeRuntimeActivatedPackagesFromConfiguration() - ->setDefaultTimezone() - ->initializeL10nLocales() - ->setMemoryLimit(); - return $this; + $configurationManager = static::createConfigurationManager(); + static::populateLocalConfiguration($configurationManager); + static::initializeErrorHandling(); + + $cacheManager = static::createCacheManager(!$allowCaching); + $packageManager = static::createPackageManager($packageManagerClassName, $cacheManager->getCache('cache_core')); + + GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManager); + GeneralUtility::setSingletonInstance(PackageManager::class, $packageManager); + ExtensionManagementUtility::setPackageManager($packageManager); + + static::initializeRuntimeActivatedPackagesFromConfiguration($packageManager); + static::setDefaultTimezone(); + Locales::initialize(); + static::setMemoryLimit(); + return static::$instance; } /** @@ -287,38 +457,58 @@ public function loadConfigurationAndInitialize($allowCaching = true, $packageMan * provided by the packages. * * @param string $packageManagerClassName Define an alternative package manager implementation (usually for the installer) - * @return Bootstrap + * @param FrontendInterface $coreCache + * @return PackageManager * @internal This is not a public API method, do not use in own extensions */ - public function initializePackageManagement($packageManagerClassName) + public static function createPackageManager($packageManagerClassName, FrontendInterface $coreCache): PackageManager { $dependencyOrderingService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Service\DependencyOrderingService::class); /** @var \TYPO3\CMS\Core\Package\PackageManager $packageManager */ $packageManager = new $packageManagerClassName($dependencyOrderingService); - GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager); - $this->setEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager); - ExtensionManagementUtility::setPackageManager($packageManager); - $packageManager->injectCoreCache(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_core')); + $packageManager->injectCoreCache($coreCache); $packageManager->initialize(); + + return $packageManager; + } + + /** + * Initializes the package system and loads the package configuration and settings + * provided by the packages. + * + * @param string $packageManagerClassName Define an alternative package manager implementation (usually for the installer) + * @return Bootstrap + * @internal This is not a public API method, do not use in own extensions + * @todo deprecate + */ + public function initializePackageManagement($packageManagerClassName) + { + $packageManager = static::createPackageManager( + $packageManagerClassName, + GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_core') + ); + GeneralUtility::setSingletonInstance(PackageManager::class, $packageManager); + ExtensionManagementUtility::setPackageManager($packageManager); + $this->setEarlyInstance(PackageManager::class, $packageManager); + return $this; } /** * Activates a package during runtime. This is used in AdditionalConfiguration.php * to enable extensions under conditions. - * - * @return Bootstrap */ - protected function initializeRuntimeActivatedPackagesFromConfiguration() + protected static function initializeRuntimeActivatedPackagesFromConfiguration(PackageManager $packageManager = null) { + if ($packageManager === null) { // @todo deprecate + $packageManager = GeneralUtility::makeInstance(PackageManager::class); + } $packages = $GLOBALS['TYPO3_CONF_VARS']['EXT']['runtimeActivatedPackages'] ?? []; if (!empty($packages)) { - $packageManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Package\PackageManager::class); foreach ($packages as $runtimeAddedPackageKey) { $packageManager->activatePackageDuringRuntime($runtimeAddedPackageKey); } } - return $this; } /** @@ -338,19 +528,31 @@ public static function loadTypo3LoadedExtAndExtLocalconf($allowCaching = true) * We need an early instance of the configuration manager. * Since makeInstance relies on the object configuration, we create it here with new instead. * + * @return ConfigurationManager + */ + public static function createConfigurationManager(): ConfigurationManager + { + return new ConfigurationManager(); + } + + /** + * We need an early instance of the configuration manager. + * Since makeInstance relies on the object configuration, we create it here with new instead. + * + * @param ConfigurationManager $configurationManager * @return Bootstrap * @internal This is not a public API method, do not use in own extensions */ - public function populateLocalConfiguration() + public static function populateLocalConfiguration(ConfigurationManager $configurationManager = null) { - try { - $configurationManager = $this->getEarlyInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class); - } catch (\TYPO3\CMS\Core\Exception $exception) { - $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager(); - $this->setEarlyInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class, $configurationManager); + if ($configurationManager === null) { // @todo deprecate + $configurationManager = new ConfigurationManager(); + static::$instance->setEarlyInstance(ConfigurationManager::class, $configurationManager); } + $configurationManager->exportConfiguration(); - return $this; + + return static::$instance; } /** @@ -359,6 +561,7 @@ public function populateLocalConfiguration() * * @return Bootstrap|null * @internal This is not a public API method, do not use in own extensions + * @todo deprecate */ public static function disableCoreCache() { @@ -368,6 +571,21 @@ public static function disableCoreCache() return static::$instance; } + /** + * Initialize caching framework, and re-initializes it (e.g. in the install tool) by recreating the instances + * again despite the Singleton instance + * + * @param bool $disableCaching + * @return CacheManager + * @internal This is not a public API method, do not use in own extensions + */ + public static function createCacheManager(bool $disableCaching = false): CacheManager + { + $cacheManager = new CacheManager($disableCaching); + $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']); + return $cacheManager; + } + /** * Initialize caching framework, and re-initializes it (e.g. in the install tool) by recreating the instances * again despite the Singleton instance @@ -375,22 +593,20 @@ public static function disableCoreCache() * @param bool $allowCaching * @return Bootstrap * @internal This is not a public API method, do not use in own extensions + * @todo deprecate */ public function initializeCachingFramework(bool $allowCaching = true) { - $cacheManager = new \TYPO3\CMS\Core\Cache\CacheManager(!$allowCaching); - $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']); - GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Cache\CacheManager::class, $cacheManager); - $this->setEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class, $cacheManager); + $cacheManager = static::createCacheManager(!$allowCaching); + GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManager); + $this->setEarlyInstance(CacheManager::class, $cacheManager); return $this; } /** * Set default timezone - * - * @return Bootstrap */ - protected function setDefaultTimezone() + protected static function setDefaultTimezone() { $timeZone = $GLOBALS['TYPO3_CONF_VARS']['SYS']['phpTimeZone']; if (empty($timeZone)) { @@ -404,27 +620,14 @@ protected function setDefaultTimezone() } // Set default to avoid E_WARNINGs with PHP > 5.3 date_default_timezone_set($timeZone); - return $this; - } - - /** - * Initialize the locales handled by TYPO3 - * - * @return Bootstrap - */ - protected function initializeL10nLocales() - { - \TYPO3\CMS\Core\Localization\Locales::initialize(); - return $this; } /** * Configure and set up exception and error handling * - * @return Bootstrap * @throws \RuntimeException */ - protected function initializeErrorHandling() + protected static function initializeErrorHandling() { $productionExceptionHandlerClassName = $GLOBALS['TYPO3_CONF_VARS']['SYS']['productionExceptionHandler']; $debugExceptionHandlerClassName = $GLOBALS['TYPO3_CONF_VARS']['SYS']['debugExceptionHandler']; @@ -472,29 +675,34 @@ protected function initializeErrorHandling() // Registering the exception handler is done in the constructor GeneralUtility::makeInstance($exceptionHandlerClassName); } - return $this; } /** * Set PHP memory limit depending on value of * $GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit'] - * - * @return Bootstrap */ - protected function setMemoryLimit() + protected static function setMemoryLimit() { if ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit'] > 16) { @ini_set('memory_limit', (string)((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit'] . 'm')); } - return $this; } /** * Define TYPO3_REQUESTTYPE* constants that can be used for developers to see if any context has been hit * also see setRequestType(). Is done at the very beginning so these parameters are always available. + * + * Note: The definition of TYPO3_REQUEST_TYPE_* constants has been moved to the SystemEnvironmentBuilder + * and is included here for backwards compatibility reasons (as Bootstrap::getInstance() is expected + * to implicitly define these constants). + * + * @todo deprecate or remove once Bootstrap::getInstance is removed */ protected function defineTypo3RequestTypes() { + if (defined('TYPO3_REQUESTTYPE_FE')) { + return; + } define('TYPO3_REQUESTTYPE_FE', 1); define('TYPO3_REQUESTTYPE_BE', 2); define('TYPO3_REQUESTTYPE_CLI', 4); @@ -508,6 +716,7 @@ protected function defineTypo3RequestTypes() * @param int $requestType * @throws \RuntimeException if the method was already called during a request * @return Bootstrap + * @todo deprecate */ public function setRequestType($requestType) { @@ -522,13 +731,17 @@ public function setRequestType($requestType) * Extensions may register new caches, so we set the * global cache array to the manager again at this point * - * @return Bootstrap + * @param CacheManager $cacheManager + * @return Bootstrap|null * @internal This is not a public API method, do not use in own extensions */ - public function setFinalCachingFrameworkCacheConfiguration() + public static function setFinalCachingFrameworkCacheConfiguration(CacheManager $cacheManager = null) { - GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']); - return $this; + if ($cacheManager === null) { // @todo deprecate + $cacheManager = GeneralUtility::makeInstance(CacheManager::class); + } + $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']); + return static::$instance; } /** @@ -567,6 +780,19 @@ public static function loadBaseTca(bool $allowCaching = true) return static::$instance; } + /** + * Check if a configuration key has been configured + */ + protected static function checkEncryptionKey() + { + if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) { + throw new \RuntimeException( + 'TYPO3 Encryption is empty. $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'encryptionKey\'] needs to be set for TYPO3 to work securely', + 1502987245 + ); + } + } + /** * Load ext_tables and friends. * diff --git a/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php b/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php index 0f6413a802bc..406339073f33 100644 --- a/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php +++ b/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php @@ -38,6 +38,17 @@ */ class SystemEnvironmentBuilder { + /** @internal */ + const REQUESTTYPE_FE = 1; + /** @internal */ + const REQUESTTYPE_BE = 2; + /** @internal */ + const REQUESTTYPE_CLI = 4; + /** @internal */ + const REQUESTTYPE_AJAX = 8; + /** @internal */ + const REQUESTTYPE_INSTALL = 16; + /** * A list of supported CGI server APIs * NOTICE: This is a duplicate of the SAME array in GeneralUtility! @@ -66,10 +77,14 @@ class SystemEnvironmentBuilder * * @internal This method should not be used by 3rd party code. It will change without further notice. * @param int $entryPointLevel Number of subdirectories where the entry script is located under the document root + * @param int $requestType */ - public static function run($entryPointLevel = 0) + public static function run(int $entryPointLevel = 0, int $requestType = self::REQUESTTYPE_FE) { self::defineBaseConstants(); + self::defineTypo3RequestTypes(); + self::setRequestType($requestType | ($requestType === self::REQUESTTYPE_BE && strpos($_REQUEST['route'] ?? '', '/ajax/') === 0 ? TYPO3_REQUESTTYPE_AJAX : 0)); + self::defineLegacyConstants($requestType === self::REQUESTTYPE_FE ? 'FE' : 'BE'); self::definePaths($entryPointLevel); self::checkMainPathsExist(); self::initializeGlobalVariables(); @@ -441,4 +456,46 @@ protected static function usesComposerClassLoading(): bool { return defined('TYPO3_COMPOSER_MODE') && TYPO3_COMPOSER_MODE; } + + /** + * Define TYPO3_REQUESTTYPE* constants that can be used for developers to see if any context has been hit + * also see setRequestType(). Is done at the very beginning so these parameters are always available. + */ + protected static function defineTypo3RequestTypes() + { + if (defined('TYPO3_REQUESTTYPE_FE')) { // @todo remove once Bootstrap::getInstance() is dropped + return; + } + define('TYPO3_REQUESTTYPE_FE', self::REQUESTTYPE_FE); + define('TYPO3_REQUESTTYPE_BE', self::REQUESTTYPE_BE); + define('TYPO3_REQUESTTYPE_CLI', self::REQUESTTYPE_CLI); + define('TYPO3_REQUESTTYPE_AJAX', self::REQUESTTYPE_AJAX); + define('TYPO3_REQUESTTYPE_INSTALL', self::REQUESTTYPE_INSTALL); + } + + /** + * Defines the TYPO3_REQUESTTYPE constant so the environment knows which context the request is running. + * + * @param int $requestType + */ + protected static function setRequestType(int $requestType) + { + if (defined('TYPO3_REQUESTTYPE')) { // @todo remove once Bootstrap::getInstance() is dropped + return; + } + define('TYPO3_REQUESTTYPE', $requestType); + } + + /** + * Define constants and variables + * + * @param string + */ + protected static function defineLegacyConstants(string $mode) + { + if (defined('TYPO3_MODE')) { // @todo remove once Bootstrap::getInstance() + return; + } + define('TYPO3_MODE', $mode); + } } diff --git a/typo3/sysext/core/Classes/Localization/Locales.php b/typo3/sysext/core/Classes/Localization/Locales.php index 9c5c3b073437..70b7eea65e38 100644 --- a/typo3/sysext/core/Classes/Localization/Locales.php +++ b/typo3/sysext/core/Classes/Localization/Locales.php @@ -131,8 +131,9 @@ class Locales implements \TYPO3\CMS\Core\SingletonInterface /** * Initializes the languages. + * @return Locales */ - public static function initialize() + public static function initialize(): Locales { /** @var $instance Locales */ $instance = GeneralUtility::makeInstance(self::class); @@ -152,6 +153,7 @@ public static function initialize() } // Merge user-provided locale dependencies ArrayUtility::mergeRecursiveWithOverrule($instance->localeDependencies, $GLOBALS['TYPO3_CONF_VARS']['SYS']['localization']['locales']['dependencies'] ?? []); + return $instance; } /** diff --git a/typo3/sysext/core/bin/typo3 b/typo3/sysext/core/bin/typo3 index 9b321402691e..f0d290406fe5 100755 --- a/typo3/sysext/core/bin/typo3 +++ b/typo3/sysext/core/bin/typo3 @@ -19,5 +19,6 @@ */ call_user_func(function() { $classLoader = require __DIR__ . '/../../../../vendor/autoload.php'; - (new \TYPO3\CMS\Core\Console\CommandApplication($classLoader))->run(); + \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(); }); diff --git a/typo3/sysext/core/composer.json b/typo3/sysext/core/composer.json index ff3829cb2696..f8baf8e1c023 100644 --- a/typo3/sysext/core/composer.json +++ b/typo3/sysext/core/composer.json @@ -24,11 +24,12 @@ "symfony/polyfill-mbstring": "^1.2", "doctrine/instantiator": "~1.0.4", "doctrine/annotations": "^1.3", - "typo3/cms-cli": "^1.0", + "typo3/cms-cli": "^2.0", "typo3/class-alias-loader": "^1.0", "typo3/cms-composer-installers": "^2.0", "psr/http-message": "~1.0", "psr/http-server-middleware": "^1.0", + "psr/container": "^1.0", "cogpowered/finediff": "~0.3.1", "mso/idna-convert": "^1.1.0", "typo3fluid/fluid": "^2.4", @@ -38,7 +39,7 @@ "symfony/polyfill-intl-icu": "^1.6" }, "require-dev": { - "typo3/testing-framework": "^3.0", + "typo3/testing-framework": "^3.2", "codeception/codeception": "^2.3", "enm1989/chromedriver": "~2.30", "typo3/cms-styleguide": "~9.0.1", diff --git a/typo3/sysext/frontend/Classes/Http/Application.php b/typo3/sysext/frontend/Classes/Http/Application.php index 2684977821d1..a1c7d045a6d7 100644 --- a/typo3/sysext/frontend/Classes/Http/Application.php +++ b/typo3/sysext/frontend/Classes/Http/Application.php @@ -16,7 +16,8 @@ */ use Psr\Http\Message\ResponseInterface; -use TYPO3\CMS\Core\Core\Bootstrap; +use Psr\Http\Message\ServerRequestInterface; +use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\Http\AbstractApplication; use TYPO3\CMS\Core\Http\RedirectResponse; @@ -36,46 +37,38 @@ class Application extends AbstractApplication protected $middlewareStack = 'frontend'; /** - * @var Bootstrap + * @var ConfigurationManager */ - protected $bootstrap; + protected $configurationManager; /** - * Number of subdirectories where the entry script is located, relative to PATH_site - * Usually this is equal to PATH_site = 0 - * @var int + * @param ConfigurationManager $configurationManager */ - protected $entryPointLevel = 0; + public function __construct(ConfigurationManager $configurationManager) + { + $this->configurationManager = $configurationManager; + } /** - * Constructor setting up legacy constant and register available Request Handlers - * - * @param \Composer\Autoload\ClassLoader $classLoader an instance of the class loader + * @param ServerRequestInterface $request + * @return ResponseInterface */ - public function __construct($classLoader) + protected function handle(ServerRequestInterface $request): ResponseInterface { - $this->defineLegacyConstants(); - - $this->bootstrap = Bootstrap::getInstance() - ->initializeClassLoader($classLoader) - ->setRequestType(TYPO3_REQUESTTYPE_FE) - ->baseSetup($this->entryPointLevel); - - // Redirect to install tool if base configuration is not found - if (!$this->bootstrap->checkIfEssentialConfigurationExists()) { - $this->sendResponse($this->installToolRedirect()); - exit; + if (!$this->checkIfEssentialConfigurationExists()) { + return $this->installToolRedirect(); } - - $this->bootstrap->configure(); + return parent::handle($request); } /** - * Define constants and variables + * Check if LocalConfiguration.php and PackageStates.php exist + * + * @return bool TRUE when the essential configuration is available, otherwise FALSE */ - protected function defineLegacyConstants() + protected function checkIfEssentialConfigurationExists(): bool { - define('TYPO3_MODE', 'FE'); + return file_exists($this->configurationManager->getLocalConfigurationFileLocation()) && file_exists(PATH_typo3conf . 'PackageStates.php'); } /** diff --git a/typo3/sysext/frontend/Resources/Private/Php/frontend.php b/typo3/sysext/frontend/Resources/Private/Php/frontend.php index 0719d2bd14c9..7fddec21d072 100644 --- a/typo3/sysext/frontend/Resources/Private/Php/frontend.php +++ b/typo3/sysext/frontend/Resources/Private/Php/frontend.php @@ -17,8 +17,9 @@ die('This version of TYPO3 CMS requires PHP 7.2 or above'); } -// Set up the application for the Frontend +// Set up the application for the frontend call_user_func(function () { $classLoader = require __DIR__ . '/../../../../../../vendor/autoload.php'; - (new \TYPO3\CMS\Frontend\Http\Application($classLoader))->run(); + \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::run(0, \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_FE); + \TYPO3\CMS\Core\Core\Bootstrap::init($classLoader)->get(\TYPO3\CMS\Frontend\Http\Application::class)->run(); }); diff --git a/typo3/sysext/install/Classes/Http/Application.php b/typo3/sysext/install/Classes/Http/Application.php index 0b6181409580..c1503c976fcc 100644 --- a/typo3/sysext/install/Classes/Http/Application.php +++ b/typo3/sysext/install/Classes/Http/Application.php @@ -16,52 +16,34 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use TYPO3\CMS\Core\Core\Bootstrap; use TYPO3\CMS\Core\Http\AbstractApplication; -use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Http\RequestHandlerInterface; /** * Entry point for the TYPO3 Install Tool */ class Application extends AbstractApplication { - /** - * @var Bootstrap - */ - protected $bootstrap; - - /** - * Number of subdirectories where the entry script is located, relative to PATH_site - * @var int - */ - protected $entryPointLevel = 1; - /** * All available request handlers that can handle an install tool request * @var array */ - protected $availableRequestHandlers = [ - \TYPO3\CMS\Install\Http\RequestHandler::class, - \TYPO3\CMS\Install\Http\InstallerRequestHandler::class - ]; + protected $availableRequestHandlers = []; /** - * Constructor setting up legacy constant and register available Request Handlers + * Construct Application * - * @param \Composer\Autoload\ClassLoader $classLoader an instance of the class loader + * @param RequestHandlerInterface $requestHandler + * @param RequestHandlerInterface $installerRequestHandler */ - public function __construct($classLoader) - { - $this->defineLegacyConstants(); - - $this->bootstrap = Bootstrap::getInstance() - ->initializeClassLoader($classLoader) - ->setRequestType(TYPO3_REQUESTTYPE_INSTALL) - ->baseSetup($this->entryPointLevel); - - $this->bootstrap - ->startOutputBuffering() - ->loadConfigurationAndInitialize(false, \TYPO3\CMS\Core\Package\FailsafePackageManager::class); + public function __construct( + RequestHandlerInterface $requestHandler, + RequestHandlerInterface $installerRequestHandler + ) { + $this->availableRequestHandlers = [ + $requestHandler, + $installerRequestHandler + ]; } /** @@ -70,8 +52,7 @@ public function __construct($classLoader) */ protected function handle(ServerRequestInterface $request): ResponseInterface { - foreach ($this->availableRequestHandlers as $requestHandler) { - $handler = GeneralUtility::makeInstance($requestHandler, $this->bootstrap); + foreach ($this->availableRequestHandlers as $handler) { if ($handler->canHandleRequest($request)) { return $handler->handleRequest($request); } @@ -79,12 +60,4 @@ protected function handle(ServerRequestInterface $request): ResponseInterface throw new \TYPO3\CMS\Core\Exception('No suitable request handler found.', 1518448686); } - - /** - * Define constants - */ - protected function defineLegacyConstants() - { - define('TYPO3_MODE', 'BE'); - } } diff --git a/typo3/sysext/install/Classes/Http/RequestHandler.php b/typo3/sysext/install/Classes/Http/RequestHandler.php index 1c7342af2e16..cfe85682b104 100644 --- a/typo3/sysext/install/Classes/Http/RequestHandler.php +++ b/typo3/sysext/install/Classes/Http/RequestHandler.php @@ -17,7 +17,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use TYPO3\CMS\Core\Core\Bootstrap; +use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\FormProtection\FormProtectionFactory; use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection; use TYPO3\CMS\Core\Http\HtmlResponse; @@ -44,10 +44,9 @@ class RequestHandler implements RequestHandlerInterface { /** - * Instance of the current TYPO3 bootstrap - * @var Bootstrap + * @var ConfigurationManager */ - protected $bootstrap; + protected $configurationManager; /** * @var array List of valid controllers @@ -62,13 +61,11 @@ class RequestHandler implements RequestHandlerInterface ]; /** - * Constructor handing over the bootstrap - * - * @param Bootstrap $bootstrap + * @param ConfigurationManager $configurationManager */ - public function __construct(Bootstrap $bootstrap) + public function __construct(ConfigurationManager $configurationManager) { - $this->bootstrap = $bootstrap; + $this->configurationManager = $configurationManager; } /** @@ -201,7 +198,7 @@ public function handleRequest(ServerRequestInterface $request): ResponseInterfac */ public function canHandleRequest(ServerRequestInterface $request): bool { - $basicIntegrity = $this->bootstrap->checkIfEssentialConfigurationExists() + $basicIntegrity = $this->checkIfEssentialConfigurationExists() && !empty($GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword']) && !EnableFileService::isFirstInstallAllowed(); if (!$basicIntegrity) { @@ -299,4 +296,14 @@ protected function checkSessionLifetime(SessionService $session): bool } return !$isExpired; } + + /** + * Check if LocalConfiguration.php and PackageStates.php exist + * + * @return bool TRUE when the essential configuration is available, otherwise FALSE + */ + protected function checkIfEssentialConfigurationExists(): bool + { + return file_exists($this->configurationManager->getLocalConfigurationFileLocation()) && file_exists(PATH_typo3conf . 'PackageStates.php'); + } } diff --git a/typo3/sysext/install/Resources/Private/Php/install.php b/typo3/sysext/install/Resources/Private/Php/install.php index 3267741ac11b..7ce212a20a76 100644 --- a/typo3/sysext/install/Resources/Private/Php/install.php +++ b/typo3/sysext/install/Resources/Private/Php/install.php @@ -100,5 +100,6 @@ call_user_func(function () { $classLoader = require __DIR__ . '/../../../../../../vendor/autoload.php'; - (new \TYPO3\CMS\Install\Http\Application($classLoader))->run(); + \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::run(1, \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_INSTALL); + \TYPO3\CMS\Core\Core\Bootstrap::init($classLoader, true)->get(\TYPO3\CMS\Install\Http\Application::class)->run(); });