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