From d5b0279c0c058d43e6f3bbe667fb26edf1eb4254 Mon Sep 17 00:00:00 2001
From: Adam G-H <32250-phenaproxima@users.noreply.drupalcode.org>
Date: Wed, 1 Mar 2023 13:42:01 +0000
Subject: [PATCH] Issue #3345039 by phenaproxima, Wim Leers, effulgentsia:
 Remove dependency on symfony/config

---
 composer.json                                 |  3 +-
 .../src/PackageManagerServiceProvider.php     | 66 +++++++++++++++----
 2 files changed, 54 insertions(+), 15 deletions(-)

diff --git a/composer.json b/composer.json
index 4ea31fff95..3f2507aae9 100644
--- a/composer.json
+++ b/composer.json
@@ -15,8 +15,7 @@
     "drupal/core": "^9.7 || ^10",
     "php-tuf/composer-stager": "^1.2",
     "composer/composer": "^2.2.12 || ^2.3.5",
-    "composer-runtime-api": "^2.1",
-    "symfony/config": "^4.4 || ^6.1"
+    "composer-runtime-api": "^2.1"
   },
   "scripts": {
     "phpcbf": "scripts/phpcbf.sh",
diff --git a/package_manager/src/PackageManagerServiceProvider.php b/package_manager/src/PackageManagerServiceProvider.php
index 26115e0807..10e61fe0b9 100644
--- a/package_manager/src/PackageManagerServiceProvider.php
+++ b/package_manager/src/PackageManagerServiceProvider.php
@@ -7,13 +7,15 @@ namespace Drupal\package_manager;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DependencyInjection\ServiceProviderBase;
 use PhpTuf\ComposerStager\Domain\Core\Beginner\BeginnerInterface;
-use Symfony\Component\Config\FileLocator;
-use Symfony\Component\DependencyInjection\Definition;
-use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
 
 /**
  * Defines dynamic container services for Package Manager.
  *
+ * Scans the Composer Stager library and registers its classes in the Drupal
+ * service container.
+ *
+ * @todo Refactor this if/when symfony/config becomes a dependency: revert https://www.drupal.org/i/3345039.
+ *
  * @internal
  *   This is an internal part of Package Manager and may be changed or removed
  *   at any time without warning. External code should not interact with this
@@ -32,18 +34,56 @@ final class PackageManagerServiceProvider extends ServiceProviderBase {
     $mirror = new \ReflectionClass(BeginnerInterface::class);
     $path = dirname($mirror->getFileName(), 4);
 
-    // Recursively register all classes and interfaces under that directory,
-    // relative to the \PhpTuf\ComposerStager namespace.
-    $loader = new DirectoryLoader($container, new FileLocator());
-    // All the registered services should be auto-wired and private by default.
-    $default_definition = new Definition();
-    $default_definition->setAutowired(TRUE);
-    $default_definition->setPublic(FALSE);
-    $loader->registerClasses($default_definition, 'PhpTuf\ComposerStager\\', $path, [
-      // Ignore classes which we don't want to register as services.
+    // Certain subdirectories of Composer Stager shouldn't be scanned for
+    // services.
+    $ignore_directories = [
       $path . '/Domain/Exception',
       $path . '/Infrastructure/Value',
-    ]);
+    ];
+    // As we scan for services, compile a list of which classes implement which
+    // interfaces so that we can set up aliases for interfaces that are only
+    // implemented by one class (to facilitate autowiring).
+    $interfaces = [];
+
+    // Find all `.php` files in Composer Stager which aren't in the ignored
+    // directories.
+    $iterator = new \RecursiveDirectoryIterator($path, \FilesystemIterator::CURRENT_AS_SELF | \FilesystemIterator::SKIP_DOTS);
+    $iterator = new \RecursiveCallbackFilterIterator($iterator, static function (\SplFileInfo $current) use ($ignore_directories): bool {
+      if ($current->isDir()) {
+        return !in_array($current->getPathname(), $ignore_directories, TRUE);
+      }
+      return $current->getExtension() === 'php';
+    });
+    $iterator = new \RecursiveIteratorIterator($iterator);
+
+    /** @var \SplFileInfo $file */
+    foreach ($iterator as $file) {
+      // Convert the file name to a class name.
+      $class_name = substr($file->getPathname(), strlen($path) + 1, -4);
+      $class_name = 'PhpTuf\\ComposerStager\\' . str_replace(DIRECTORY_SEPARATOR, '\\', $class_name);
+
+      // Don't register interfaces and abstract classes as services.
+      $reflector = new \ReflectionClass($class_name);
+      if ($reflector->isInterface() || $reflector->isAbstract()) {
+        continue;
+      }
+      foreach ($reflector->getInterfaceNames() as $interface) {
+        $interfaces[$interface][] = $class_name;
+      }
+      // Register the class as an autowired, private service.
+      $container->register($class_name)
+        ->setClass($class_name)
+        ->setAutowired(TRUE)
+        ->setPublic(FALSE);
+    }
+
+    // Create aliases for interfaces that are only implemented by one class.
+    // Ignore interfaces that already have a service alias.
+    foreach ($interfaces as $interface_name => $implementations) {
+      if (count($implementations) === 1 && !$container->hasAlias($interface_name)) {
+        $container->setAlias($interface_name, $implementations[0]);
+      }
+    }
   }
 
 }
-- 
GitLab