From 941b8193a579bede9fd976b5e2af12dd291e860d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Chris=20M=C3=BCller?= <typo3@krue.ml>
Date: Mon, 7 Nov 2022 11:00:01 +0100
Subject: [PATCH] [FEATURE] Allow global services configuration

The Service Configuration for the dependency container
is now also checked in `config/system/services.yaml` or
`config/system/services.php`.

Note that this only works in Composer-based
installations where the project root is outside
the document root.

Resolves: #98912
Resolves: #98316
Releases: main
Change-Id: Id60b3614a59a28f38aafb372389002fc13b2d3ee
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/76449
Tested-by: core-ci <typo3@b13.com>
Tested-by: Susanne Moog <look@susi.dev>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Benjamin Franzke <bfr@qbus.de>
Reviewed-by: Susanne Moog <look@susi.dev>
Reviewed-by: Benni Mack <benni@typo3.org>
---
 .../DependencyInjection/ContainerBuilder.php  | 15 ++++++
 ...nstallation-wide-ServicesConfiguration.rst | 54 +++++++++++++++++++
 2 files changed, 69 insertions(+)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/12.1/Feature-98912-Installation-wide-ServicesConfiguration.rst

diff --git a/typo3/sysext/core/Classes/DependencyInjection/ContainerBuilder.php b/typo3/sysext/core/Classes/DependencyInjection/ContainerBuilder.php
index 352dd4557faa..f495df8cdc76 100644
--- a/typo3/sysext/core/Classes/DependencyInjection/ContainerBuilder.php
+++ b/typo3/sysext/core/Classes/DependencyInjection/ContainerBuilder.php
@@ -25,6 +25,7 @@ use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
 use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
+use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Package\Cache\PackageDependentCacheIdentifier;
 use TYPO3\CMS\Core\Package\PackageManager;
 
@@ -117,6 +118,20 @@ class ContainerBuilder
 
         $containerBuilder->addCompilerPass(new ServiceProviderCompilationPass($registry, $this->serviceProviderRegistryServiceName));
 
+        $globalConfigDir = Environment::getConfigPath();
+        // If the config folder is outside of the document root, we allow further services per-project
+        // This is usually the case in composer-based installations
+        if (Environment::isComposerMode() && Environment::getPublicPath() !== Environment::getProjectPath()) {
+            if (file_exists($globalConfigDir . '/system/services.php')) {
+                $phpFileLoader = new PhpFileLoader($containerBuilder, new FileLocator($globalConfigDir . '/system'));
+                $phpFileLoader->load('services.php');
+            }
+            if (file_exists($globalConfigDir . '/system/services.yaml')) {
+                $yamlFileLoader = new YamlFileLoader($containerBuilder, new FileLocator($globalConfigDir . '/system'));
+                $yamlFileLoader->load('services.yaml');
+            }
+        }
+
         $packages = $packageManager->getActivePackages();
         foreach ($packages as $package) {
             $diConfigDir = $package->getPackagePath() . 'Configuration/';
diff --git a/typo3/sysext/core/Documentation/Changelog/12.1/Feature-98912-Installation-wide-ServicesConfiguration.rst b/typo3/sysext/core/Documentation/Changelog/12.1/Feature-98912-Installation-wide-ServicesConfiguration.rst
new file mode 100644
index 000000000000..6b6eb388a08b
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.1/Feature-98912-Installation-wide-ServicesConfiguration.rst
@@ -0,0 +1,54 @@
+.. include:: /Includes.rst.txt
+
+.. _feature-98912-1667814888:
+
+==========================================================
+Feature: #98912 - Installation-wide services configuration
+==========================================================
+
+See :issue:`98912`
+
+Description
+===========
+
+With TYPO3 v12, it is possible to set up a global services configuration for a
+project that can be used in multiple project-specific extensions. This way you
+can, for example, alias an interface with a concrete implementation to be used in
+several extensions. It is also possible to register project-specific CLI commands
+without having the need for a project-specific extension.
+
+However, this only works - due to security restrictions - if TYPO3 is configured
+in a way that the project root is outside the document root, which usually
+happens in Composer-based installations.
+
+Impact
+======
+
+The global services configuration files :file:`services.yaml` and
+:file:`services.php` are now read within the the :file:`config/system/` path
+of a TYPO3 project in Composer-based installations.
+
+Example
+-------
+
+You want to use the interface of the PHP package `stella-maris/clock` as type
+hint for DI in the service classes of your project's various extensions. Then
+the concrete implementation may change without touching your code. In this
+example we use `lcobucci/clock` for the concrete implementation.
+
+..  code-block:: php
+    :caption: config/system/services.php
+
+    use Lcobucci\Clock\SystemClock;
+    use StellaMaris\Clock\ClockInterface;
+    use Symfony\Component\DependencyInjection\ContainerBuilder;
+    use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+
+    return static function (ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void {
+        $services = $containerConfigurator->services();
+        $services->set(ClockInterface::class)
+            ->factory([SystemClock::class, 'fromUTC']);
+    };
+
+
+.. index:: ext:core
-- 
GitLab