[TASK] Decouple Bootstrap and Application engaging a PSR-11 container 73/55773/51
authorBenjamin Franzke <bfr@qbus.de>
Thu, 8 Feb 2018 20:21:05 +0000 (21:21 +0100)
committerBenni Mack <benni@typo3.org>
Thu, 8 Mar 2018 21:01:18 +0000 (22:01 +0100)
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 <no-reply@typo3.com>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
15 files changed:
composer.json
composer.lock
typo3/sysext/backend/Classes/Http/Application.php
typo3/sysext/backend/Resources/Private/Php/backend.php
typo3/sysext/core/Classes/Console/CommandApplication.php
typo3/sysext/core/Classes/Core/Bootstrap.php
typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php
typo3/sysext/core/Classes/Localization/Locales.php
typo3/sysext/core/bin/typo3
typo3/sysext/core/composer.json
typo3/sysext/frontend/Classes/Http/Application.php
typo3/sysext/frontend/Resources/Private/Php/frontend.php
typo3/sysext/install/Classes/Http/Application.php
typo3/sysext/install/Classes/Http/RequestHandler.php
typo3/sysext/install/Resources/Private/Php/install.php

index 58efd20..c0deb34 100644 (file)
@@ -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",
index 795a96b..b80845c 100644 (file)
@@ -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",
             "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",
             "source": {
         },
         {
             "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",
         },
         {
             "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",
                 "tests",
                 "typo3"
             ],
-            "time": "2018-02-27T15:30:06+00:00"
+            "time": "2018-03-08T20:33:54+00:00"
         },
         {
             "name": "webmozart/assert",
index 5e4c744..ab79a68 100644 (file)
@@ -16,7 +16,8 @@ namespace TYPO3\CMS\Backend\Http;
  */
 
 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 @@ class Application extends AbstractApplication
      */
     protected function installToolRedirect(): ResponseInterface
     {
-        $path = '../' . TYPO3_mainDir . 'install.php';
-        return new RedirectResponse($path, 302);
+        return new RedirectResponse('./install.php', 302);
     }
 }
index 83abb22..352dbc9 100644 (file)
@@ -20,6 +20,6 @@ if (version_compare(PHP_VERSION, '7.2.0', '<')) {
 // 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();
 });
index 93d6a2c..6e0d6c5 100644 (file)
@@ -15,7 +15,6 @@ namespace TYPO3\CMS\Core\Console;
  */
 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 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 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();
     }
 
     /**
@@ -69,14 +47,6 @@ class CommandApplication implements ApplicationInterface
     }
 
     /**
-     * Define constants and variables
-     */
-    protected function defineLegacyConstants()
-    {
-        define('TYPO3_MODE', 'BE');
-    }
-
-    /**
      * Check the script is called from a cli environment.
      */
     protected function checkEnvironmentOrDie()
index 837fe36..b09cd7f 100644 (file)
@@ -14,9 +14,18 @@ namespace TYPO3\CMS\Core\Core;
  * 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;
 
@@ -39,34 +48,170 @@ 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
      */
     protected $earlyInstances = [];
 
     /**
      * 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,19 +234,31 @@ class Bootstrap
      *
      * @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 @@ class Bootstrap
      * 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 @@ class Bootstrap
             ->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 @@ class Bootstrap
      * @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 @@ class Bootstrap
         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 @@ class Bootstrap
      * @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 @@ class Bootstrap
      * @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 @@ class Bootstrap
      *
      * @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 @@ class Bootstrap
     /**
      * 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 @@ class Bootstrap
      * 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 @@ class Bootstrap
      * 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 @@ class Bootstrap
      *
      * @return Bootstrap|null
      * @internal This is not a public API method, do not use in own extensions
+     * @todo deprecate
      */
     public static function disableCoreCache()
     {
@@ -372,25 +575,38 @@ class Bootstrap
      * 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
+     *
      * @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 @@ class Bootstrap
         }
         // 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 @@ class Bootstrap
             // 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 @@ class Bootstrap
      * @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 @@ class Bootstrap
      * 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;
     }
 
     /**
@@ -568,6 +781,19 @@ class Bootstrap
     }
 
     /**
+     * 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.
      *
      * This will mainly load and execute ext_tables.php files of loaded extensions
index 0f6413a..4063390 100644 (file)
@@ -38,6 +38,17 @@ use TYPO3\CMS\Core\Utility\StringUtility;
  */
 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 @@ class SystemEnvironmentBuilder
     {
         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);
+    }
 }
index 9c5c3b0..70b7eea 100644 (file)
@@ -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 @@ class Locales implements \TYPO3\CMS\Core\SingletonInterface
         }
         // Merge user-provided locale dependencies
         ArrayUtility::mergeRecursiveWithOverrule($instance->localeDependencies, $GLOBALS['TYPO3_CONF_VARS']['SYS']['localization']['locales']['dependencies'] ?? []);
+        return $instance;
     }
 
     /**
index 9b32140..f0d2904 100755 (executable)
@@ -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();
 });
index ff3829c..f8baf8e 100644 (file)
                "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",
index 2684977..a1c7d04 100644 (file)
@@ -16,7 +16,8 @@ namespace TYPO3\CMS\Frontend\Http;
  */
 
 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');
     }
 
     /**
index 0719d2b..7fddec2 100644 (file)
@@ -17,8 +17,9 @@ if (version_compare(PHP_VERSION, '7.2.0', '<')) {
     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();
 });
index 0b61814..c1503c9 100644 (file)
@@ -16,9 +16,8 @@ namespace TYPO3\CMS\Install\Http;
 
 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
@@ -26,42 +25,25 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 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 @@ class Application extends AbstractApplication
      */
     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 @@ class Application extends AbstractApplication
 
         throw new \TYPO3\CMS\Core\Exception('No suitable request handler found.', 1518448686);
     }
-
-    /**
-     * Define constants
-     */
-    protected function defineLegacyConstants()
-    {
-        define('TYPO3_MODE', 'BE');
-    }
 }
index 1c7342a..cfe8568 100644 (file)
@@ -17,7 +17,7 @@ namespace TYPO3\CMS\Install\Http;
 
 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 @@ use TYPO3\CMS\Saltedpasswords\Salt\SaltFactory;
 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 @@ class RequestHandler implements RequestHandlerInterface
      */
     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 @@ class RequestHandler implements RequestHandlerInterface
         }
         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');
+    }
 }
index 3267741..7ce212a 100644 (file)
@@ -100,5 +100,6 @@ if (version_compare(PHP_VERSION, '7.2.0', '<')) {
 
 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();
 });