Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a service adapter for drush.services.yml #5553

Merged
merged 31 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8b5789f
Add a basic Drush service adapter. Feed it from the existing drush.se…
greg-1-anderson May 4, 2023
fb89bd4
Use our own service discovery class instead of Symfony DI container c…
greg-1-anderson May 5, 2023
6b47912
Code style
greg-1-anderson May 5, 2023
62a9682
Fix module command discovery
greg-1-anderson May 6, 2023
705f2fd
Fix typo in Console command handling
greg-1-anderson May 6, 2023
9e02f9a
Rename DrushServiceFinder to LegacyServiceFinder and add ServiceManag…
greg-1-anderson May 8, 2023
51834e0
Code style
greg-1-anderson May 8, 2023
f993888
Move ModuleGeneratorTest to the functional tests, because integration…
greg-1-anderson May 8, 2023
dc97db7
Merge branch '12.x' into drush-service-adapter
greg-1-anderson May 8, 2023
aa661d3
Skip failing archive:restore test.
greg-1-anderson May 8, 2023
ece54ea
Simplify LegacyServiceInstantiator; remove unnecessary containers.
greg-1-anderson May 8, 2023
674207b
Code style
greg-1-anderson May 8, 2023
2025668
Remove unused class DrushServiceModifier, and also remove some unused…
greg-1-anderson May 9, 2023
36cc20f
Remove container test, because Drush is no longer involved with conta…
greg-1-anderson May 9, 2023
dbcdbbb
Move PSR-4 command discovery out of Generate command and into the Ser…
greg-1-anderson May 9, 2023
da8f93b
Remove unused FindCommandsCompilerPass
greg-1-anderson May 9, 2023
170b5ad
Move command discovery to the service manager.
greg-1-anderson May 9, 2023
c74ec7e
Code style
greg-1-anderson May 9, 2023
077f123
Fix typo in LegacyServiceFinder
greg-1-anderson May 9, 2023
971aff9
Use module handler instead of container.modules in DrupalBoot8.
greg-1-anderson May 9, 2023
e5fad4e
Move module discovery methods to the service manager
greg-1-anderson May 9, 2023
c9428ba
Move discovery code out of DrupalKernelTrait and into LegacyServiceIn…
greg-1-anderson May 10, 2023
c979e3c
Code style
greg-1-anderson May 10, 2023
ebfa31f
Merge branch '12.x' into drush-service-adapter
greg-1-anderson May 11, 2023
503fa6f
Move bootstrap classes from Application to ServiceManager
greg-1-anderson May 11, 2023
d59b343
Declare FilterHooks in ServiceManager
greg-1-anderson May 11, 2023
f003f14
Explicitly instantiate command info alterers
greg-1-anderson May 11, 2023
b092149
Add some docblock comments
greg-1-anderson May 11, 2023
ce2ce5d
Throw Symfony ParameterNotFoundException when service cannot be initi…
greg-1-anderson May 11, 2023
d3998b7
Merge branch '12.x' into drush-service-adapter
greg-1-anderson May 11, 2023
c598710
Typehints and docblock comments for service manager
greg-1-anderson May 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 41 additions & 28 deletions src/Boot/DrupalBoot8.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
use Drupal\Core\Session\AnonymousUserSession;
use Drush\Config\ConfigLocator;
use Drush\Drupal\DrushLoggerServiceProvider;
use Drush\Drupal\DrushServiceModifier;
use Drush\Drush;
use Psr\Log\LoggerInterface;
use Symfony\Component\Filesystem\Path;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Consolidation\AnnotatedCommand\CommandFileDiscovery;
use Robo\Robo;
use Drush\Runtime\LegacyServiceInstantiator;
use Drush\Runtime\LegacyServiceFinder;

class DrupalBoot8 extends DrupalBoot implements AutoloaderAwareInterface
{
Expand All @@ -29,6 +30,10 @@ class DrupalBoot8 extends DrupalBoot implements AutoloaderAwareInterface
protected ?DrupalKernelInterface $kernel = null;
protected Request $request;

public function __construct(protected $serviceManager)
{
}

public function getRequest(): Request
{
return $this->request;
Expand Down Expand Up @@ -217,9 +222,6 @@ public function bootstrapDrupalConfiguration(BootstrapManager $manager, Annotati
$allow_dumping = $kernel !== Kernels::UPDATE;
/** @var DrupalKernelInterface kernel */
$this->kernel = $kernel_factory($request, $classloader, 'prod', $allow_dumping, $manager->getRoot());
// Include Drush services in the container.
// @see Drush\Drupal\DrupalKernel::addServiceModifier()
$this->kernel->addServiceModifier(new DrushServiceModifier());

// Unset drupal error handler and restore Drush's one.
restore_error_handler();
Expand Down Expand Up @@ -247,13 +249,22 @@ public function bootstrapDrupalFull(BootstrapManager $manager): void
public function addDrupalModuleDrushCommands($manager): void
{
$application = Drush::getApplication();
$drushContainer = Drush::getContainer();

$this->logger->debug(dt("Loading drupal module drush commands & etc.", []));

// We have to get the service command list from the container, because
// it is constructed in an indirect way during the container initialization.
// The upshot is that the list of console commands is not available
// until after $kernel->boot() is called.
$container = \Drupal::getContainer();

// Legacy service adapters for drush.services.yml files.
$serviceFinder = new LegacyServiceFinder(\Drupal::moduleHandler(), Drush::config());
$drushServiceFiles = $serviceFinder->getDrushServiceFiles();
$legacyServiceInstantiator = new LegacyServiceInstantiator($container);
$legacyServiceInstantiator->loadServiceFiles($drushServiceFiles);

// Find the containerless commands, generators and command info alterers
$bootstrapCommandClasses = $application->bootstrapCommandClasses();
$commandInfoAlterers = [];
Expand All @@ -265,37 +276,37 @@ public function addDrupalModuleDrushCommands($manager): void
$commandInfoAlterers = array_merge($commandInfoAlterers, $commandInfoAlterersInThisModule);
}

// Look up the generators from the legacy service instantiator and inject
// them into the service manager. The `generate` command will get the
// generators from the service manager.
$this->serviceManager->injectGenerators($legacyServiceInstantiator->taggedServices('drush.generator.v3'));

// Find the command info alterers in Drush services.
if ($container->has(DrushServiceModifier::DRUSH_COMMAND_INFO_ALTERER_SERVICES)) {
$serviceCommandInfoAltererList = $container->get(DrushServiceModifier::DRUSH_COMMAND_INFO_ALTERER_SERVICES);
$commandFactory = Drush::commandFactory();
$commandInfoAlterers = array_merge($commandInfoAlterers, $serviceCommandInfoAltererList->getCommandList());
}
$commandFactory = Drush::commandFactory();
$commandInfoAlterers = array_merge($commandInfoAlterers, $legacyServiceInstantiator->taggedServices('drush.command_info_alterer'));

// Set the command info alterers.
foreach ($serviceCommandInfoAltererList->getCommandList() as $altererHandler) {
// Set the command info alterers. We must do this prior to calling
// Robo::register to add any commands, as that is the point where the
// alteration will happen.
foreach ($commandInfoAlterers as $altererHandler) {
$commandFactory->addCommandInfoAlterer($altererHandler);
$this->logger->debug(dt('Commands are potentially altered in !class.', ['!class' => get_class($altererHandler)]));
}

// Register the Drush Symfony Console commands found in Drush services
if ($container->has(DrushServiceModifier::DRUSH_CONSOLE_SERVICES)) {
$serviceCommandList = $container->get(DrushServiceModifier::DRUSH_CONSOLE_SERVICES);
foreach ($serviceCommandList->getCommandList() as $command) {
$manager->inflect($command);
$this->logger->debug(dt('Add a command: !name', ['!name' => $command->getName()]));
$application->add($command);
}
$drushServicesConsoleCommands = $legacyServiceInstantiator->taggedServices('console.command');
foreach ($drushServicesConsoleCommands as $command) {
$manager->inflect($command);
$this->logger->debug(dt('Add a command: !name', ['!name' => $command->getName()]));
$application->add($command);
}

// Do the same thing with the annotation commands.
if ($container->has(DrushServiceModifier::DRUSH_COMMAND_SERVICES)) {
$serviceCommandList = $container->get(DrushServiceModifier::DRUSH_COMMAND_SERVICES);
foreach ($serviceCommandList->getCommandList() as $commandHandler) {
$manager->inflect($commandHandler);
$this->logger->debug(dt('Add a commandfile class: !name', ['!name' => get_class($commandHandler)]));
Robo::register($application, $commandHandler);
}
// Add annotation commands from drush.services.yml
$drushServicesCommandHandlers = $legacyServiceInstantiator->taggedServices('drush.command');
foreach ($drushServicesCommandHandlers as $commandHandler) {
$manager->inflect($commandHandler);
$this->logger->debug(dt('Add a commandfile class: !name', ['!name' => get_class($commandHandler)]));
Robo::register($application, $commandHandler);
}

// Finally, instantiate all of the classes we discovered in
Expand All @@ -309,13 +320,15 @@ public function addDrupalModuleDrushCommands($manager): void
// of double-instantiating Drush service commands, if anyone decided
// to put those in the same namespace (\Drupal\modulename\Drush\Commands)
if ($this->hasStaticCreateFactory($class)) {
$commandHandler = $class::create($container);
$commandHandler = $class::create($container, $drushContainer);
}
} catch (\Exception $e) {
}
// Fail silently if the command handler could not be
// instantiated, e.g. if it tries to fetch services from
// a module that has not been enabled.
// a module that has not been enabled. Note that Robo::register
// can accept either Annotated Command command handlers or
// Symfony Console Command objects.
if ($commandHandler) {
$manager->inflect($commandHandler);
Robo::register($application, $commandHandler);
Expand Down
6 changes: 2 additions & 4 deletions src/Drupal/Commands/generate/ApplicationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,8 @@ public function discover(): array
$global_generators = $this->discoverPsr4Generators();

$module_generators = [];
if ($this->container->has(DrushServiceModifier::DRUSH_GENERATOR_SERVICES)) {
$module_generators = $this->container->get(DrushServiceModifier::DRUSH_GENERATOR_SERVICES)->getCommandList(
);
}
$serviceManager = \Drush\Drush::service('service.manager');
$module_generators = $serviceManager->getGenerators();
greg-1-anderson marked this conversation as resolved.
Show resolved Hide resolved

$generators = [
new DrushCommandFile(),
Expand Down
18 changes: 17 additions & 1 deletion src/Drupal/DrupalKernelTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ trait DrupalKernelTrait
{
/** @var ServiceModifierInterface[] */
protected $serviceModifiers = [];
protected $serviceFinder;
protected $drushServiceYamls = [];

public function getDrushServiceFiles()
{
return $this->drushServiceYamls;
}

/**
* Add a service modifier to the container builder.
Expand Down Expand Up @@ -92,6 +99,11 @@ public function discoverServiceProviders()
// Let Drupal discover all of its service providers
parent::discoverServiceProviders();

$this->discoverDrushServiceProviders();
}

protected function discoverDrushServiceProviders()
{
// Add those Drush service providers from Drush core that
// need references to the Drupal DI container. This includes
// Drush commands, and those services needed by those Drush
Expand Down Expand Up @@ -209,7 +221,11 @@ protected function findAppropriateServicesFile($module, $services, $dir)
protected function addDrushServiceProvider($serviceProviderName, $serviceYmlPath = '')
{
if (($serviceYmlPath !== null) && file_exists($serviceYmlPath)) {
$this->serviceYamls['app'][$serviceProviderName] = $serviceYmlPath;
// Keep our own list of service files
$this->drushServiceYamls[$serviceProviderName] = $serviceYmlPath;
// This is how we used to add our drush.services.yml file
// to the Drush service container. This is no longer necessary.
//$this->serviceYamls['app'][$serviceProviderName] = $serviceYmlPath;
}
}
}
54 changes: 0 additions & 54 deletions src/Drupal/DrushServiceModifier.php

This file was deleted.

5 changes: 4 additions & 1 deletion src/Runtime/DependencyInjection.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ protected function addDrushServices($container, ClassLoader $loader, DrushDrupal
->addMethodCall('addDefaultSimplifiers', []);

// Add some of our own objects to the container
Robo::addShared($container, 'bootstrap.drupal8', 'Drush\Boot\DrupalBoot8');
Robo::addShared($container, 'service.manager', 'Drush\Runtime\ServiceManager')
->addArgument('loader');
Robo::addShared($container, 'bootstrap.drupal8', 'Drush\Boot\DrupalBoot8')
->addArgument('service.manager');
Robo::addShared($container, 'bootstrap.manager', 'Drush\Boot\BootstrapManager')
->addMethodCall('setDrupalFinder', [$drupalFinder]);
// TODO: Can we somehow add these via discovery (e.g. backdrop extension?)
Expand Down
58 changes: 58 additions & 0 deletions src/Runtime/LegacyServiceFinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Drush\Runtime;

use Drush\Log\Logger;
use League\Container\Container;
use Drush\Drush;
use Symfony\Component\Console\Application;
use Composer\Autoload\ClassLoader;
use Drush\Command\DrushCommandInfoAlterer;
use Psr\Container\ContainerInterface;
use League\Container\Container as DrushContainer;
use Drush\Drupal\DrupalKernelTrait;
use Drush\Config\DrushConfig;

/**
* Find drush.services.yml files.
*
* This discovery class is used solely for backwards compatability with
* Drupal modules that still use drush.services.ymls to define Drush
* Commands, Generators & etc.; this mechanism is deprecated, though.
* Modules should instead use the static factory `create` mechanism.
*/
class LegacyServiceFinder
{
// We get the discovery code we need from the DrupalKernelTrait.
// We could also just move the code from there to here eventually,
// as that trait won't be needed once this class is used exclusively.
use DrupalKernelTrait;

protected $drushServiceYamls = [];

public function __construct(protected $moduleHandler, protected DrushConfig $drushConfig)
{
}

public function getDrushServiceFiles()
{
if (empty($drushServiceYamls)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably it should be $this->drushServiceYamls

$this->discoverDrushServiceProviders();
}
return $this->drushServiceYamls;
}

protected function getModuleFileNames()
{
$modules = $this->moduleHandler->getModuleList();
$moduleFilenames = [];

foreach ($modules as $module => $extension) {
$moduleFilenames[$module] = $extension->getPathname();
}

return $moduleFilenames;
}
}
Loading