From c020e9baac8ffc81362179e3ca6258c19ab6f566 Mon Sep 17 00:00:00 2001
From: Benjamin Franzke <bfr@qbus.de>
Date: Sat, 9 Feb 2019 17:44:02 +0100
Subject: [PATCH] [TASK] Inject singletons used by EXT:install in service
 providers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This will allow both, dependency injection for these services (manually
wired in service providers), and usage in install/maintenance tool
(where we do not use the caching symfony container for basic tasks).

The move to DI is possible thanks to the failsafe container and the
service providers which can fed the failsafe container with service
factories. These factories are used to wire services manually.

Notes:

 * The definition in a service provider means we are required to use
   manual wiring and are forced to define the dependencies in the service
   provider when we add new depenencies to services that are being used
   by the install..
   With that approach we can assure that we do not accidentally add new
   dependencies to services which would become available in symfony DI due
   to autowiring, but would be unavailable in the install tool.

 * The install tool has operations that require a booted symfony
   container which is provided by the LateBootService. Services that
   are used in that mode do not need to be listed in service providers.
   Therefore we do only add core services to service providers if they
   are used in a context without a fully booted symfony DI container.

 * GLOBALS['LANG'] mocks in Core\Tests\Unit\DataHandling\DataHandlerTest
   have been removed as the code under test does no longer use
   $GLOBALS['LANG']->csConvObj->substr() but mb_substr. The test had been
   introduced with #68602 but the tested code was adapted in #78670.

 * We got a bit ugly constructs now, where a unit(!) test previously used

     $GLOBALS['LANG'] = new LanguageService();

   …we now have to encode the dependency structure:

     $GLOBALS['LANG'] = new LanguageService(new Locales,
         new LocalizationFactory(
             new LanguageStore,
             $cacheManagerProphecy->reveal()
         )
     );

   This isn't nice, but this change reveals that the affected unit tests
   should either be adapted, removed or be moved to a functional test.
   Such adaption are out of scope for this change.

 * loadExtLocalconfDatabaseAndExtTables() is removed from the
   EXT:install AbstractController, as it hides the implicit dependency
   to LateBootService

 * Nullable constructor arguments have been changed to be non-nullable
   whenever possible. That results in some more test adaptions, but
   reveals, where unit tests rely on implicit dependencies and offers
   better readability and less possible codepaths.

Releases: master
Resolves: #89892
Resolves: #89891
Change-Id: Ib72d6440f81b2c0d05279e8768697c3b48aecfe4
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/62575
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Susanne Moog <look@susi.dev>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Susanne Moog <look@susi.dev>
---
 .../core/Classes/Imaging/IconFactory.php      |   6 +-
 .../Classes/Localization/LanguageService.php  |  30 +--
 .../Localization/LanguageServiceFactory.php   |  53 ++++++
 .../Localization/LocalizationFactory.php      |  17 +-
 typo3/sysext/core/Classes/ServiceProvider.php | 174 ++++++++++++++++--
 typo3/sysext/core/Configuration/Services.yaml |  12 +-
 .../Database/QueryGeneratorTest.php           |   2 +-
 .../Unit/DataHandling/DataHandlerTest.php     |   2 -
 .../Error/ProductionExceptionHandlerTest.php  |  10 +
 .../core/Tests/Unit/Imaging/IconTest.php      |   3 +-
 .../Localization/LocalizationFactoryTest.php  |  19 +-
 .../Localization/Parser/XliffParserTest.php   |  26 ++-
 .../Utility/File/ExtendedFileUtilityTest.php  |   1 +
 .../Localization/LocalizationFactoryTest.php  |   6 +-
 .../Parser/LocallangXmlParserTest.php         |  14 +-
 .../extbase/Classes/ServiceProvider.php       |  11 ++
 .../ActionControllerValidationTest.php        |   4 +-
 .../Unit/Utility/LocalizationUtilityTest.php  |  25 ++-
 .../Unit/Utility/FileHandlingUtilityTest.php  |   2 +-
 .../Unit/Service/TranslationServiceTest.php   |   4 +-
 .../Menu/AbstractMenuContentObjectTest.php    |  17 ++
 .../Unit/Controller/ErrorControllerTest.php   |  14 ++
 .../TypoScriptFrontendControllerTest.php      |  32 ++++
 .../Tests/Unit/Http/RequestHandlerTest.php    |  15 ++
 .../Middleware/PageArgumentValidatorTest.php  |  12 ++
 .../TypoScriptFrontendControllerTest.php      |   5 +
 .../Command/UpgradeWizardListCommand.php      |  18 +-
 .../Command/UpgradeWizardRunCommand.php       |  18 +-
 .../Classes/Controller/AbstractController.php |  22 ---
 .../Classes/Controller/IconController.php     |  10 +-
 .../Controller/InstallerController.php        | 115 +++++++-----
 .../Classes/Controller/LayoutController.php   |  14 +-
 .../Controller/MaintenanceController.php      | 100 +++++++---
 .../Classes/Controller/SettingsController.php |  34 +++-
 .../Classes/Controller/UpgradeController.php  |  76 ++++----
 .../install/Classes/Middleware/Installer.php  |  14 +-
 .../Classes/Middleware/Maintenance.php        |   8 +-
 .../Classes/Service/CoreUpdateService.php     |   9 +-
 .../Classes/Service/CoreVersionService.php    |  14 --
 .../Service/ExtensionConfigurationService.php |  18 +-
 .../Classes/Service/LanguagePackService.php   |   6 +-
 .../Classes/Service/LateBootService.php       |  11 ++
 .../SilentConfigurationUpgradeService.php     |   6 +-
 .../install/Classes/ServiceProvider.php       | 158 +++++++++++++++-
 .../Unit/Controller/UpgradeControllerTest.php |  11 +-
 45 files changed, 871 insertions(+), 307 deletions(-)
 create mode 100644 typo3/sysext/core/Classes/Localization/LanguageServiceFactory.php

diff --git a/typo3/sysext/core/Classes/Imaging/IconFactory.php b/typo3/sysext/core/Classes/Imaging/IconFactory.php
index 4f1912561796..7644a16430a7 100644
--- a/typo3/sysext/core/Classes/Imaging/IconFactory.php
+++ b/typo3/sysext/core/Classes/Imaging/IconFactory.php
@@ -68,10 +68,10 @@ class IconFactory
      * @param EventDispatcherInterface $eventDispatcher
      * @param IconRegistry $iconRegistry
      */
-    public function __construct(EventDispatcherInterface $eventDispatcher = null, IconRegistry $iconRegistry = null)
+    public function __construct(EventDispatcherInterface $eventDispatcher, IconRegistry $iconRegistry)
     {
-        $this->eventDispatcher = $eventDispatcher ?? GeneralUtility::getContainer()->get(EventDispatcherInterface::class);
-        $this->iconRegistry = $iconRegistry ?? GeneralUtility::makeInstance(IconRegistry::class);
+        $this->eventDispatcher = $eventDispatcher;
+        $this->iconRegistry = $iconRegistry;
         $this->recordStatusMapping = $GLOBALS['TYPO3_CONF_VARS']['SYS']['IconFactory']['recordStatusMapping'];
         $this->overlayPriorities = $GLOBALS['TYPO3_CONF_VARS']['SYS']['IconFactory']['overlayPriorities'];
     }
diff --git a/typo3/sysext/core/Classes/Localization/LanguageService.php b/typo3/sysext/core/Classes/Localization/LanguageService.php
index 76d0baa8e60b..d419bc2690ac 100644
--- a/typo3/sysext/core/Classes/Localization/LanguageService.php
+++ b/typo3/sysext/core/Classes/Localization/LanguageService.php
@@ -105,10 +105,22 @@ class LanguageService
     protected $labels = [];
 
     /**
-     * LanguageService constructor.
+     * @var Locales
      */
-    public function __construct()
+    protected $locales;
+
+    /**
+     * @var LocalizationFactory
+     */
+    protected $localizationFactory;
+
+    /**
+     * @internal use one of the factory methods instead
+     */
+    public function __construct(Locales $locales, LocalizationFactory $localizationFactory)
     {
+        $this->locales = $locales;
+        $this->localizationFactory = $localizationFactory;
         $this->debugKey = (bool)$GLOBALS['TYPO3_CONF_VARS']['BE']['languageDebug'];
     }
 
@@ -124,13 +136,12 @@ class LanguageService
     public function init($languageKey)
     {
         // Find the requested language in this list based on the $languageKey
-        $locales = GeneralUtility::makeInstance(Locales::class);
         // Language is found. Configure it:
-        if (in_array($languageKey, $locales->getLocales(), true)) {
+        if (in_array($languageKey, $this->locales->getLocales(), true)) {
             // The current language key
             $this->lang = $languageKey;
             $this->languageDependencies[] = $languageKey;
-            foreach ($locales->getLocaleDependencies($languageKey) as $language) {
+            foreach ($this->locales->getLocaleDependencies($languageKey) as $language) {
                 $this->languageDependencies[] = $language;
             }
         }
@@ -371,9 +382,6 @@ class LanguageService
             return $this->languageFileCache[$fileRef . $this->lang];
         }
 
-        /** @var LocalizationFactory $languageFactory */
-        $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
-
         if ($this->lang !== 'default') {
             $languages = array_reverse($this->languageDependencies);
         } else {
@@ -381,7 +389,7 @@ class LanguageService
         }
         $localLanguage = [];
         foreach ($languages as $language) {
-            $tempLL = $languageFactory->getParsedData($fileRef, $language);
+            $tempLL = $this->localizationFactory->getParsedData($fileRef, $language);
             $localLanguage['default'] = $tempLL['default'];
             if (!isset($localLanguage[$this->lang])) {
                 $localLanguage[$this->lang] = $localLanguage['default'];
@@ -435,9 +443,7 @@ class LanguageService
      */
     public static function create(string $locale): self
     {
-        $obj = GeneralUtility::makeInstance(LanguageService::class);
-        $obj->init($locale);
-        return $obj;
+        return GeneralUtility::makeInstance(LanguageServiceFactory::class)->create($locale);
     }
 
     public static function createFromUserPreferences(?AbstractUserAuthentication $user): self
diff --git a/typo3/sysext/core/Classes/Localization/LanguageServiceFactory.php b/typo3/sysext/core/Classes/Localization/LanguageServiceFactory.php
new file mode 100644
index 000000000000..3768d42bf592
--- /dev/null
+++ b/typo3/sysext/core/Classes/Localization/LanguageServiceFactory.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Localization;
+
+/**
+ * @internal
+ */
+class LanguageServiceFactory
+{
+    /**
+     * @var Locales
+     */
+    protected $locales;
+
+    /**
+     * @var LocalizationFactory
+     */
+    protected $localizationFactory;
+
+    public function __construct(Locales $locales, LocalizationFactory $localizationFactory)
+    {
+        $this->locales = $locales;
+        $this->localizationFactory = $localizationFactory;
+    }
+
+    /**
+     * Factory method to create a language service object.
+     *
+     * @param string $locale the locale (= the TYPO3-internal locale given)
+     * @return LanguageService
+     */
+    public function create(string $locale): LanguageService
+    {
+        $obj = new LanguageService($this->locales, $this->localizationFactory);
+        $obj->init($locale);
+        return $obj;
+    }
+}
diff --git a/typo3/sysext/core/Classes/Localization/LocalizationFactory.php b/typo3/sysext/core/Classes/Localization/LocalizationFactory.php
index ea30ff67f653..0e1694c0a39b 100644
--- a/typo3/sysext/core/Classes/Localization/LocalizationFactory.php
+++ b/typo3/sysext/core/Classes/Localization/LocalizationFactory.php
@@ -36,21 +36,10 @@ class LocalizationFactory implements SingletonInterface
      */
     public $store;
 
-    /**
-     * Class constructor
-     */
-    public function __construct()
-    {
-        $this->store = GeneralUtility::makeInstance(LanguageStore::class);
-        $this->initializeCache();
-    }
-
-    /**
-     * Initialize cache instance to be ready to use
-     */
-    protected function initializeCache()
+    public function __construct(LanguageStore $languageStore, CacheManager $cacheManager)
     {
-        $this->cacheInstance = GeneralUtility::makeInstance(CacheManager::class)->getCache('l10n');
+        $this->store = $languageStore;
+        $this->cacheInstance = $cacheManager->getCache('l10n');
     }
 
     /**
diff --git a/typo3/sysext/core/Classes/ServiceProvider.php b/typo3/sysext/core/Classes/ServiceProvider.php
index d5b534ef3c2b..b38da10e6d0c 100644
--- a/typo3/sysext/core/Classes/ServiceProvider.php
+++ b/typo3/sysext/core/Classes/ServiceProvider.php
@@ -20,6 +20,7 @@ namespace TYPO3\CMS\Core;
 use ArrayObject;
 use Psr\Container\ContainerInterface;
 use Psr\EventDispatcher\EventDispatcherInterface;
+use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Package\AbstractServiceProvider;
 
 /**
@@ -36,15 +37,37 @@ class ServiceProvider extends AbstractServiceProvider
     {
         return [
             Cache\CacheManager::class => [ static::class, 'getCacheManager' ],
+            Charset\CharsetConverter::class => [ static::class, 'getCharsetConverter' ],
+            Configuration\SiteConfiguration::class => [ static::class, 'getSiteConfiguration' ],
             Console\CommandApplication::class => [ static::class, 'getConsoleCommandApplication' ],
             Console\CommandRegistry::class => [ static::class, 'getConsoleCommandRegistry' ],
             Context\Context::class => [ static::class, 'getContext' ],
+            Crypto\PasswordHashing\PasswordHashFactory::class => [ static::class, 'getPasswordHashFactory' ],
             EventDispatcher\EventDispatcher::class => [ static::class, 'getEventDispatcher' ],
             EventDispatcher\ListenerProvider::class => [ static::class, 'getEventListenerProvider' ],
             Http\MiddlewareStackResolver::class => [ static::class, 'getMiddlewareStackResolver' ],
-            Service\DependencyOrderingService::class => [ static::class, 'getDependencyOrderingService' ],
-            Crypto\PasswordHashing\PasswordHashFactory::class => [ static::class, 'getPasswordHashFactory' ],
+            Http\RequestFactory::class => [ static::class, 'getRequestFactory' ],
+            Imaging\IconFactory::class => [ static::class, 'getIconFactory' ],
+            Imaging\IconRegistry::class => [ static::class, 'getIconRegistry' ],
+            Localization\LanguageServiceFactory::class => [ static::class, 'getLanguageServiceFactory' ],
+            Localization\LanguageStore::class => [ static::class, 'getLanguageStore' ],
+            Localization\Locales::class => [ static::class, 'getLocales' ],
+            Localization\LocalizationFactory::class => [ static::class, 'getLocalizationFactory' ],
+            Mail\TransportFactory::class => [ static::class, 'getMailTransportFactory' ],
+            Messaging\FlashMessageService::class => [ static::class, 'getFlashMessageService' ],
+            Package\FailsafePackageManager::class => [ static::class, 'getFailsafePackageManager' ],
+            Registry::class => [ static::class, 'getRegistry' ],
+            Resource\Index\FileIndexRepository::class => [ static::class, 'getFileIndexRepository' ],
+            Resource\Driver\DriverRegistry::class => [ static::class, 'getDriverRegistry' ],
+            Resource\ProcessedFileRepository::class => [ static::class, 'getProcessedFileRepository' ],
             Resource\ResourceFactory::class => [ static::class, 'getResourceFactory' ],
+            Resource\StorageRepository::class => [ static::class, 'getStorageRepository' ],
+            Service\DependencyOrderingService::class => [ static::class, 'getDependencyOrderingService' ],
+            Service\FlexFormService::class => [ static::class, 'getFlexFormService' ],
+            Service\OpcodeCacheService::class => [ static::class, 'getOpcodeCacheService' ],
+            TimeTracker\TimeTracker::class => [ static::class, 'getTimeTracker' ],
+            TypoScript\Parser\ConstantConfigurationParser::class => [ static::class, 'getTypoScriptConstantConfigurationParser' ],
+            TypoScript\TypoScriptService::class => [ static::class, 'getTypoScriptService' ],
             'middlewares' => [ static::class, 'getMiddlewares' ],
         ];
     }
@@ -81,6 +104,16 @@ class ServiceProvider extends AbstractServiceProvider
         return $cacheManager;
     }
 
+    public static function getCharsetConverter(ContainerInterface $container): Charset\CharsetConverter
+    {
+        return self::new($container, Charset\CharsetConverter::class);
+    }
+
+    public static function getSiteConfiguration(ContainerInterface $container): Configuration\SiteConfiguration
+    {
+        return new Configuration\SiteConfiguration(Environment::getConfigPath() . '/sites');
+    }
+
     public static function getConsoleCommandApplication(ContainerInterface $container): Console\CommandApplication
     {
         return new Console\CommandApplication(
@@ -118,11 +151,6 @@ class ServiceProvider extends AbstractServiceProvider
         return $listenerProvider;
     }
 
-    public static function getDependencyOrderingService(ContainerInterface $container): Service\DependencyOrderingService
-    {
-        return new Service\DependencyOrderingService();
-    }
-
     public static function getContext(ContainerInterface $container): Context\Context
     {
         return new Context\Context();
@@ -133,13 +161,84 @@ class ServiceProvider extends AbstractServiceProvider
         return new Crypto\PasswordHashing\PasswordHashFactory();
     }
 
-    public static function getMiddlewareStackResolver(ContainerInterface $container): Http\MiddlewareStackResolver
+    public static function getIconFactory(ContainerInterface $container): Imaging\IconFactory
     {
-        return new Http\MiddlewareStackResolver(
-            $container,
-            $container->get(Service\DependencyOrderingService::class),
-            $container->get('cache.core')
-        );
+        return self::new($container, Imaging\IconFactory::class, [
+            $container->get(EventDispatcherInterface::class),
+            $container->get(Imaging\IconRegistry::class)
+        ]);
+    }
+
+    public static function getIconRegistry(ContainerInterface $container): Imaging\IconRegistry
+    {
+        return self::new($container, Imaging\IconRegistry::class);
+    }
+
+    public static function getLanguageServiceFactory(ContainerInterface $container): Localization\LanguageServiceFactory
+    {
+        return self::new($container, Localization\LanguageServiceFactory::class, [
+            $container->get(Localization\Locales::class),
+            $container->get(Localization\LocalizationFactory::class)
+        ]);
+    }
+
+    public static function getLanguageStore(ContainerInterface $container): Localization\LanguageStore
+    {
+        return self::new($container, Localization\LanguageStore::class);
+    }
+
+    public static function getLocales(ContainerInterface $container): Localization\Locales
+    {
+        return self::new($container, Localization\Locales::class);
+    }
+
+    public static function getLocalizationFactory(ContainerInterface $container): Localization\LocalizationFactory
+    {
+        return self::new($container, Localization\LocalizationFactory::class, [
+            $container->get(Localization\LanguageStore::class),
+            $container->get(Cache\CacheManager::class)
+        ]);
+    }
+
+    public static function getMailTransportFactory(ContainerInterface $container): Mail\TransportFactory
+    {
+        return self::new($container, Mail\TransportFactory::class);
+    }
+
+    public static function getFlashMessageService(ContainerInterface $container): Messaging\FlashMessageService
+    {
+        return self::new($container, Messaging\FlashMessageService::class);
+    }
+
+    public static function getFailsafePackageManager(ContainerInterface $container): Package\FailsafePackageManager
+    {
+        $packageManager = $container->get(Package\PackageManager::class);
+        if ($packageManager instanceof Package\FailsafePackageManager) {
+            return $packageManager;
+        }
+        throw new \RuntimeException('FailsafePackageManager can only be instantiated in failsafe (maintenance tool) mode.', 1586861816);
+    }
+
+    public static function getRegistry(ContainerInterface $container): Registry
+    {
+        return self::new($container, Registry::class);
+    }
+
+    public static function getFileIndexRepository(ContainerInterface $container): Resource\Index\FileIndexRepository
+    {
+        return self::new($container, Resource\Index\FileIndexRepository::class, [
+            $container->get(EventDispatcherInterface::class)
+        ]);
+    }
+
+    public static function getDriverRegistry(ContainerInterface $container): Resource\Driver\DriverRegistry
+    {
+        return self::new($container, Resource\Driver\DriverRegistry::class);
+    }
+
+    public static function getProcessedFileRepository(ContainerInterface $container): Resource\ProcessedFileRepository
+    {
+        return self::new($container, Resource\ProcessedFileRepository::class);
     }
 
     public static function getResourceFactory(ContainerInterface $container): Resource\ResourceFactory
@@ -149,6 +248,55 @@ class ServiceProvider extends AbstractServiceProvider
         ]);
     }
 
+    public static function getStorageRepository(ContainerInterface $container): Resource\StorageRepository
+    {
+        return self::new($container, Resource\StorageRepository::class);
+    }
+
+    public static function getDependencyOrderingService(ContainerInterface $container): Service\DependencyOrderingService
+    {
+        return new Service\DependencyOrderingService();
+    }
+
+    public static function getFlexFormService(ContainerInterface $container): Service\FlexFormService
+    {
+        return self::new($container, Service\FlexFormService::class);
+    }
+
+    public static function getOpcodeCacheService(ContainerInterface $container): Service\OpcodeCacheService
+    {
+        return self::new($container, Service\OpcodeCacheService::class);
+    }
+
+    public static function getTimeTracker(ContainerInterface $container): TimeTracker\TimeTracker
+    {
+        return self::new($container, TimeTracker\TimeTracker::class);
+    }
+
+    public static function getTypoScriptConstantConfigurationParser(ContainerInterface $container): TypoScript\Parser\ConstantConfigurationParser
+    {
+        return self::new($container, TypoScript\Parser\ConstantConfigurationParser::class);
+    }
+
+    public static function getTypoScriptService(ContainerInterface $container): TypoScript\TypoScriptService
+    {
+        return self::new($container, TypoScript\TypoScriptService::class);
+    }
+
+    public static function getRequestFactory(ContainerInterface $container): Http\RequestFactory
+    {
+        return new Http\RequestFactory();
+    }
+
+    public static function getMiddlewareStackResolver(ContainerInterface $container): Http\MiddlewareStackResolver
+    {
+        return new Http\MiddlewareStackResolver(
+            $container,
+            $container->get(Service\DependencyOrderingService::class),
+            $container->get('cache.core')
+        );
+    }
+
     public static function getMiddlewares(ContainerInterface $container): ArrayObject
     {
         return new ArrayObject();
diff --git a/typo3/sysext/core/Configuration/Services.yaml b/typo3/sysext/core/Configuration/Services.yaml
index 2b461c5e4667..4e3000f43087 100644
--- a/typo3/sysext/core/Configuration/Services.yaml
+++ b/typo3/sysext/core/Configuration/Services.yaml
@@ -56,9 +56,6 @@ services:
     arguments:
       $configPath: "%env(TYPO3:configPath)%/sites"
 
-  TYPO3\CMS\Core\Package\FailsafePackageManager:
-    autoconfigure: false
-
   TYPO3\CMS\Core\Package\UnitTestPackageManager:
     autoconfigure: false
 
@@ -321,6 +318,15 @@ services:
         method: 'addCategoryDatabaseSchema'
         event: TYPO3\CMS\Core\Database\Event\AlterTableDefinitionStatementsEvent
 
+  # @internal
+  # @todo: deprecate makeInstance(LanguageService::class)
+  # This service entry is provided for legacy code that instantiates LanguageService
+  # using GeneralUtility::makeInstance instead of the factory methods which itself
+  # use LanguageServiceFactory (for install tool compatibility).
+  TYPO3\CMS\Core\Localization\LanguageService:
+    shared: false
+    public: true
+
   TYPO3\CMS\Core\Page\AssetRenderer:
     public: true
     arguments:
diff --git a/typo3/sysext/core/Tests/Functional/Database/QueryGeneratorTest.php b/typo3/sysext/core/Tests/Functional/Database/QueryGeneratorTest.php
index 43e71dfe416e..28338a688bb5 100644
--- a/typo3/sysext/core/Tests/Functional/Database/QueryGeneratorTest.php
+++ b/typo3/sysext/core/Tests/Functional/Database/QueryGeneratorTest.php
@@ -32,7 +32,7 @@ class QueryGeneratorTest extends FunctionalTestCase
     {
         parent::setUp();
         $this->setUpBackendUserFromFixture(1);
-        $GLOBALS['LANG'] = new LanguageService();
+        $GLOBALS['LANG'] = LanguageService::create('default');
     }
 
     /**
diff --git a/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php b/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php
index 2991190906ff..26232f5204f1 100644
--- a/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php
+++ b/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php
@@ -943,8 +943,6 @@ class DataHandlerTest extends UnitTestCase
      */
     public function checkValueForInputConvertsNullToEmptyString()
     {
-        $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageService::class);
-        $GLOBALS['LANG']->init('default');
         $expectedResult = ['value' => ''];
         self::assertSame($expectedResult, $this->subject->_call('checkValueForInput', null, ['type' => 'string', 'max' => 40], 'tt_content', 'NEW55c0e67f8f4d32.04974534', 89, 'table_caption'));
     }
diff --git a/typo3/sysext/core/Tests/Unit/Error/ProductionExceptionHandlerTest.php b/typo3/sysext/core/Tests/Unit/Error/ProductionExceptionHandlerTest.php
index 7e4d8a302fd7..e5242562c015 100644
--- a/typo3/sysext/core/Tests/Unit/Error/ProductionExceptionHandlerTest.php
+++ b/typo3/sysext/core/Tests/Unit/Error/ProductionExceptionHandlerTest.php
@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Core\Tests\Unit\Error;
 use Prophecy\Argument;
 use Psr\Log\LoggerInterface;
 use TYPO3\CMS\Core\Error\ProductionExceptionHandler;
+use TYPO3\CMS\Core\Information\Typo3Information;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
@@ -51,6 +52,9 @@ class ProductionExceptionHandlerTest extends UnitTestCase
      */
     public function echoExceptionWebEscapesExceptionMessage()
     {
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
         $message = '<b>b</b><script>alert(1);</script>';
         $exception = new \Exception($message, 1476049364);
         ob_start();
@@ -66,6 +70,9 @@ class ProductionExceptionHandlerTest extends UnitTestCase
      */
     public function echoExceptionWebEscapesExceptionTitle()
     {
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
         $title = '<b>b</b><script>alert(1);</script>';
         /** @var $exception \Exception|\PHPUnit\Framework\MockObject\MockObject */
         $exception = $this->getMockBuilder('Exception')
@@ -120,6 +127,9 @@ class ProductionExceptionHandlerTest extends UnitTestCase
      */
     public function logEntriesContainAnonymousTokens(string $originalUrl, string $expectedUrl)
     {
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
         $subject = new ProductionExceptionHandler();
         $logger = $this->prophesize(LoggerInterface::class);
         $logger->critical(Argument::containingString($expectedUrl), Argument::cetera())->shouldBeCalled();
diff --git a/typo3/sysext/core/Tests/Unit/Imaging/IconTest.php b/typo3/sysext/core/Tests/Unit/Imaging/IconTest.php
index 03b94a2f17f3..21ddcc9630e4 100644
--- a/typo3/sysext/core/Tests/Unit/Imaging/IconTest.php
+++ b/typo3/sysext/core/Tests/Unit/Imaging/IconTest.php
@@ -21,6 +21,7 @@ use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
+use TYPO3\CMS\Core\Imaging\IconRegistry;
 use TYPO3\CMS\Core\Type\Icon\IconState;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
@@ -58,7 +59,7 @@ class IconTest extends UnitTestCase
         $cacheFrontendProphecy->get(Argument::cetera())->willReturn(false);
         $cacheFrontendProphecy->set(Argument::cetera())->willReturn(null);
         $eventDispatcherProphecy = $this->prophesize(EventDispatcherInterface::class);
-        $iconFactory = new IconFactory($eventDispatcherProphecy->reveal());
+        $iconFactory = new IconFactory($eventDispatcherProphecy->reveal(), new IconRegistry());
         $this->subject = $iconFactory->getIcon($this->iconIdentifier, Icon::SIZE_SMALL, $this->overlayIdentifier, IconState::cast(IconState::STATE_DISABLED));
     }
 
diff --git a/typo3/sysext/core/Tests/Unit/Localization/LocalizationFactoryTest.php b/typo3/sysext/core/Tests/Unit/Localization/LocalizationFactoryTest.php
index 784e8c351a11..a87b0aae1faf 100644
--- a/typo3/sysext/core/Tests/Unit/Localization/LocalizationFactoryTest.php
+++ b/typo3/sysext/core/Tests/Unit/Localization/LocalizationFactoryTest.php
@@ -20,7 +20,6 @@ use TYPO3\CMS\Core\Cache\Frontend\VariableFrontend;
 use TYPO3\CMS\Core\Localization\Exception\FileNotFoundException;
 use TYPO3\CMS\Core\Localization\LanguageStore;
 use TYPO3\CMS\Core\Localization\LocalizationFactory;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
 /**
@@ -28,21 +27,11 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
  */
 class LocalizationFactoryTest extends UnitTestCase
 {
-    public function tearDown(): void
-    {
-        // Drop created singletons again
-        GeneralUtility::purgeInstances();
-        parent::tearDown();
-    }
-
     /**
      * @test
      */
     public function getParsedDataCallsLocalizationOverrideIfFileNotFoundExceptionIsThrown()
     {
-        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
-        GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
-
         $languageStore = $this->getMockBuilder(LanguageStore::class)
             ->onlyMethods(['hasData', 'setConfiguration', 'getData', 'setData'])
             ->getMock();
@@ -50,14 +39,16 @@ class LocalizationFactoryTest extends UnitTestCase
             ->onlyMethods(['get', 'set'])
             ->disableOriginalConstructor()
             ->getMock();
-        $cacheManagerProphecy->getCache('l10n')->willReturn($cacheInstance);
 
-        $localizationFactory = new LocalizationFactory();
-        $localizationFactory->store = $languageStore;
         $languageStore->method('hasData')->willReturn(false);
         $languageStore->method('getData')->willReturn(['default' => []]);
         $languageStore->method('setConfiguration')->willThrowException(new FileNotFoundException('testing', 1476049512));
         $cacheInstance->method('get')->willReturn(false);
+        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
+        $cacheManagerProphecy->getCache('l10n')->willReturn($cacheInstance);
+
+        /** @var $localizationFactory LocalizationFactory */
+        $localizationFactory = $this->getAccessibleMock(LocalizationFactory::class, ['localizationOverride'], [$languageStore, $cacheManagerProphecy->reveal()]);
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'] = ['foo' => 'bar'];
 
         $localizationFactory->getParsedData('EXT:backend/Resources/Private/Language/locallang_layout.xlf', 'default');
diff --git a/typo3/sysext/core/Tests/Unit/Localization/Parser/XliffParserTest.php b/typo3/sysext/core/Tests/Unit/Localization/Parser/XliffParserTest.php
index 6f99e629244e..cc1af8e2b1e3 100644
--- a/typo3/sysext/core/Tests/Unit/Localization/Parser/XliffParserTest.php
+++ b/typo3/sysext/core/Tests/Unit/Localization/Parser/XliffParserTest.php
@@ -19,9 +19,9 @@ use Prophecy\Argument;
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Core\Environment;
+use TYPO3\CMS\Core\Localization\LanguageStore;
 use TYPO3\CMS\Core\Localization\LocalizationFactory;
 use TYPO3\CMS\Core\Localization\Parser\XliffParser;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
 /**
@@ -34,6 +34,11 @@ class XliffParserTest extends UnitTestCase
      */
     protected $xliffFileNames;
 
+    /**
+     * @var ObjectProphecy|CacheManager
+     */
+    protected $cacheManagerProphecy;
+
     /**
      * Prepares the environment before running a test.
      */
@@ -49,24 +54,15 @@ class XliffParserTest extends UnitTestCase
         ];
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['format']['priority'] = 'xlf';
 
-        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
-        GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
+        $this->languageStoreProphecy = $this->prophesize(LanguageStore::class);
+        $this->cacheManagerProphecy = $this->prophesize(CacheManager::class);
         $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
-        $cacheManagerProphecy->getCache('l10n')->willReturn($cacheFrontendProphecy->reveal());
+        $this->cacheManagerProphecy->getCache('l10n')->willReturn($cacheFrontendProphecy->reveal());
         $cacheFrontendProphecy->get(Argument::cetera())->willReturn(false);
         $cacheFrontendProphecy->set(Argument::cetera())->willReturn(null);
         $cacheFrontendProphecy->flush()->willReturn(null);
     }
 
-    /**
-     * Cleans up the environment after running a test.
-     */
-    protected function tearDown(): void
-    {
-        GeneralUtility::purgeInstances();
-        parent::tearDown();
-    }
-
     /**
      * @test
      */
@@ -107,7 +103,7 @@ class XliffParserTest extends UnitTestCase
     public function canOverrideXliff()
     {
         /** @var $factory LocalizationFactory */
-        $factory = new LocalizationFactory();
+        $factory = new LocalizationFactory(new LanguageStore(), $this->cacheManagerProphecy->reveal());
 
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$this->xliffFileNames['locallang']][] = $this->xliffFileNames['locallang_override'];
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']['fr'][$this->xliffFileNames['locallang']][] = $this->xliffFileNames['locallang_override_fr'];
@@ -144,7 +140,7 @@ class XliffParserTest extends UnitTestCase
      */
     public function canOverrideXliffWithFrenchOnly()
     {
-        $factory = new LocalizationFactory();
+        $factory = new LocalizationFactory(new LanguageStore(), $this->cacheManagerProphecy->reveal());
 
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']['fr'][$this->xliffFileNames['locallang']][] = $this->xliffFileNames['locallang_override_fr'];
         $LOCAL_LANG = $factory->getParsedData($this->xliffFileNames['locallang'], 'fr');
diff --git a/typo3/sysext/core/Tests/Unit/Utility/File/ExtendedFileUtilityTest.php b/typo3/sysext/core/Tests/Unit/Utility/File/ExtendedFileUtilityTest.php
index 8e11f31461f5..5acd37dc4656 100644
--- a/typo3/sysext/core/Tests/Unit/Utility/File/ExtendedFileUtilityTest.php
+++ b/typo3/sysext/core/Tests/Unit/Utility/File/ExtendedFileUtilityTest.php
@@ -40,6 +40,7 @@ class ExtendedFileUtilityTest extends UnitTestCase
     {
         parent::setUp();
         $GLOBALS['LANG'] = $this->getMockBuilder(LanguageService::class)
+            ->disableOriginalConstructor()
             ->setMethods(['sL'])
             ->getMock();
     }
diff --git a/typo3/sysext/core/Tests/UnitDeprecated/Localization/LocalizationFactoryTest.php b/typo3/sysext/core/Tests/UnitDeprecated/Localization/LocalizationFactoryTest.php
index bd467937ba4c..3f857ddc8258 100644
--- a/typo3/sysext/core/Tests/UnitDeprecated/Localization/LocalizationFactoryTest.php
+++ b/typo3/sysext/core/Tests/UnitDeprecated/Localization/LocalizationFactoryTest.php
@@ -45,13 +45,13 @@ class LocalizationFactoryTest extends UnitTestCase
     public function getParsedDataHandlesLocallangXMLOverride()
     {
         $cacheManagerProphecy = $this->prophesize(CacheManager::class);
-        GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
         $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
         $cacheManagerProphecy->getCache('l10n')->willReturn($cacheFrontendProphecy->reveal());
         $cacheFrontendProphecy->get(Argument::cetera())->willReturn(false);
         $cacheFrontendProphecy->set(Argument::cetera())->willReturn(null);
 
-        $subject = new LocalizationFactory();
+        $store = new LanguageStore();
+        $subject = new LocalizationFactory($store, $cacheManagerProphecy->reveal());
 
         $unique = 'locallangXMLOverrideTest' . substr(StringUtility::getUniqueId(), 0, 10);
         $xml = '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
@@ -72,8 +72,6 @@ class LocalizationFactoryTest extends UnitTestCase
         // Set override file
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']['EXT:core/Resources/Private/Language/locallang_core.xlf'][$unique] = $file;
 
-        /** @var $store LanguageStore */
-        $store = GeneralUtility::makeInstance(LanguageStore::class);
         $store->flushData('EXT:core/Resources/Private/Language/locallang_core.xlf');
 
         // Get override value
diff --git a/typo3/sysext/core/Tests/UnitDeprecated/Localization/Parser/LocallangXmlParserTest.php b/typo3/sysext/core/Tests/UnitDeprecated/Localization/Parser/LocallangXmlParserTest.php
index ba188a7007fc..81e27bc71a86 100644
--- a/typo3/sysext/core/Tests/UnitDeprecated/Localization/Parser/LocallangXmlParserTest.php
+++ b/typo3/sysext/core/Tests/UnitDeprecated/Localization/Parser/LocallangXmlParserTest.php
@@ -32,6 +32,11 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
  */
 class LocallangXmlParserTest extends UnitTestCase
 {
+    /**
+     * @var ObjectProphecy|CacheManager
+     */
+    protected $cacheManagerProphecy;
+
     /**
      * Prepares the environment before running a test.
      */
@@ -41,10 +46,9 @@ class LocallangXmlParserTest extends UnitTestCase
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['format']['priority'] = 'xml';
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['parser']['xml'] = \TYPO3\CMS\Core\Localization\Parser\LocallangXmlParser::class;
 
-        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
-        GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
+        $this->cacheManagerProphecy = $this->prophesize(CacheManager::class);
         $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
-        $cacheManagerProphecy->getCache('l10n')->willReturn($cacheFrontendProphecy->reveal());
+        $this->cacheManagerProphecy->getCache('l10n')->willReturn($cacheFrontendProphecy->reveal());
         $cacheFrontendProphecy->get(Argument::cetera())->willReturn(false);
         $cacheFrontendProphecy->set(Argument::cetera())->willReturn(null);
 
@@ -122,7 +126,7 @@ class LocallangXmlParserTest extends UnitTestCase
     public function canOverrideLlxml()
     {
         /** @var $factory LocalizationFactory */
-        $factory = new LocalizationFactory();
+        $factory = new LocalizationFactory(new LanguageStore(), $this->cacheManagerProphecy->reveal());
 
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][self::getFixtureFilePath('locallang.xml')][] = self::getFixtureFilePath('locallang_override.xml');
         $LOCAL_LANG = array_merge(
@@ -183,7 +187,7 @@ class LocallangXmlParserTest extends UnitTestCase
     public function canTranslateNumericKeys($key, $expectedResult)
     {
         /** @var $factory LocalizationFactory */
-        $factory = new LocalizationFactory();
+        $factory = new LocalizationFactory(new LanguageStore(), $this->cacheManagerProphecy->reveal());
 
         $LOCAL_LANG = $factory->getParsedData(self::getFixtureFilePath('locallangNumericKeys.xml'), 'fr');
 
diff --git a/typo3/sysext/extbase/Classes/ServiceProvider.php b/typo3/sysext/extbase/Classes/ServiceProvider.php
index 41f5cb1b9ab8..28ec596e4cb0 100644
--- a/typo3/sysext/extbase/Classes/ServiceProvider.php
+++ b/typo3/sysext/extbase/Classes/ServiceProvider.php
@@ -21,6 +21,7 @@ use Psr\Container\ContainerInterface;
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Log\LogManager;
 use TYPO3\CMS\Core\Package\AbstractServiceProvider;
+use TYPO3\CMS\Core\TypoScript\TypoScriptService;
 
 /**
  * @internal
@@ -38,6 +39,7 @@ class ServiceProvider extends AbstractServiceProvider
             Object\Container\Container::class => [ static::class, 'getObjectContainer' ],
             Object\ObjectManager::class => [ static::class, 'getObjectManager' ],
             SignalSlot\Dispatcher::class => [ static::class, 'getSignalSlotDispatcher' ],
+            Configuration\BackendConfigurationManager::class => [ static::class, 'getBackendConfigurationManager' ],
             Configuration\ConfigurationManager::class => [ static::class, 'getConfigurationManager' ],
             Reflection\ReflectionService::class => [ static::class, 'getReflectionService' ],
             Service\EnvironmentService::class => [ static::class, 'getEnvironmentService' ],
@@ -62,6 +64,15 @@ class ServiceProvider extends AbstractServiceProvider
         return self::new($container, SignalSlot\Dispatcher::class, [$container->get(Object\ObjectManager::class), $logger]);
     }
 
+    public static function getBackendConfigurationManager(ContainerInterface $container): Configuration\BackendConfigurationManager
+    {
+        return self::new($container, Configuration\BackendConfigurationManager::class, [
+            $container->get(Object\ObjectManager::class),
+            $container->get(TypoScriptService::class),
+            $container->get(Service\EnvironmentService::class),
+        ]);
+    }
+
     public static function getConfigurationManager(ContainerInterface $container): Configuration\ConfigurationManager
     {
         return self::new($container, Configuration\ConfigurationManager::class, [
diff --git a/typo3/sysext/extbase/Tests/Functional/Mvc/Validation/ActionControllerValidationTest.php b/typo3/sysext/extbase/Tests/Functional/Mvc/Validation/ActionControllerValidationTest.php
index daf8bead47de..5b2a6b466878 100644
--- a/typo3/sysext/extbase/Tests/Functional/Mvc/Validation/ActionControllerValidationTest.php
+++ b/typo3/sysext/extbase/Tests/Functional/Mvc/Validation/ActionControllerValidationTest.php
@@ -67,7 +67,7 @@ class ActionControllerValidationTest extends FunctionalTestCase
      */
     public function forwardedActionValidatesPreviouslyIgnoredArgument(array $blogPostArgument, array $trustedProperties, array $expectedErrorCodes)
     {
-        $GLOBALS['LANG'] = new LanguageService();
+        $GLOBALS['LANG'] = GeneralUtility::getContainer()->get(LanguageService::class);
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = 'testkey';
 
         $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/pages.xml');
@@ -115,7 +115,7 @@ class ActionControllerValidationTest extends FunctionalTestCase
      */
     public function validationResultsAreProvidedForTheSameObjectInDifferentArguments()
     {
-        $GLOBALS['LANG'] = new LanguageService();
+        $GLOBALS['LANG'] = GeneralUtility::getContainer()->get(LanguageService::class);
         $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = 'testkey';
 
         $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/pages.xml');
diff --git a/typo3/sysext/extbase/Tests/Unit/Utility/LocalizationUtilityTest.php b/typo3/sysext/extbase/Tests/Unit/Utility/LocalizationUtilityTest.php
index 0edd48732075..aa9e263c8b05 100644
--- a/typo3/sysext/extbase/Tests/Unit/Utility/LocalizationUtilityTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Utility/LocalizationUtilityTest.php
@@ -17,7 +17,11 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Utility;
 
 use Prophecy\Argument;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Cache\CacheManager;
+use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Localization\LanguageService;
+use TYPO3\CMS\Core\Localization\LanguageStore;
+use TYPO3\CMS\Core\Localization\Locales;
 use TYPO3\CMS\Core\Localization\LocalizationFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
@@ -343,7 +347,12 @@ class LocalizationUtilityTest extends UnitTestCase
         $property = $reflectionClass->getProperty('LOCAL_LANG');
         $property->setAccessible(true);
         $property->setValue($this->LOCAL_LANG);
-        $GLOBALS['LANG'] = new LanguageService();
+        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
+        $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
+        $cacheManagerProphecy->getCache('l10n')->willReturn($cacheFrontendProphecy->reveal());
+        $cacheFrontendProphecy->get(Argument::cetera())->willReturn(false);
+        $cacheFrontendProphecy->set(Argument::cetera())->willReturn(null);
+        $GLOBALS['LANG'] = new LanguageService(new Locales(), new LocalizationFactory(new LanguageStore(), $cacheManagerProphecy->reveal()));
         self::assertEquals($expected, LocalizationUtility::translate($key, 'core', $arguments, $languageKey, $altLanguageKeys));
     }
 
@@ -503,7 +512,12 @@ class LocalizationUtilityTest extends UnitTestCase
         $method->setAccessible(true);
         $method->invoke(null, 'core', $this->languageFilePath);
 
-        $GLOBALS['LANG'] = new LanguageService();
+        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
+        $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
+        $cacheManagerProphecy->getCache('l10n')->willReturn($cacheFrontendProphecy->reveal());
+        $cacheFrontendProphecy->get(Argument::cetera())->willReturn(false);
+        $cacheFrontendProphecy->set(Argument::cetera())->willReturn(null);
+        $GLOBALS['LANG'] = new LanguageService(new Locales(), new LocalizationFactory(new LanguageStore(), $cacheManagerProphecy->reveal()));
 
         $result = LocalizationUtility::translate('key1', 'core', null, 'dk');
         self::assertNotNull($result);
@@ -545,7 +559,12 @@ class LocalizationUtilityTest extends UnitTestCase
         $method->setAccessible(true);
         $method->invoke(null, 'core', ''); // setting the language file path to an empty string here
 
-        $GLOBALS['LANG'] = new LanguageService();
+        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
+        $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
+        $cacheManagerProphecy->getCache('l10n')->willReturn($cacheFrontendProphecy->reveal());
+        $cacheFrontendProphecy->get(Argument::cetera())->willReturn(false);
+        $cacheFrontendProphecy->set(Argument::cetera())->willReturn(null);
+        $GLOBALS['LANG'] = new LanguageService(new Locales(), new LocalizationFactory(new LanguageStore(), $cacheManagerProphecy->reveal()));
 
         $result = LocalizationUtility::translate('key1', 'core', null, 'dk');
         self::assertNotNull($result);
diff --git a/typo3/sysext/extensionmanager/Tests/Unit/Utility/FileHandlingUtilityTest.php b/typo3/sysext/extensionmanager/Tests/Unit/Utility/FileHandlingUtilityTest.php
index 76e09085226b..fd9af2fb7e77 100644
--- a/typo3/sysext/extensionmanager/Tests/Unit/Utility/FileHandlingUtilityTest.php
+++ b/typo3/sysext/extensionmanager/Tests/Unit/Utility/FileHandlingUtilityTest.php
@@ -149,7 +149,7 @@ class FileHandlingUtilityTest extends UnitTestCase
         $this->expectException(ExtensionManagerException::class);
         $this->expectExceptionCode(1337280417);
         $fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['removeDirectory', 'addDirectory']);
-        $languageServiceMock = $this->getMockBuilder(LanguageService::class)->getMock();
+        $languageServiceMock = $this->getMockBuilder(LanguageService::class)->disableOriginalConstructor()->getMock();
         $fileHandlerMock->_set('languageService', $languageServiceMock);
         $fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath');
     }
diff --git a/typo3/sysext/form/Tests/Unit/Service/TranslationServiceTest.php b/typo3/sysext/form/Tests/Unit/Service/TranslationServiceTest.php
index a0883a59b09a..7c820152570d 100644
--- a/typo3/sysext/form/Tests/Unit/Service/TranslationServiceTest.php
+++ b/typo3/sysext/form/Tests/Unit/Service/TranslationServiceTest.php
@@ -22,6 +22,7 @@ use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Localization\LanguageStore;
+use TYPO3\CMS\Core\Localization\Locales;
 use TYPO3\CMS\Core\Localization\LocalizationFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
@@ -102,10 +103,11 @@ class TranslationServiceTest extends UnitTestCase
             'getLanguageService'
         ], [], '', false);
 
+        $languageService = new LanguageService(new Locales(), new LocalizationFactory(new LanguageStore(), $cacheManagerProphecy->reveal()));
         $this->mockTranslationService
             ->expects(self::any())
             ->method('getLanguageService')
-            ->willReturn(GeneralUtility::makeInstance(LanguageService::class));
+            ->willReturn($languageService);
 
         $this->mockTranslationService
             ->expects(self::any())
diff --git a/typo3/sysext/frontend/Tests/Unit/ContentObject/Menu/AbstractMenuContentObjectTest.php b/typo3/sysext/frontend/Tests/Unit/ContentObject/Menu/AbstractMenuContentObjectTest.php
index c3f9218f5005..e5020f6b1661 100644
--- a/typo3/sysext/frontend/Tests/Unit/ContentObject/Menu/AbstractMenuContentObjectTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/ContentObject/Menu/AbstractMenuContentObjectTest.php
@@ -19,6 +19,8 @@ namespace TYPO3\CMS\Frontend\Tests\Unit\ContentObject\Menu;
 
 use Doctrine\DBAL\Driver\Statement;
 use Prophecy\Argument;
+use TYPO3\CMS\Core\Cache\CacheManager;
+use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Cache\Frontend\VariableFrontend;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Context\LanguageAspect;
@@ -27,6 +29,11 @@ use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
 use TYPO3\CMS\Core\Domain\Repository\PageRepository;
 use TYPO3\CMS\Core\Http\ServerRequest;
+use TYPO3\CMS\Core\Localization\LanguageService;
+use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
+use TYPO3\CMS\Core\Localization\LanguageStore;
+use TYPO3\CMS\Core\Localization\Locales;
+use TYPO3\CMS\Core\Localization\LocalizationFactory;
 use TYPO3\CMS\Core\Routing\PageArguments;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -64,6 +71,16 @@ class AbstractMenuContentObjectTest extends UnitTestCase
                 ]
             ]
         ]);
+        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
+        GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
+        $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
+        $cacheManagerProphecy->getCache('l10n')->willReturn($cacheFrontendProphecy->reveal());
+        $cacheFrontendProphecy->get(Argument::cetera())->willReturn(false);
+        $cacheFrontendProphecy->set(Argument::cetera())->willReturn(null);
+        $languageService = new LanguageService(new Locales(), new LocalizationFactory(new LanguageStore(), $cacheManagerProphecy->reveal()));
+        $languageServiceFactoryProphecy = $this->prophesize(LanguageServiceFactory::class);
+        $languageServiceFactoryProphecy->create(Argument::any())->willReturn($languageService);
+        GeneralUtility::addInstance(LanguageServiceFactory::class, $languageServiceFactoryProphecy->reveal());
         $GLOBALS['TSFE'] = $this->getMockBuilder(TypoScriptFrontendController::class)
             ->setConstructorArgs([new Context(), $site, $site->getDefaultLanguage(), new PageArguments(1, '1', [])])
             ->setMethods(['initCaches'])
diff --git a/typo3/sysext/frontend/Tests/Unit/Controller/ErrorControllerTest.php b/typo3/sysext/frontend/Tests/Unit/Controller/ErrorControllerTest.php
index c4dadc9d6cc1..871d8aa10ffe 100644
--- a/typo3/sysext/frontend/Tests/Unit/Controller/ErrorControllerTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/Controller/ErrorControllerTest.php
@@ -18,6 +18,8 @@ declare(strict_types=1);
 namespace TYPO3\CMS\Frontend\Tests\Unit\Controller;
 
 use TYPO3\CMS\Core\Http\ServerRequest;
+use TYPO3\CMS\Core\Information\Typo3Information;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\Controller\ErrorController;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
@@ -33,6 +35,9 @@ class ErrorControllerTest extends UnitTestCase
      */
     public function pageNotFoundHandlingThrowsExceptionIfNotConfigured()
     {
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
         $GLOBALS['TYPO3_REQUEST'] = [];
         $subject = new ErrorController();
         $response = $subject->pageNotFoundAction(new ServerRequest(), 'This test page was not found!');
@@ -72,6 +77,9 @@ class ErrorControllerTest extends UnitTestCase
      */
     public function defaultErrorHandlerWithHtmlResponseIsChosenWhenNoSiteConfiguredForPageNotFoundAction()
     {
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
         $subject = new ErrorController();
         $response = $subject->pageNotFoundAction(new ServerRequest(), 'Error handler is not configured.');
         self::assertSame(404, $response->getStatusCode());
@@ -97,6 +105,9 @@ class ErrorControllerTest extends UnitTestCase
      */
     public function defaultErrorHandlerWithHtmlResponseIsChosenWhenNoSiteConfiguredForUnavailableAction()
     {
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
         $subject = new ErrorController();
         $response = $subject->unavailableAction(new ServerRequest(), 'Error handler is not configured.');
         self::assertSame(500, $response->getStatusCode());
@@ -122,6 +133,9 @@ class ErrorControllerTest extends UnitTestCase
      */
     public function defaultErrorHandlerWithHtmlResponseIsChosenWhenNoSiteConfiguredForAccessDeniedAction()
     {
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
         $subject = new ErrorController();
         $response = $subject->accessDeniedAction(new ServerRequest(), 'Error handler is not configured.');
         self::assertSame(403, $response->getStatusCode());
diff --git a/typo3/sysext/frontend/Tests/Unit/Controller/TypoScriptFrontendControllerTest.php b/typo3/sysext/frontend/Tests/Unit/Controller/TypoScriptFrontendControllerTest.php
index b514a675a422..851341bd3bb4 100644
--- a/typo3/sysext/frontend/Tests/Unit/Controller/TypoScriptFrontendControllerTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/Controller/TypoScriptFrontendControllerTest.php
@@ -17,6 +17,7 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Frontend\Tests\Unit\Controller;
 
+use Prophecy\Argument;
 use Psr\Container\ContainerInterface;
 use TYPO3\CMS\Core\Cache\Backend\NullBackend;
 use TYPO3\CMS\Core\Cache\CacheManager;
@@ -26,6 +27,11 @@ use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
 use TYPO3\CMS\Core\EventDispatcher\ListenerProvider;
 use TYPO3\CMS\Core\Http\ServerRequest;
 use TYPO3\CMS\Core\Http\ServerRequestFactory;
+use TYPO3\CMS\Core\Localization\LanguageService;
+use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
+use TYPO3\CMS\Core\Localization\LanguageStore;
+use TYPO3\CMS\Core\Localization\Locales;
+use TYPO3\CMS\Core\Localization\LocalizationFactory;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\PageTitle\PageTitleProviderManager;
 use TYPO3\CMS\Core\Routing\PageArguments;
@@ -141,6 +147,16 @@ class TypoScriptFrontendControllerTest extends UnitTestCase
      */
     public function localizationReturnsUnchangedStringIfNotLocallangLabel()
     {
+        $nullCacheBackend = new NullBackend('');
+        $cacheManager = $this->prophesize(CacheManager::class);
+        $cacheManager->getCache('l10n')->willReturn($nullCacheBackend);
+        $languageService = new LanguageService(new Locales(), new LocalizationFactory(new LanguageStore(), $cacheManager->reveal()));
+        $languageServiceFactoryProphecy = $this->prophesize(LanguageServiceFactory::class);
+        $languageServiceFactoryProphecy->create(Argument::any())->will(function ($args) use ($languageService) {
+            $languageService->init($args[0]);
+            return $languageService;
+        });
+        GeneralUtility::addInstance(LanguageServiceFactory::class, $languageServiceFactoryProphecy->reveal());
         $string = StringUtility::getUniqueId();
         $site = $this->createSiteWithDefaultLanguage([
             'locale' => 'fr',
@@ -531,12 +547,20 @@ class TypoScriptFrontendControllerTest extends UnitTestCase
         $nullCacheBackend = new NullBackend('');
         $cacheManager = $this->prophesize(CacheManager::class);
         $cacheManager->getCache('pages')->willReturn($nullCacheBackend);
+        $cacheManager->getCache('l10n')->willReturn($nullCacheBackend);
         GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManager->reveal());
         $GLOBALS['TYPO3_REQUEST'] = new ServerRequest('https://www.example.com/');
         $site = $this->createSiteWithDefaultLanguage([
             'locale' => 'fr',
             'typo3Language' => 'fr-test',
         ]);
+        $languageService = new LanguageService(new Locales(), new LocalizationFactory(new LanguageStore(), $cacheManager->reveal()));
+        $languageServiceFactoryProphecy = $this->prophesize(LanguageServiceFactory::class);
+        $languageServiceFactoryProphecy->create(Argument::any())->will(function ($args) use ($languageService) {
+            $languageService->init($args[0]);
+            return $languageService;
+        });
+        GeneralUtility::addInstance(LanguageServiceFactory::class, $languageServiceFactoryProphecy->reveal());
         // Constructor calling initPageRenderer()
         new TypoScriptFrontendController(
             new Context(),
@@ -556,12 +580,20 @@ class TypoScriptFrontendControllerTest extends UnitTestCase
         $nullCacheBackend = new NullBackend('');
         $cacheManager = $this->prophesize(CacheManager::class);
         $cacheManager->getCache('pages')->willReturn($nullCacheBackend);
+        $cacheManager->getCache('l10n')->willReturn($nullCacheBackend);
         GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManager->reveal());
         $GLOBALS['TYPO3_REQUEST'] = new ServerRequest('https://www.example.com/');
         $site = $this->createSiteWithDefaultLanguage([
             'locale' => 'fr',
             'typo3Language' => 'fr',
         ]);
+        $languageService = new LanguageService(new Locales(), new LocalizationFactory(new LanguageStore(), $cacheManager->reveal()));
+        $languageServiceFactoryProphecy = $this->prophesize(LanguageServiceFactory::class);
+        $languageServiceFactoryProphecy->create(Argument::any())->will(function ($args) use ($languageService) {
+            $languageService->init($args[0]);
+            return $languageService;
+        });
+        GeneralUtility::addInstance(LanguageServiceFactory::class, $languageServiceFactoryProphecy->reveal());
         // Constructor calling setOutputLanguage()
         $subject = $this->getAccessibleMock(
             TypoScriptFrontendController::class,
diff --git a/typo3/sysext/frontend/Tests/Unit/Http/RequestHandlerTest.php b/typo3/sysext/frontend/Tests/Unit/Http/RequestHandlerTest.php
index fb5b08e1e368..85186218c6a4 100644
--- a/typo3/sysext/frontend/Tests/Unit/Http/RequestHandlerTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/Http/RequestHandlerTest.php
@@ -21,6 +21,7 @@ use Prophecy\Argument;
 use Psr\EventDispatcher\EventDispatcherInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Http\ServerRequestFactory;
+use TYPO3\CMS\Core\Information\Typo3Information;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
@@ -250,6 +251,9 @@ class RequestHandlerTest extends UnitTestCase
         $tsfe->pSetup = [
             'meta.' => $typoScript
         ];
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getInlineHeaderComment()->willReturn('dummy');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
 
         $pageRendererProphecy = $this->prophesize(PageRenderer::class);
         $subject = $this->getAccessibleMock(RequestHandler::class, ['getPageRenderer'], [], '', false);
@@ -292,6 +296,10 @@ class RequestHandlerTest extends UnitTestCase
         $tsfe->pSetup = [
             'meta.' => $typoScript
         ];
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getInlineHeaderComment()->willReturn('dummy');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
+
         $pageRendererProphecy = $this->prophesize(PageRenderer::class);
         $subject = $this->getAccessibleMock(RequestHandler::class, ['getPageRenderer'], [], '', false);
         $requestProphecy = $this->prophesize(ServerRequestInterface::class)->reveal();
@@ -335,6 +343,9 @@ class RequestHandlerTest extends UnitTestCase
         $tsfe->pSetup = [
             'meta.' => $typoScript
         ];
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getInlineHeaderComment()->willReturn('dummy');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
 
         $pageRendererProphecy = $this->prophesize(PageRenderer::class);
         $subject = $this->getAccessibleMock(RequestHandler::class, ['getPageRenderer'], [], '', false);
@@ -434,6 +445,10 @@ class RequestHandlerTest extends UnitTestCase
         $tsfe->pSetup = [
             'meta.' => $typoScript
         ];
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getInlineHeaderComment()->willReturn('This website is...');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
+
         $pageRendererProphecy = $this->prophesize(PageRenderer::class);
         $subject = $this->getAccessibleMock(RequestHandler::class, ['getPageRenderer'], [], '', false);
         $subject->expects(self::any())->method('getPageRenderer')->willReturn($pageRendererProphecy->reveal());
diff --git a/typo3/sysext/frontend/Tests/Unit/Middleware/PageArgumentValidatorTest.php b/typo3/sysext/frontend/Tests/Unit/Middleware/PageArgumentValidatorTest.php
index da0925d6848f..119ccdc24f45 100644
--- a/typo3/sysext/frontend/Tests/Unit/Middleware/PageArgumentValidatorTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/Middleware/PageArgumentValidatorTest.php
@@ -23,8 +23,10 @@ use Psr\Http\Server\RequestHandlerInterface;
 use Psr\Log\NullLogger;
 use TYPO3\CMS\Core\Http\Response;
 use TYPO3\CMS\Core\Http\ServerRequest;
+use TYPO3\CMS\Core\Information\Typo3Information;
 use TYPO3\CMS\Core\Routing\PageArguments;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\Middleware\PageArgumentValidator;
 use TYPO3\CMS\Frontend\Middleware\PageResolver;
 use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
@@ -104,6 +106,10 @@ class PageArgumentValidatorTest extends UnitTestCase
         $request = $request->withAttribute('routing', $pageArguments);
 
         $subject = new PageArgumentValidator($this->cacheHashCalculator, $this->timeTrackerStub);
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
+
         $response = $subject->process($request, $this->responseOutputHandler);
         self::assertEquals(404, $response->getStatusCode());
     }
@@ -117,6 +123,9 @@ class PageArgumentValidatorTest extends UnitTestCase
         $request = new ServerRequest($incomingUrl, 'GET');
 
         $subject = new PageArgumentValidator($this->cacheHashCalculator, $this->timeTrackerStub);
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
         $response = $subject->process($request, $this->responseOutputHandler);
         self::assertEquals(404, $response->getStatusCode());
     }
@@ -151,6 +160,9 @@ class PageArgumentValidatorTest extends UnitTestCase
         $request = $request->withAttribute('routing', $pageArguments);
 
         $subject = new PageArgumentValidator($this->cacheHashCalculator, $this->timeTrackerStub);
+        $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+        $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+        GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
         $response = $subject->process($request, $this->responseOutputHandler);
         self::assertEquals(404, $response->getStatusCode());
     }
diff --git a/typo3/sysext/frontend/Tests/UnitDeprecated/Controller/TypoScriptFrontendControllerTest.php b/typo3/sysext/frontend/Tests/UnitDeprecated/Controller/TypoScriptFrontendControllerTest.php
index f73a2a4babc0..52101d3f8213 100644
--- a/typo3/sysext/frontend/Tests/UnitDeprecated/Controller/TypoScriptFrontendControllerTest.php
+++ b/typo3/sysext/frontend/Tests/UnitDeprecated/Controller/TypoScriptFrontendControllerTest.php
@@ -21,8 +21,10 @@ use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Domain\Repository\PageRepository;
 use TYPO3\CMS\Core\Http\ImmediateResponseException;
 use TYPO3\CMS\Core\Http\ServerRequest;
+use TYPO3\CMS\Core\Information\Typo3Information;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Routing\PageArguments;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
@@ -127,6 +129,9 @@ class TypoScriptFrontendControllerTest extends UnitTestCase
         $this->subject->_set('pageArguments', new PageArguments(1, '0', ['tx_test' => 1], ['tx_test' => 1], $remainingArguments));
 
         if ($expected) {
+            $typo3InformationProphecy = $this->prophesize(Typo3Information::class);
+            $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX');
+            GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal());
             static::expectException(ImmediateResponseException::class);
         }
         $this->subject->reqCHash();
diff --git a/typo3/sysext/install/Classes/Command/UpgradeWizardListCommand.php b/typo3/sysext/install/Classes/Command/UpgradeWizardListCommand.php
index 3409cf5a2858..f3e1089accad 100644
--- a/typo3/sysext/install/Classes/Command/UpgradeWizardListCommand.php
+++ b/typo3/sysext/install/Classes/Command/UpgradeWizardListCommand.php
@@ -37,6 +37,11 @@ use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
  */
 class UpgradeWizardListCommand extends Command
 {
+    /**
+     * @var LateBootService
+     */
+    private $lateBootService;
+
     /**
      * @var UpgradeWizardsService
      */
@@ -52,12 +57,22 @@ class UpgradeWizardListCommand extends Command
      */
     private $input;
 
+    public function __construct(
+        string $name,
+        LateBootService $lateBootService,
+        UpgradeWizardsService $upgradeWizardsService
+    ) {
+        $this->lateBootService = $lateBootService;
+        $this->upgradeWizardsService = $upgradeWizardsService;
+        parent::__construct($name);
+    }
+
     /**
      * Bootstrap running of upgradeWizards
      */
     protected function bootstrap(): void
     {
-        GeneralUtility::makeInstance(LateBootService::class)->loadExtLocalconfDatabaseAndExtTables();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
         Bootstrap::initializeBackendUser(CommandLineUserAuthentication::class);
         Bootstrap::initializeBackendAuthentication();
     }
@@ -88,7 +103,6 @@ class UpgradeWizardListCommand extends Command
         $this->output = new SymfonyStyle($input, $output);
         $this->input = $input;
         $this->bootstrap();
-        $this->upgradeWizardsService = new UpgradeWizardsService();
 
         $result = 0;
         $wizards = [];
diff --git a/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php b/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php
index 7f03b5c25fea..709a8a311fa8 100644
--- a/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php
+++ b/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php
@@ -41,6 +41,11 @@ use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
  */
 class UpgradeWizardRunCommand extends Command
 {
+    /**
+     * @var LateBootService
+     */
+    private $lateBootService;
+
     /**
      * @var UpgradeWizardsService
      */
@@ -56,16 +61,25 @@ class UpgradeWizardRunCommand extends Command
      */
     private $input;
 
+    public function __construct(
+        string $name,
+        LateBootService $lateBootService,
+        UpgradeWizardsService $upgradeWizardsService
+    ) {
+        $this->lateBootService = $lateBootService;
+        $this->upgradeWizardsService = $upgradeWizardsService;
+        parent::__construct($name);
+    }
+
     /**
      * Bootstrap running of upgrade wizard,
      * ensure database is utf-8
      */
     protected function bootstrap(): void
     {
-        GeneralUtility::makeInstance(LateBootService::class)->loadExtLocalconfDatabaseAndExtTables();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
         Bootstrap::initializeBackendUser(CommandLineUserAuthentication::class);
         Bootstrap::initializeBackendAuthentication();
-        $this->upgradeWizardsService = new UpgradeWizardsService();
         $this->upgradeWizardsService->isDatabaseCharsetUtf8() ?: $this->upgradeWizardsService->setDatabaseCharsetUtf8();
     }
 
diff --git a/typo3/sysext/install/Classes/Controller/AbstractController.php b/typo3/sysext/install/Classes/Controller/AbstractController.php
index 2c182a5d836d..6638d8bb3f86 100644
--- a/typo3/sysext/install/Classes/Controller/AbstractController.php
+++ b/typo3/sysext/install/Classes/Controller/AbstractController.php
@@ -17,13 +17,11 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Install\Controller;
 
-use Psr\Container\ContainerInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Information\Typo3Version;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Fluid\View\StandaloneView;
-use TYPO3\CMS\Install\Service\LateBootService;
 
 /**
  * Controller abstract for shared parts of the install tool
@@ -55,24 +53,4 @@ class AbstractController
         ]);
         return $view;
     }
-
-    /**
-     * Some actions like the database analyzer and the upgrade wizards need additional
-     * bootstrap actions performed.
-     *
-     * Those actions can potentially fatal if some old extension is loaded that triggers
-     * a fatal in ext_localconf or ext_tables code! Use only if really needed.
-     *
-     * @param bool $resetContainer
-     * @return ContainerInterface
-     */
-    public function loadExtLocalconfDatabaseAndExtTables(bool $resetContainer = true): ContainerInterface
-    {
-        return GeneralUtility::makeInstance(LateBootService::class)->loadExtLocalconfDatabaseAndExtTables($resetContainer);
-    }
-
-    public function resetGlobalContainer(): void
-    {
-        GeneralUtility::makeInstance(LateBootService::class)->makeCurrent(null, []);
-    }
 }
diff --git a/typo3/sysext/install/Classes/Controller/IconController.php b/typo3/sysext/install/Classes/Controller/IconController.php
index ade8f76f9192..9ed9c7d52710 100644
--- a/typo3/sysext/install/Classes/Controller/IconController.php
+++ b/typo3/sysext/install/Classes/Controller/IconController.php
@@ -23,7 +23,6 @@ use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Imaging\IconRegistry;
 use TYPO3\CMS\Core\Type\Icon\IconState;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Controller for icon handling
@@ -41,13 +40,10 @@ class IconController extends AbstractController
      */
     protected $iconFactory;
 
-    /**
-     * Set up dependencies
-     */
-    public function __construct()
+    public function __construct(IconRegistry $iconRegistry, IconFactory $iconFactory)
     {
-        $this->iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
-        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
+        $this->iconRegistry = $iconRegistry;
+        $this->iconFactory = $iconFactory;
     }
 
     /**
diff --git a/typo3/sysext/install/Classes/Controller/InstallerController.php b/typo3/sysext/install/Classes/Controller/InstallerController.php
index 95fa3f4957f3..1f65e6616ec6 100644
--- a/typo3/sysext/install/Classes/Controller/InstallerController.php
+++ b/typo3/sysext/install/Classes/Controller/InstallerController.php
@@ -19,7 +19,6 @@ namespace TYPO3\CMS\Install\Controller;
 
 use Doctrine\DBAL\DBALException;
 use Doctrine\DBAL\DriverManager;
-use Psr\Container\ContainerInterface;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
@@ -49,7 +48,6 @@ use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
 use TYPO3\CMS\Core\Package\FailsafePackageManager;
 use TYPO3\CMS\Core\Package\PackageInterface;
-use TYPO3\CMS\Core\Package\PackageManager;
 use TYPO3\CMS\Core\Registry;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Fluid\View\StandaloneView;
@@ -70,6 +68,52 @@ use TYPO3\CMS\Install\SystemEnvironment\SetupCheck;
  */
 class InstallerController
 {
+    /**
+     * @var LateBootService
+     */
+    private $lateBootService;
+
+    /**
+     * @var SilentConfigurationUpgradeService
+     */
+    private $silentConfigurationUpgradeService;
+
+    /**
+     * @var ConfigurationManager
+     */
+    private $configurationManager;
+
+    /**
+     * @var SiteConfiguration
+     */
+    private $siteConfiguration;
+
+    /**
+     * @var Registry
+     */
+    private $registry;
+
+    /**
+     * @var FailsafePackageManager
+     */
+    private $packageManager;
+
+    public function __construct(
+        LateBootService $lateBootService,
+        SilentConfigurationUpgradeService $silentConfigurationUpgradeService,
+        ConfigurationManager $configurationManager,
+        SiteConfiguration $siteConfiguration,
+        Registry $registry,
+        FailsafePackageManager $packageManager
+    ) {
+        $this->lateBootService = $lateBootService;
+        $this->silentConfigurationUpgradeService = $silentConfigurationUpgradeService;
+        $this->configurationManager = $configurationManager;
+        $this->siteConfiguration = $siteConfiguration;
+        $this->registry = $registry;
+        $this->packageManager = $packageManager;
+    }
+
     /**
      * Init action loads <head> with JS initiating further stuff
      *
@@ -129,7 +173,7 @@ class InstallerController
     public function checkEnvironmentAndFoldersAction(): ResponseInterface
     {
         return new JsonResponse([
-            'success' => @is_file(GeneralUtility::makeInstance(ConfigurationManager::class)->getLocalConfigurationFileLocation()),
+            'success' => @is_file($this->configurationManager->getLocalConfigurationFileLocation()),
         ]);
     }
 
@@ -175,22 +219,19 @@ class InstallerController
         $errorsFromStructure = $structureFixMessageQueue->getAllMessages(FlashMessage::ERROR);
 
         if (@is_dir(Environment::getLegacyConfigPath())) {
-            $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
-            $configurationManager->createLocalConfigurationFromFactoryConfiguration();
+            $this->configurationManager->createLocalConfigurationFromFactoryConfiguration();
 
             // Create a PackageStates.php with all packages activated marked as "part of factory default"
             if (!file_exists(Environment::getLegacyConfigPath() . '/PackageStates.php')) {
-                /** @var FailsafePackageManager $packageManager */
-                $packageManager = GeneralUtility::makeInstance(PackageManager::class);
-                $packages = $packageManager->getAvailablePackages();
+                $packages = $this->packageManager->getAvailablePackages();
                 foreach ($packages as $package) {
                     if ($package instanceof PackageInterface
                         && $package->isPartOfFactoryDefault()
                     ) {
-                        $packageManager->activatePackage($package->getPackageKey());
+                        $this->packageManager->activatePackage($package->getPackageKey());
                     }
                 }
-                $packageManager->forceSortAndSavePackageStates();
+                $this->packageManager->forceSortAndSavePackageStates();
             }
             $extensionConfiguration = new ExtensionConfiguration();
             $extensionConfiguration->synchronizeExtConfTemplateWithLocalConfigurationOfAllExtensions();
@@ -225,8 +266,7 @@ class InstallerController
     public function executeAdjustTrustedHostsPatternAction(): ResponseInterface
     {
         if (!GeneralUtility::hostHeaderValueMatchesTrustedHostsPattern($_SERVER['HTTP_HOST'])) {
-            $configurationManager = new ConfigurationManager();
-            $configurationManager->setLocalConfigurationValueByPath('SYS/trustedHostsPattern', '.*');
+            $this->configurationManager->setLocalConfigurationValueByPath('SYS/trustedHostsPattern', '.*');
         }
         return new JsonResponse([
             'success' => true,
@@ -240,10 +280,9 @@ class InstallerController
      */
     public function executeSilentConfigurationUpdateAction(): ResponseInterface
     {
-        $silentUpdate = new SilentConfigurationUpgradeService();
         $success = true;
         try {
-            $silentUpdate->execute();
+            $this->silentConfigurationUpgradeService->execute();
         } catch (ConfigurationChangedException $e) {
             $success = false;
         }
@@ -529,11 +568,10 @@ class InstallerController
             foreach ($defaultConnectionSettings as $settingsName => $value) {
                 $localConfigurationPathValuePairs['DB/Connections/Default/' . $settingsName] = $value;
             }
-            $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
             // Remove full default connection array
-            $configurationManager->removeLocalConfigurationKeysByPath(['DB/Connections/Default']);
+            $this->configurationManager->removeLocalConfigurationKeysByPath(['DB/Connections/Default']);
             // Write new values
-            $configurationManager->setLocalConfigurationValuesByPathValuePairs($localConfigurationPathValuePairs);
+            $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($localConfigurationPathValuePairs);
         }
 
         return new JsonResponse([
@@ -635,8 +673,7 @@ class InstallerController
                     ->dropDatabase($databaseName);
             }
 
-            GeneralUtility::makeInstance(ConfigurationManager::class)
-                ->removeLocalConfigurationKeysByPath(['DB/Connections/Default/dbname']);
+            $this->configurationManager->removeLocalConfigurationKeysByPath(['DB/Connections/Default/dbname']);
 
             $message = new FlashMessage(
                 sprintf(
@@ -783,7 +820,6 @@ class InstallerController
     public function executeDatabaseDataAction(ServerRequestInterface $request): ResponseInterface
     {
         $messages = [];
-        $configurationManager = new ConfigurationManager();
         $postValues = $request->getParsedBody()['install']['values'];
         $username = (string)$postValues['username'] !== '' ? $postValues['username'] : 'admin';
         // Check password and return early if not good enough
@@ -803,7 +839,7 @@ class InstallerController
         }
         // Set site name
         if (!empty($postValues['sitename'])) {
-            $configurationManager->setLocalConfigurationValueByPath('SYS/sitename', $postValues['sitename']);
+            $this->configurationManager->setLocalConfigurationValueByPath('SYS/sitename', $postValues['sitename']);
         }
         try {
             $messages = $this->importDatabaseData();
@@ -850,7 +886,7 @@ class InstallerController
             ]);
         }
         // Set password as install tool password, add admin user to system maintainers
-        $configurationManager->setLocalConfigurationValuesByPathValuePairs([
+        $this->configurationManager->setLocalConfigurationValuesByPathValuePairs([
             'BE/installToolPassword' => $this->getHashedPassword($password),
             'SYS/systemMaintainers' => [$adminUserUid]
         ]);
@@ -974,16 +1010,14 @@ For each website you need a TypoScript template on the main page of your website
         }
 
         // Mark upgrade wizards as done
-        $this->loadExtLocalconfDatabaseAndExtTables();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
         if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'])) {
-            $registry = GeneralUtility::makeInstance(Registry::class);
             foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] as $updateClassName) {
-                $registry->set('installUpdate', $updateClassName, 1);
+                $this->registry->set('installUpdate', $updateClassName, 1);
             }
         }
 
-        $configurationManager = new ConfigurationManager();
-        $configurationManager->setLocalConfigurationValuesByPathValuePairs($configurationValues);
+        $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($configurationValues);
 
         $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
         $formProtection->clean();
@@ -1179,7 +1213,7 @@ For each website you need a TypoScript template on the main page of your website
             GeneralUtility::makeInstance(ConnectionPool::class)
                 ->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME)
                 ->exec(PlatformInformation::getDatabaseCreateStatementWithCharset($platform, $dbName));
-            GeneralUtility::makeInstance(ConfigurationManager::class)
+            $this->configurationManager
                 ->setLocalConfigurationValueByPath('DB/Connections/Default/dbname', $dbName);
         } catch (DBALException $e) {
             return new FlashMessage(
@@ -1209,7 +1243,6 @@ For each website you need a TypoScript template on the main page of your website
     {
         $result = new FlashMessage('');
         $localConfigurationPathValuePairs = [];
-        $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
 
         $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][ConnectionPool::DEFAULT_CONNECTION_NAME]['dbname'] = $dbName;
         try {
@@ -1238,7 +1271,7 @@ For each website you need a TypoScript template on the main page of your website
         }
 
         if ($result->getSeverity() === FlashMessage::OK && !empty($localConfigurationPathValuePairs)) {
-            $configurationManager->setLocalConfigurationValuesByPathValuePairs($localConfigurationPathValuePairs);
+            $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($localConfigurationPathValuePairs);
         }
 
         return $result;
@@ -1285,7 +1318,7 @@ For each website you need a TypoScript template on the main page of your website
         // Will load ext_localconf and ext_tables. This is pretty safe here since we are
         // in first install (database empty), so it is very likely that no extension is loaded
         // that could trigger a fatal at this point.
-        $container = $this->loadExtLocalconfDatabaseAndExtTables();
+        $container = $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
 
         $sqlReader = $container->get(SqlReader::class);
         $sqlCode = $sqlReader->getTablesDefinitionString(true);
@@ -1313,20 +1346,6 @@ For each website you need a TypoScript template on the main page of your website
         return array_values($results);
     }
 
-    /**
-     * Some actions like the database analyzer and the upgrade wizards need additional
-     * bootstrap actions performed.
-     *
-     * Those actions can potentially fatal if some old extension is loaded that triggers
-     * a fatal in ext_localconf or ext_tables code! Use only if really needed.
-     *
-     * @return ContainerInterface
-     */
-    protected function loadExtLocalconfDatabaseAndExtTables(): ContainerInterface
-    {
-        return GeneralUtility::makeInstance(LateBootService::class)->loadExtLocalconfDatabaseAndExtTables();
-    }
-
     /**
      * Creates a site configuration with one language "English" which is the de-facto default language for TYPO3 in general.
      *
@@ -1343,10 +1362,6 @@ For each website you need a TypoScript template on the main page of your website
         }
 
         // Create a default site configuration called "main" as best practice
-        $siteConfiguration = GeneralUtility::makeInstance(
-            SiteConfiguration::class,
-            Environment::getConfigPath() . '/sites'
-        );
-        $siteConfiguration->createNewBasicSite($identifier, $rootPageId, $normalizedParams->getSiteUrl());
+        $this->siteConfiguration->createNewBasicSite($identifier, $rootPageId, $normalizedParams->getSiteUrl());
     }
 }
diff --git a/typo3/sysext/install/Classes/Controller/LayoutController.php b/typo3/sysext/install/Classes/Controller/LayoutController.php
index bb9951cd8fcd..6a6af4cb24c9 100644
--- a/typo3/sysext/install/Classes/Controller/LayoutController.php
+++ b/typo3/sysext/install/Classes/Controller/LayoutController.php
@@ -37,6 +37,17 @@ use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
  */
 class LayoutController extends AbstractController
 {
+    /**
+     * @var SilentConfigurationUpgradeService
+     */
+    private $silentConfigurationUpgradeService;
+
+    public function __construct(
+        SilentConfigurationUpgradeService $silentConfigurationUpgradeService
+    ) {
+        $this->silentConfigurationUpgradeService = $silentConfigurationUpgradeService;
+    }
+
     /**
      * The init action renders an HTML response with HTML view having <head> section
      * containing resources to main .js routing.
@@ -90,10 +101,9 @@ class LayoutController extends AbstractController
      */
     public function executeSilentConfigurationUpdateAction(): ResponseInterface
     {
-        $silentUpdate = new SilentConfigurationUpgradeService();
         $success = true;
         try {
-            $silentUpdate->execute();
+            $this->silentConfigurationUpgradeService->execute();
         } catch (ConfigurationChangedException $e) {
             $success = false;
         }
diff --git a/typo3/sysext/install/Classes/Controller/MaintenanceController.php b/typo3/sysext/install/Classes/Controller/MaintenanceController.php
index a373da545013..e4e73e5c4100 100644
--- a/typo3/sysext/install/Classes/Controller/MaintenanceController.php
+++ b/typo3/sysext/install/Classes/Controller/MaintenanceController.php
@@ -38,6 +38,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Install\Service\ClearCacheService;
 use TYPO3\CMS\Install\Service\ClearTableService;
 use TYPO3\CMS\Install\Service\LanguagePackService;
+use TYPO3\CMS\Install\Service\LateBootService;
 use TYPO3\CMS\Install\Service\Typo3tempFileService;
 
 /**
@@ -46,6 +47,58 @@ use TYPO3\CMS\Install\Service\Typo3tempFileService;
  */
 class MaintenanceController extends AbstractController
 {
+    /**
+     * @var LateBootService
+     */
+    private $lateBootService;
+
+    /**
+     * @var ClearCacheService
+     */
+    private $clearCacheService;
+
+    /**
+     * @var LanguagePackService
+     */
+    private $languagePackService;
+
+    /**
+     * @var Typo3tempFileService
+     */
+    private $typo3tempFileService;
+
+    /**
+     * @var ConfigurationManager
+     */
+    private $configurationManager;
+
+    /**
+     * @var PasswordHashFactory
+     */
+    private $passwordHashFactory;
+
+    /**
+     * @var Locales
+     */
+    private $locales;
+
+    public function __construct(
+        LateBootService $lateBootService,
+        ClearCacheService $clearCacheService,
+        LanguagePackService $languagePackService,
+        Typo3tempFileService $typo3tempFileService,
+        ConfigurationManager $configurationManager,
+        PasswordHashFactory $passwordHashFactory,
+        Locales $locales
+    ) {
+        $this->lateBootService = $lateBootService;
+        $this->clearCacheService = $clearCacheService;
+        $this->languagePackService = $languagePackService;
+        $this->typo3tempFileService = $typo3tempFileService;
+        $this->configurationManager = $configurationManager;
+        $this->passwordHashFactory = $passwordHashFactory;
+        $this->locales = $locales;
+    }
     /**
      * Main "show the cards" view
      *
@@ -68,7 +121,7 @@ class MaintenanceController extends AbstractController
      */
     public function cacheClearAllAction(): ResponseInterface
     {
-        GeneralUtility::makeInstance(ClearCacheService::class)->clearAll();
+        $this->clearCacheService->clearAll();
         GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
         $messageQueue = (new FlashMessageQueue('install'))->enqueue(
             new FlashMessage('Successfully cleared all caches and all available opcode caches.', 'Caches cleared')
@@ -87,7 +140,7 @@ class MaintenanceController extends AbstractController
      */
     public function clearTypo3tempFilesStatsAction(ServerRequestInterface $request): ResponseInterface
     {
-        $this->loadExtLocalconfDatabaseAndExtTables();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
         $view = $this->initializeStandaloneView($request, 'Maintenance/ClearTypo3tempFiles.html');
         $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
         $view->assignMultiple([
@@ -96,7 +149,7 @@ class MaintenanceController extends AbstractController
         return new JsonResponse(
             [
                 'success' => true,
-                'stats' => (new Typo3tempFileService())->getDirectoryStatistics(),
+                'stats' => $this->typo3tempFileService->getDirectoryStatistics(),
                 'html' => $view->render(),
                 'buttons' => [
                     [
@@ -116,18 +169,17 @@ class MaintenanceController extends AbstractController
      */
     public function clearTypo3tempFilesAction(ServerRequestInterface $request): ResponseInterface
     {
-        $this->loadExtLocalconfDatabaseAndExtTables();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
         $messageQueue = new FlashMessageQueue('install');
-        $typo3tempFileService = new Typo3tempFileService();
         $folder = $request->getParsedBody()['install']['folder'];
         // storageUid is an optional post param if FAL storages should be cleaned
         $storageUid = $request->getParsedBody()['install']['storageUid'] ?? null;
         if ($storageUid === null) {
-            $typo3tempFileService->clearAssetsFolder($folder);
+            $this->typo3tempFileService->clearAssetsFolder($folder);
             $messageQueue->enqueue(new FlashMessage('Cleared files in "' . $folder . '" folder'));
         } else {
             $storageUid = (int)$storageUid;
-            $failedDeletions = $typo3tempFileService->clearProcessedFiles($storageUid);
+            $failedDeletions = $this->typo3tempFileService->clearProcessedFiles($storageUid);
             if ($failedDeletions) {
                 $messageQueue->enqueue(new FlashMessage(
                     'Failed to delete ' . $failedDeletions . ' processed files. See TYPO3 log (by default typo3temp/var/log/typo3_*.log)',
@@ -206,7 +258,7 @@ class MaintenanceController extends AbstractController
      */
     public function databaseAnalyzerAnalyzeAction(ServerRequestInterface $request): ResponseInterface
     {
-        $container = $this->loadExtLocalconfDatabaseAndExtTables();
+        $container = $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
         $messageQueue = new FlashMessageQueue('install');
         $suggestions = [];
         try {
@@ -362,7 +414,7 @@ class MaintenanceController extends AbstractController
      */
     public function databaseAnalyzerExecuteAction(ServerRequestInterface $request): ResponseInterface
     {
-        $container = $this->loadExtLocalconfDatabaseAndExtTables();
+        $container = $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
         $messageQueue = new FlashMessageQueue('install');
         $selectedHashes = $request->getParsedBody()['install']['hashes'] ?? [];
         if (empty($selectedHashes)) {
@@ -521,7 +573,7 @@ class MaintenanceController extends AbstractController
                     FlashMessage::ERROR
                 ));
             } else {
-                $hashInstance = GeneralUtility::makeInstance(PasswordHashFactory::class)->getDefaultHashInstance('BE');
+                $hashInstance = $this->passwordHashFactory->getDefaultHashInstance('BE');
                 $hashedPassword = $hashInstance->getHashedPassword($password);
                 $adminUserFields = [
                     'username' => $username,
@@ -548,8 +600,7 @@ class MaintenanceController extends AbstractController
                     $newSystemMaintainersList[] = $newAdminUserUid;
 
                     // Update the LocalConfiguration.php file with the new list
-                    $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
-                    $configurationManager->setLocalConfigurationValuesByPathValuePairs(
+                    $this->configurationManager->setLocalConfigurationValuesByPathValuePairs(
                         ['SYS/systemMaintainers' => $newSystemMaintainersList]
                     );
                 }
@@ -585,15 +636,14 @@ class MaintenanceController extends AbstractController
             'languagePacksUpdateIsoTimesToken' => $formProtection->generateToken('installTool', 'languagePacksUpdateIsoTimes'),
         ]);
         // This action needs TYPO3_CONF_VARS for full GeneralUtility::getUrl() config
-        $this->loadExtLocalconfDatabaseAndExtTables();
-        $languagePacksService = GeneralUtility::makeInstance(LanguagePackService::class);
-        $languagePacksService->updateMirrorBaseUrl();
-        $extensions = $languagePacksService->getExtensionLanguagePackDetails();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
+        $this->languagePackService->updateMirrorBaseUrl();
+        $extensions = $this->languagePackService->getExtensionLanguagePackDetails();
         return new JsonResponse([
             'success' => true,
-            'languages' => $languagePacksService->getLanguageDetails(),
+            'languages' => $this->languagePackService->getLanguageDetails(),
             'extensions' => $extensions,
-            'activeLanguages' => $languagePacksService->getActiveLanguages(),
+            'activeLanguages' => $this->languagePackService->getActiveLanguages(),
             'activeExtensions' => array_column($extensions, 'key'),
             'html' => $view->render(),
         ]);
@@ -609,7 +659,6 @@ class MaintenanceController extends AbstractController
     {
         $messageQueue = new FlashMessageQueue('install');
         $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
-        $locales = GeneralUtility::makeInstance(Locales::class);
         $availableLanguages = $languagePackService->getAvailableLanguages();
         $activeLanguages = $languagePackService->getActiveLanguages();
         $iso = $request->getParsedBody()['install']['iso'];
@@ -617,7 +666,7 @@ class MaintenanceController extends AbstractController
         foreach ($availableLanguages as $availableIso => $name) {
             if ($availableIso === $iso && !in_array($availableIso, $activeLanguages, true)) {
                 $activateArray[] = $iso;
-                $dependencies = $locales->getLocaleDependencies($availableIso);
+                $dependencies = $this->locales->getLocaleDependencies($availableIso);
                 if (!empty($dependencies)) {
                     foreach ($dependencies as $dependency) {
                         if (!in_array($dependency, $activeLanguages, true)) {
@@ -630,8 +679,7 @@ class MaintenanceController extends AbstractController
         if (!empty($activateArray)) {
             $activeLanguages = array_merge($activeLanguages, $activateArray);
             sort($activeLanguages);
-            $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
-            $configurationManager->setLocalConfigurationValueByPath(
+            $this->configurationManager->setLocalConfigurationValueByPath(
                 'EXTCONF/lang',
                 ['availableLanguages' => $activeLanguages]
             );
@@ -666,7 +714,6 @@ class MaintenanceController extends AbstractController
     {
         $messageQueue = new FlashMessageQueue('install');
         $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
-        $locales = GeneralUtility::makeInstance(Locales::class);
         $availableLanguages = $languagePackService->getAvailableLanguages();
         $activeLanguages = $languagePackService->getActiveLanguages();
         $iso = $request->getParsedBody()['install']['iso'];
@@ -678,7 +725,7 @@ class MaintenanceController extends AbstractController
             if ($activeLanguage === $iso) {
                 continue;
             }
-            $dependencies = $locales->getLocaleDependencies($activeLanguage);
+            $dependencies = $this->locales->getLocaleDependencies($activeLanguage);
             if (in_array($iso, $dependencies, true)) {
                 $otherActiveLanguageDependencies[] = $activeLanguage;
             }
@@ -708,8 +755,7 @@ class MaintenanceController extends AbstractController
                     }
                     $newActiveLanguages[] = $activeLanguage;
                 }
-                $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
-                $configurationManager->setLocalConfigurationValueByPath(
+                $this->configurationManager->setLocalConfigurationValueByPath(
                     'EXTCONF/lang',
                     ['availableLanguages' => $newActiveLanguages]
                 );
@@ -743,7 +789,7 @@ class MaintenanceController extends AbstractController
      */
     public function languagePacksUpdatePackAction(ServerRequestInterface $request): ResponseInterface
     {
-        $this->loadExtLocalconfDatabaseAndExtTables();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
         $iso = $request->getParsedBody()['install']['iso'];
         $key = $request->getParsedBody()['install']['extension'];
         $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
diff --git a/typo3/sysext/install/Classes/Controller/SettingsController.php b/typo3/sysext/install/Classes/Controller/SettingsController.php
index 00a6b32d772a..e62054d22f13 100644
--- a/typo3/sysext/install/Classes/Controller/SettingsController.php
+++ b/typo3/sysext/install/Classes/Controller/SettingsController.php
@@ -29,7 +29,7 @@ use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
 use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
 use TYPO3\CMS\Core\Http\JsonResponse;
-use TYPO3\CMS\Core\Localization\LanguageService;
+use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
 use TYPO3\CMS\Core\Package\PackageManager;
@@ -46,6 +46,31 @@ use TYPO3\CMS\Install\Service\LocalConfigurationValueService;
  */
 class SettingsController extends AbstractController
 {
+    /**
+     * @var PackageManager
+     */
+    private $packageManager;
+
+    /**
+     * @var ExtensionConfigurationService
+     */
+    private $extensionConfigurationService;
+
+    /**
+     * @var LanguageServiceFactory
+     */
+    private $languageServiceFactory;
+
+    public function __construct(
+        PackageManager $packageManager,
+        ExtensionConfigurationService $extensionConfigurationService,
+        LanguageServiceFactory $languageServiceFactory
+    ) {
+        $this->packageManager = $packageManager;
+        $this->extensionConfigurationService = $extensionConfigurationService;
+        $this->languageServiceFactory = $languageServiceFactory;
+    }
+
     /**
      * Main "show the cards" view
      *
@@ -379,15 +404,14 @@ class SettingsController extends AbstractController
     public function extensionConfigurationGetContentAction(ServerRequestInterface $request): ResponseInterface
     {
         // Extension configuration needs initialized $GLOBALS['LANG']
-        $GLOBALS['LANG'] = LanguageService::create('default');
-        $extensionConfigurationService = new ExtensionConfigurationService();
+        $GLOBALS['LANG'] = $this->languageServiceFactory->create('default');
         $extensionsWithConfigurations = [];
-        $activePackages = GeneralUtility::makeInstance(PackageManager::class)->getActivePackages();
+        $activePackages = $this->packageManager->getActivePackages();
         foreach ($activePackages as $extensionKey => $activePackage) {
             if (@file_exists($activePackage->getPackagePath() . 'ext_conf_template.txt')) {
                 $extensionsWithConfigurations[$extensionKey] = [
                     'packageInfo' => $activePackage,
-                    'configuration' => $extensionConfigurationService->getConfigurationPreparedForView($extensionKey),
+                    'configuration' => $this->extensionConfigurationService->getConfigurationPreparedForView($extensionKey),
                 ];
             }
         }
diff --git a/typo3/sysext/install/Classes/Controller/UpgradeController.php b/typo3/sysext/install/Classes/Controller/UpgradeController.php
index 6236a0457a55..d77e1626377a 100644
--- a/typo3/sysext/install/Classes/Controller/UpgradeController.php
+++ b/typo3/sysext/install/Classes/Controller/UpgradeController.php
@@ -86,6 +86,11 @@ class UpgradeController extends AbstractController
      */
     protected $coreVersionService;
 
+    /**
+     * @var UpgradeWizardsService
+     */
+    private $upgradeWizardsService;
+
     /**
      * @var PackageManager
      */
@@ -96,14 +101,14 @@ class UpgradeController extends AbstractController
      */
     private $lateBootService;
 
-    /**
-     * @param PackageManager $packageManager
-     * @param LateBootService $lateBootService
-     */
-    public function __construct(PackageManager $packageManager, LateBootService $lateBootService)
-    {
+    public function __construct(
+        PackageManager $packageManager,
+        LateBootService $lateBootService,
+        UpgradeWizardsService $upgradeWizardsService
+    ) {
         $this->packageManager = $packageManager;
         $this->lateBootService = $lateBootService;
+        $this->upgradeWizardsService = $upgradeWizardsService;
     }
 
     /**
@@ -945,13 +950,12 @@ class UpgradeController extends AbstractController
     public function upgradeWizardsBlockingDatabaseAddsAction(): ResponseInterface
     {
         // ext_localconf, db and ext_tables must be loaded for the updates :(
-        $this->loadExtLocalconfDatabaseAndExtTables(false);
-        $upgradeWizardsService = new UpgradeWizardsService();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(false);
         $adds = [];
         $needsUpdate = false;
         try {
-            $adds = $upgradeWizardsService->getBlockingDatabaseAdds();
-            $this->resetGlobalContainer();
+            $adds = $this->upgradeWizardsService->getBlockingDatabaseAdds();
+            $this->lateBootService->resetGlobalContainer();
             if (!empty($adds)) {
                 $needsUpdate = true;
             }
@@ -973,10 +977,9 @@ class UpgradeController extends AbstractController
     public function upgradeWizardsBlockingDatabaseExecuteAction(): ResponseInterface
     {
         // ext_localconf, db and ext_tables must be loaded for the updates :(
-        $this->loadExtLocalconfDatabaseAndExtTables(false);
-        $upgradeWizardsService = new UpgradeWizardsService();
-        $upgradeWizardsService->addMissingTablesAndFields();
-        $this->resetGlobalContainer();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(false);
+        $this->upgradeWizardsService->addMissingTablesAndFields();
+        $this->lateBootService->resetGlobalContainer();
         $messages = new FlashMessageQueue('install');
         $messages->enqueue(new FlashMessage(
             '',
@@ -995,8 +998,7 @@ class UpgradeController extends AbstractController
      */
     public function upgradeWizardsBlockingDatabaseCharsetFixAction(): ResponseInterface
     {
-        $upgradeWizardsService = new UpgradeWizardsService();
-        $upgradeWizardsService->setDatabaseCharsetUtf8();
+        $this->upgradeWizardsService->setDatabaseCharsetUtf8();
         $messages = new FlashMessageQueue('install');
         $messages->enqueue(new FlashMessage(
             '',
@@ -1015,8 +1017,7 @@ class UpgradeController extends AbstractController
      */
     public function upgradeWizardsBlockingDatabaseCharsetTestAction(): ResponseInterface
     {
-        $upgradeWizardsService = new UpgradeWizardsService();
-        $result = !$upgradeWizardsService->isDatabaseCharsetUtf8();
+        $result = !$this->upgradeWizardsService->isDatabaseCharsetUtf8();
         return new JsonResponse([
             'success' => true,
             'needsUpdate' => $result,
@@ -1030,11 +1031,10 @@ class UpgradeController extends AbstractController
      */
     public function upgradeWizardsDoneUpgradesAction(): ResponseInterface
     {
-        $this->loadExtLocalconfDatabaseAndExtTables(false);
-        $upgradeWizardsService = new UpgradeWizardsService();
-        $wizardsDone = $upgradeWizardsService->listOfWizardsDone();
-        $rowUpdatersDone = $upgradeWizardsService->listOfRowUpdatersDone();
-        $this->resetGlobalContainer();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(false);
+        $wizardsDone = $this->upgradeWizardsService->listOfWizardsDone();
+        $rowUpdatersDone = $this->upgradeWizardsService->listOfRowUpdatersDone();
+        $this->lateBootService->resetGlobalContainer();
         $messages = new FlashMessageQueue('install');
         if (empty($wizardsDone) && empty($rowUpdatersDone)) {
             $messages->enqueue(new FlashMessage(
@@ -1059,11 +1059,10 @@ class UpgradeController extends AbstractController
     public function upgradeWizardsExecuteAction(ServerRequestInterface $request): ResponseInterface
     {
         // ext_localconf, db and ext_tables must be loaded for the updates :(
-        $this->loadExtLocalconfDatabaseAndExtTables(false);
-        $upgradeWizardsService = new UpgradeWizardsService();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(false);
         $identifier = $request->getParsedBody()['install']['identifier'];
-        $messages = $upgradeWizardsService->executeWizard($identifier);
-        $this->resetGlobalContainer();
+        $messages = $this->upgradeWizardsService->executeWizard($identifier);
+        $this->lateBootService->resetGlobalContainer();
         return new JsonResponse([
             'success' => true,
             'status' => $messages,
@@ -1079,11 +1078,10 @@ class UpgradeController extends AbstractController
     public function upgradeWizardsInputAction(ServerRequestInterface $request): ResponseInterface
     {
         // ext_localconf, db and ext_tables must be loaded for the updates :(
-        $this->loadExtLocalconfDatabaseAndExtTables(false);
-        $upgradeWizardsService = new UpgradeWizardsService();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(false);
         $identifier = $request->getParsedBody()['install']['identifier'];
-        $result = $upgradeWizardsService->getWizardUserInput($identifier);
-        $this->resetGlobalContainer();
+        $result = $this->upgradeWizardsService->getWizardUserInput($identifier);
+        $this->lateBootService->resetGlobalContainer();
         return new JsonResponse([
             'success' => true,
             'status' => [],
@@ -1099,10 +1097,9 @@ class UpgradeController extends AbstractController
     public function upgradeWizardsListAction(): ResponseInterface
     {
         // ext_localconf, db and ext_tables must be loaded for the updates :(
-        $this->loadExtLocalconfDatabaseAndExtTables(false);
-        $upgradeWizardsService = new UpgradeWizardsService();
-        $wizards = $upgradeWizardsService->getUpgradeWizardsList();
-        $this->resetGlobalContainer();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(false);
+        $wizards = $this->upgradeWizardsService->getUpgradeWizardsList();
+        $this->lateBootService->resetGlobalContainer();
         return new JsonResponse([
             'success' => true,
             'status' => [],
@@ -1118,11 +1115,10 @@ class UpgradeController extends AbstractController
      */
     public function upgradeWizardsMarkUndoneAction(ServerRequestInterface $request): ResponseInterface
     {
-        $this->loadExtLocalconfDatabaseAndExtTables(false);
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(false);
         $wizardToBeMarkedAsUndoneIdentifier = $request->getParsedBody()['install']['identifier'];
-        $upgradeWizardsService = new UpgradeWizardsService();
-        $result = $upgradeWizardsService->markWizardUndone($wizardToBeMarkedAsUndoneIdentifier);
-        $this->resetGlobalContainer();
+        $result = $this->upgradeWizardsService->markWizardUndone($wizardToBeMarkedAsUndoneIdentifier);
+        $this->lateBootService->resetGlobalContainer();
         $messages = new FlashMessageQueue('install');
         if ($result) {
             $messages->enqueue(new FlashMessage(
@@ -1178,7 +1174,7 @@ class UpgradeController extends AbstractController
             );
         }
         // @todo: Does the core updater really depend on loaded ext_* files?
-        $this->loadExtLocalconfDatabaseAndExtTables();
+        $this->lateBootService->loadExtLocalconfDatabaseAndExtTables();
     }
 
     /**
diff --git a/typo3/sysext/install/Classes/Middleware/Installer.php b/typo3/sysext/install/Classes/Middleware/Installer.php
index 9ffee4ded10e..b76301f51737 100644
--- a/typo3/sysext/install/Classes/Middleware/Installer.php
+++ b/typo3/sysext/install/Classes/Middleware/Installer.php
@@ -17,6 +17,7 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Install\Middleware;
 
+use Psr\Container\ContainerInterface;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\MiddlewareInterface;
@@ -35,6 +36,16 @@ use TYPO3\CMS\Install\Service\SessionService;
  */
 class Installer implements MiddlewareInterface
 {
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
     /**
      * Handles an Install Tool request when nothing is there
      *
@@ -49,7 +60,8 @@ class Installer implements MiddlewareInterface
             return $handler->handle($request);
         }
 
-        $controller = new InstallerController();
+        // Lazy load InstallerController, to instantiate the class and the dependencies only if we handle an install request.
+        $controller = $this->container->get(InstallerController::class);
         $actionName = $request->getParsedBody()['install']['action'] ?? $request->getQueryParams()['install']['action'] ?? 'init';
         $action = $actionName . 'Action';
 
diff --git a/typo3/sysext/install/Classes/Middleware/Maintenance.php b/typo3/sysext/install/Classes/Middleware/Maintenance.php
index fa74623335c2..5e57ac140c4c 100644
--- a/typo3/sysext/install/Classes/Middleware/Maintenance.php
+++ b/typo3/sysext/install/Classes/Middleware/Maintenance.php
@@ -126,14 +126,14 @@ class Maintenance implements MiddlewareInterface
                 'isAuthorized' => $session->isAuthorized()
             ]);
         } elseif ($actionName === 'init') {
-            $controller = new LayoutController();
+            $controller = $this->container->get(LayoutController::class);
             $response = $controller->initAction($request);
         } elseif ($actionName === 'checkEnableInstallToolFile') {
             $response = new JsonResponse([
                 'success' => $this->checkEnableInstallToolFile(),
             ]);
         } elseif ($actionName === 'showEnableInstallToolFile') {
-            $controller = new LoginController();
+            $controller = $this->container->get(LoginController::class);
             $response = $controller->showEnableInstallToolFileAction($request);
         } elseif ($actionName === 'checkLogin') {
             if (!$this->checkEnableInstallToolFile() && !$session->isAuthorizedBackendUserSession()) {
@@ -156,7 +156,7 @@ class Maintenance implements MiddlewareInterface
             if (!$this->checkEnableInstallToolFile()) {
                 throw new \RuntimeException('Not authorized', 1505564888);
             }
-            $controller = new LoginController();
+            $controller = $this->container->get(LoginController::class);
             $response = $controller->showLoginAction($request);
         } elseif ($actionName === 'login') {
             if (!$this->checkEnableInstallToolFile()) {
@@ -221,7 +221,7 @@ class Maintenance implements MiddlewareInterface
             $this->recreatePackageStatesFileIfMissing();
             $className = $this->controllers[$controllerName];
             /** @var AbstractController $controller */
-            $controller = $this->container->has($className) ? $this->container->get($className) : new $className();
+            $controller = $this->container->get($className);
             if (!method_exists($controller, $action)) {
                 throw new \RuntimeException(
                     'Unknown action method ' . $action . ' in controller ' . $controllerName,
diff --git a/typo3/sysext/install/Classes/Service/CoreUpdateService.php b/typo3/sysext/install/Classes/Service/CoreUpdateService.php
index 44ecd2af2279..b5240a8e175a 100644
--- a/typo3/sysext/install/Classes/Service/CoreUpdateService.php
+++ b/typo3/sysext/install/Classes/Service/CoreUpdateService.php
@@ -38,7 +38,7 @@ use TYPO3\CMS\Install\FolderStructure\DefaultFactory;
 class CoreUpdateService
 {
     /**
-     * @var \TYPO3\CMS\Install\Service\CoreVersionService
+     * @var CoreVersionService
      */
     protected $coreVersionService;
 
@@ -68,12 +68,9 @@ class CoreUpdateService
      */
     protected $downloadBaseUri;
 
-    /**
-     * @param CoreVersionService $coreVersionService
-     */
-    public function __construct(CoreVersionService $coreVersionService = null)
+    public function __construct(CoreVersionService $coreVersionService)
     {
-        $this->coreVersionService = $coreVersionService ?: GeneralUtility::makeInstance(CoreVersionService::class);
+        $this->coreVersionService = $coreVersionService;
         $this->setDownloadTargetPath(Environment::getVarPath() . '/transient/');
         $this->symlinkToCoreFiles = $this->discoverCurrentCoreSymlink();
         $this->downloadBaseUri = 'https://get.typo3.org';
diff --git a/typo3/sysext/install/Classes/Service/CoreVersionService.php b/typo3/sysext/install/Classes/Service/CoreVersionService.php
index 26521463812f..1150d919d094 100644
--- a/typo3/sysext/install/Classes/Service/CoreVersionService.php
+++ b/typo3/sysext/install/Classes/Service/CoreVersionService.php
@@ -18,7 +18,6 @@ declare(strict_types=1);
 namespace TYPO3\CMS\Install\Service;
 
 use TYPO3\CMS\Core\Information\Typo3Version;
-use TYPO3\CMS\Core\Registry;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -27,11 +26,6 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  */
 class CoreVersionService
 {
-    /**
-     * @var \TYPO3\CMS\Core\Registry
-     */
-    protected $registry;
-
     /**
      * Base URI for TYPO3 Version REST api
      *
@@ -39,14 +33,6 @@ class CoreVersionService
      */
     protected $apiBaseUrl = 'https://get.typo3.org/v1/api/';
 
-    /**
-     * Initialize update URI
-     */
-    public function __construct()
-    {
-        $this->registry = GeneralUtility::makeInstance(Registry::class);
-    }
-
     /**
      * Development git checkout versions always end with '-dev'. They are
      * not "released" as such and can not be updated.
diff --git a/typo3/sysext/install/Classes/Service/ExtensionConfigurationService.php b/typo3/sysext/install/Classes/Service/ExtensionConfigurationService.php
index 0570d6262482..aa05c1b07b78 100644
--- a/typo3/sysext/install/Classes/Service/ExtensionConfigurationService.php
+++ b/typo3/sysext/install/Classes/Service/ExtensionConfigurationService.php
@@ -21,7 +21,6 @@ use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExis
 use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
 use TYPO3\CMS\Core\Package\PackageManager;
 use TYPO3\CMS\Core\TypoScript\Parser\ConstantConfigurationParser;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Service to prepare extension configuration settings from ext_conf_template.txt
@@ -35,15 +34,22 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  */
 class ExtensionConfigurationService
 {
+    /**
+     * @var PackageManager
+     */
+    private $packageManager;
 
     /**
-     * @var \TYPO3\CMS\Core\TypoScript\Parser\ConstantConfigurationParser
+     * @var ConstantConfigurationParser
      */
     private $configurationParser;
 
-    public function __construct(ConstantConfigurationParser $configurationParser = null)
-    {
-        $this->configurationParser = $configurationParser ?? GeneralUtility::makeInstance(ConstantConfigurationParser::class);
+    public function __construct(
+        PackageManager $packageManager,
+        ConstantConfigurationParser $configurationParser
+    ) {
+        $this->packageManager = $packageManager;
+        $this->configurationParser = $configurationParser;
     }
     /**
      * Compiles ext_conf_template file and merges it with values from LocalConfiguration['EXTENSIONS'].
@@ -54,7 +60,7 @@ class ExtensionConfigurationService
      */
     public function getConfigurationPreparedForView(string $extensionKey): array
     {
-        $package = GeneralUtility::makeInstance(PackageManager::class)->getPackage($extensionKey);
+        $package = $this->packageManager->getPackage($extensionKey);
         if (!@is_file($package->getPackagePath() . 'ext_conf_template.txt')) {
             return [];
         }
diff --git a/typo3/sysext/install/Classes/Service/LanguagePackService.php b/typo3/sysext/install/Classes/Service/LanguagePackService.php
index ff50efe3c18b..59fc0d15d751 100644
--- a/typo3/sysext/install/Classes/Service/LanguagePackService.php
+++ b/typo3/sysext/install/Classes/Service/LanguagePackService.php
@@ -69,12 +69,12 @@ class LanguagePackService implements LoggerAwareInterface
     ];
     private const LANGUAGE_PACK_URL = 'https://localize.typo3.org/xliff/';
 
-    public function __construct(EventDispatcherInterface $eventDispatcher = null, RequestFactory $requestFactory = null)
+    public function __construct(EventDispatcherInterface $eventDispatcher, RequestFactory $requestFactory)
     {
-        $this->eventDispatcher = $eventDispatcher ?? GeneralUtility::getContainer()->get(EventDispatcherInterface::class);
+        $this->eventDispatcher = $eventDispatcher;
         $this->locales = GeneralUtility::makeInstance(Locales::class);
         $this->registry = GeneralUtility::makeInstance(Registry::class);
-        $this->requestFactory = $requestFactory ?? GeneralUtility::makeInstance(RequestFactory::class);
+        $this->requestFactory = $requestFactory;
     }
 
     /**
diff --git a/typo3/sysext/install/Classes/Service/LateBootService.php b/typo3/sysext/install/Classes/Service/LateBootService.php
index b85e860c8e9c..ac779dfa7ff8 100644
--- a/typo3/sysext/install/Classes/Service/LateBootService.php
+++ b/typo3/sysext/install/Classes/Service/LateBootService.php
@@ -114,6 +114,12 @@ class LateBootService
     /**
      * Bootstrap a non-failsafe container and load ext_localconf
      *
+     * Use by actions like the database analyzer and the upgrade wizards which
+     * need additional bootstrap actions performed.
+     *
+     * Those actions can potentially fatal if some old extension is loaded that triggers
+     * a fatal in ext_localconf or ext_tables code! Use only if really needed.
+     *
      * @param bool $resetContainer
      * @return ContainerInterface
      */
@@ -141,4 +147,9 @@ class LateBootService
 
         return $container;
     }
+
+    public function resetGlobalContainer(): void
+    {
+        $this->makeCurrent(null, []);
+    }
 }
diff --git a/typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php b/typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php
index 44ea736908fb..3e51c11a092b 100644
--- a/typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php
+++ b/typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php
@@ -43,7 +43,7 @@ use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException;
 class SilentConfigurationUpgradeService
 {
     /**
-     * @var \TYPO3\CMS\Core\Configuration\ConfigurationManager
+     * @var ConfigurationManager
      */
     protected $configurationManager;
 
@@ -157,9 +157,9 @@ class SilentConfigurationUpgradeService
         'SYS/systemLogLevel',
     ];
 
-    public function __construct(ConfigurationManager $configurationManager = null)
+    public function __construct(ConfigurationManager $configurationManager)
     {
-        $this->configurationManager = $configurationManager ?: GeneralUtility::makeInstance(ConfigurationManager::class);
+        $this->configurationManager = $configurationManager;
     }
 
     /**
diff --git a/typo3/sysext/install/Classes/ServiceProvider.php b/typo3/sysext/install/Classes/ServiceProvider.php
index e7279fa076b4..5226c5ffbf36 100644
--- a/typo3/sysext/install/Classes/ServiceProvider.php
+++ b/typo3/sysext/install/Classes/ServiceProvider.php
@@ -18,15 +18,25 @@ declare(strict_types=1);
 namespace TYPO3\CMS\Install;
 
 use Psr\Container\ContainerInterface;
+use Psr\EventDispatcher\EventDispatcherInterface;
 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
+use TYPO3\CMS\Core\Configuration\SiteConfiguration;
 use TYPO3\CMS\Core\Console\CommandRegistry;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
 use TYPO3\CMS\Core\DependencyInjection\ContainerBuilder;
 use TYPO3\CMS\Core\Http\MiddlewareDispatcher;
+use TYPO3\CMS\Core\Http\RequestFactory;
+use TYPO3\CMS\Core\Imaging\IconFactory;
+use TYPO3\CMS\Core\Imaging\IconRegistry;
+use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
+use TYPO3\CMS\Core\Localization\Locales;
 use TYPO3\CMS\Core\Middleware\NormalizedParamsAttribute as NormalizedParamsMiddleware;
 use TYPO3\CMS\Core\Package\AbstractServiceProvider;
+use TYPO3\CMS\Core\Package\FailsafePackageManager;
 use TYPO3\CMS\Core\Package\PackageManager;
+use TYPO3\CMS\Core\Registry;
+use TYPO3\CMS\Core\TypoScript\Parser\ConstantConfigurationParser;
 
 /**
  * @internal
@@ -43,11 +53,25 @@ class ServiceProvider extends AbstractServiceProvider
         return [
             Http\Application::class => [ static::class, 'getApplication' ],
             Http\NotFoundRequestHandler::class => [ static::class, 'getNotFoundRequestHandler' ],
-            Service\LateBootService::class => [ static::class, 'getLateBootService' ],
             Service\ClearCacheService::class => [ static::class, 'getClearCacheService' ],
+            Service\CoreUpdateService::class => [ static::class, 'getCoreUpdateService' ],
+            Service\CoreVersionService::class => [ static::class, 'getCoreVersionService' ],
+            Service\ExtensionConfigurationService::class => [ static::class, 'getExtensionConfigurationService' ],
+            Service\LanguagePackService::class => [ static::class, 'getLanguagePackService' ],
+            Service\LateBootService::class => [ static::class, 'getLateBootService' ],
             Service\LoadTcaService::class => [ static::class, 'getLoadTcaService' ],
+            Service\SilentConfigurationUpgradeService::class => [ static::class, 'getSilentConfigurationUpgradeService' ],
+            Service\Typo3tempFileService::class => [ static::class, 'getTypo3tempFileService' ],
+            Service\UpgradeWizardsService::class => [ static::class, 'getUpgradeWizardsService' ],
+            Middleware\Installer::class => [ static::class, 'getInstallerMiddleware' ],
             Middleware\Maintenance::class => [ static::class, 'getMaintenanceMiddleware' ],
             Controller\EnvironmentController::class => [ static::class, 'getEnvironmentController' ],
+            Controller\IconController::class => [ static::class, 'getIconController' ],
+            Controller\InstallerController::class => [ static::class, 'getInstallerController' ],
+            Controller\LayoutController::class => [ static::class, 'getLayoutController' ],
+            Controller\LoginController::class => [ static::class, 'getLoginController' ],
+            Controller\MaintenanceController::class => [ static::class, 'getMaintenanceController' ],
+            Controller\SettingsController::class => [ static::class, 'getSettingsController' ],
             Controller\UpgradeController::class => [ static::class, 'getUpgradeController' ],
             Command\LanguagePackCommand::class => [ static::class, 'getLanguagePackCommand' ],
             Command\UpgradeWizardRunCommand::class => [ static::class, 'getUpgradeWizardRunCommand' ],
@@ -80,6 +104,41 @@ class ServiceProvider extends AbstractServiceProvider
         return new Http\NotFoundRequestHandler();
     }
 
+    public static function getClearCacheService(ContainerInterface $container): Service\ClearCacheService
+    {
+        return new Service\ClearCacheService(
+            $container->get(Service\LateBootService::class)
+        );
+    }
+
+    public static function getCoreUpdateService(ContainerInterface $container): Service\CoreUpdateService
+    {
+        return new Service\CoreUpdateService(
+            $container->get(Service\CoreVersionService::class)
+        );
+    }
+
+    public static function getCoreVersionService(ContainerInterface $container): Service\CoreVersionService
+    {
+        return new Service\CoreVersionService();
+    }
+
+    public static function getExtensionConfigurationService(ContainerInterface $container): Service\ExtensionConfigurationService
+    {
+        return new Service\ExtensionConfigurationService(
+            $container->get(PackageManager::class),
+            $container->get(ConstantConfigurationParser::class)
+        );
+    }
+
+    public static function getLanguagePackService(ContainerInterface $container): Service\LanguagePackService
+    {
+        return new Service\LanguagePackService(
+            $container->get(EventDispatcherInterface::class),
+            $container->get(RequestFactory::class)
+        );
+    }
+
     public static function getLateBootService(ContainerInterface $container): Service\LateBootService
     {
         return new Service\LateBootService(
@@ -88,20 +147,38 @@ class ServiceProvider extends AbstractServiceProvider
         );
     }
 
-    public static function getClearCacheService(ContainerInterface $container): Service\ClearCacheService
+    public static function getLoadTcaService(ContainerInterface $container): Service\LoadTcaService
     {
-        return new Service\ClearCacheService($container->get(Service\LateBootService::class));
+        return new Service\LoadTcaService(
+            $container->get(Service\LateBootService::class)
+        );
     }
 
-    public static function getLoadTcaService(ContainerInterface $container): Service\LoadTcaService
+    public static function getSilentConfigurationUpgradeService(ContainerInterface $container): Service\SilentConfigurationUpgradeService
+    {
+        return new Service\SilentConfigurationUpgradeService(
+            $container->get(ConfigurationManager::class)
+        );
+    }
+    public static function getTypo3tempFileService(ContainerInterface $container): Service\Typo3tempFileService
+    {
+        return new Service\Typo3tempFileService();
+    }
+
+    public static function getUpgradeWizardsService(ContainerInterface $container): Service\UpgradeWizardsService
     {
-        return new Service\LoadTcaService($container->get(Service\LateBootService::class));
+        return new Service\UpgradeWizardsService();
+    }
+
+    public static function getInstallerMiddleware(ContainerInterface $container): Middleware\Installer
+    {
+        return new Middleware\Installer($container);
     }
 
     public static function getMaintenanceMiddleware(ContainerInterface $container): Middleware\Maintenance
     {
         return new Middleware\Maintenance(
-            $container->get(PackageManager::class),
+            $container->get(FailsafePackageManager::class),
             $container->get(ConfigurationManager::class),
             $container->get(PasswordHashFactory::class),
             $container
@@ -115,11 +192,66 @@ class ServiceProvider extends AbstractServiceProvider
         );
     }
 
+    public static function getIconController(ContainerInterface $container): Controller\IconController
+    {
+        return new Controller\IconController(
+            $container->get(IconRegistry::class),
+            $container->get(IconFactory::class)
+        );
+    }
+
+    public static function getInstallerController(ContainerInterface $container): Controller\InstallerController
+    {
+        return new Controller\InstallerController(
+            $container->get(Service\LateBootService::class),
+            $container->get(Service\SilentConfigurationUpgradeService::class),
+            $container->get(ConfigurationManager::class),
+            $container->get(SiteConfiguration::class),
+            $container->get(Registry::class),
+            $container->get(FailsafePackageManager::class)
+        );
+    }
+
+    public static function getLayoutController(ContainerInterface $container): Controller\LayoutController
+    {
+        return new Controller\LayoutController(
+            $container->get(Service\SilentConfigurationUpgradeService::class)
+        );
+    }
+
+    public static function getLoginController(ContainerInterface $container): Controller\LoginController
+    {
+        return new Controller\LoginController();
+    }
+
+    public static function getMaintenanceController(ContainerInterface $container): Controller\MaintenanceController
+    {
+        return new Controller\MaintenanceController(
+            $container->get(Service\LateBootService::class),
+            $container->get(Service\ClearCacheService::class),
+            $container->get(Service\LanguagePackService::class),
+            $container->get(Service\Typo3tempFileService::class),
+            $container->get(ConfigurationManager::class),
+            $container->get(PasswordHashFactory::class),
+            $container->get(Locales::class)
+        );
+    }
+
+    public static function getSettingsController(ContainerInterface $container): Controller\SettingsController
+    {
+        return new Controller\SettingsController(
+            $container->get(PackageManager::class),
+            $container->get(Service\ExtensionConfigurationService::class),
+            $container->get(LanguageServiceFactory::class)
+        );
+    }
+
     public static function getUpgradeController(ContainerInterface $container): Controller\UpgradeController
     {
         return new Controller\UpgradeController(
             $container->get(PackageManager::class),
-            $container->get(Service\LateBootService::class)
+            $container->get(Service\LateBootService::class),
+            $container->get(Service\UpgradeWizardsService::class)
         );
     }
 
@@ -130,12 +262,20 @@ class ServiceProvider extends AbstractServiceProvider
 
     public static function getUpgradeWizardRunCommand(ContainerInterface $container): Command\UpgradeWizardRunCommand
     {
-        return new Command\UpgradeWizardRunCommand('upgrade:run');
+        return new Command\UpgradeWizardRunCommand(
+            'upgrade:run',
+            $container->get(Service\LateBootService::class),
+            $container->get(Service\UpgradeWizardsService::class)
+        );
     }
 
     public static function getUpgradeWizardListCommand(ContainerInterface $container): Command\UpgradeWizardListCommand
     {
-        return new Command\UpgradeWizardListCommand('upgrade:list');
+        return new Command\UpgradeWizardListCommand(
+            'upgrade:list',
+            $container->get(Service\LateBootService::class),
+            $container->get(Service\UpgradeWizardsService::class)
+        );
     }
 
     public static function configureCommands(ContainerInterface $container, CommandRegistry $commandRegistry): CommandRegistry
diff --git a/typo3/sysext/install/Tests/Unit/Controller/UpgradeControllerTest.php b/typo3/sysext/install/Tests/Unit/Controller/UpgradeControllerTest.php
index 68dbd41cc607..449ecf66e549 100644
--- a/typo3/sysext/install/Tests/Unit/Controller/UpgradeControllerTest.php
+++ b/typo3/sysext/install/Tests/Unit/Controller/UpgradeControllerTest.php
@@ -18,10 +18,8 @@ declare(strict_types=1);
 namespace TYPO3\CMS\Install\Tests\Unit\Controller;
 
 use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Core\Package\PackageManager;
 use TYPO3\CMS\Fluid\View\StandaloneView;
 use TYPO3\CMS\Install\Controller\UpgradeController;
-use TYPO3\CMS\Install\Service\LateBootService;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
 /**
@@ -73,16 +71,9 @@ class UpgradeControllerTest extends UnitTestCase
             ],
         ]);
 
-        $packageManagerMock = $this->getMockBuilder(PackageManager::class)
-            ->disableOriginalConstructor()
-            ->getMock();
-        $lateBootServiceMock = $this->getMockBuilder(LateBootService::class)
-            ->disableOriginalConstructor()
-            ->getMock();
-
         /** @var UpgradeController|\PHPUnit\Framework\MockObject\MockObject $subject */
         $subject = $this->getMockBuilder(UpgradeController::class)
-            ->setConstructorArgs([$packageManagerMock, $lateBootServiceMock])
+            ->disableOriginalConstructor()
             ->setMethods(['getDocumentationFiles', 'initializeStandaloneView'])
             ->getMock();
 
-- 
GitLab