Skip to content

Commit

Permalink
Create a service adapter for drush.services.yml (#5553)
Browse files Browse the repository at this point in the history
* Add a basic Drush service adapter. Feed it from the existing drush.services.yml discovery in the Drush Drupal Kernel trait.

* Use our own service discovery class instead of Symfony DI container compiler pass to find commands and etc.

* Code style

* Fix module command discovery

* Fix typo in Console command handling

* Rename DrushServiceFinder to LegacyServiceFinder and add ServiceManager. Use ServiceManager to pass generators to the generate command.

* Code style

* Move ModuleGeneratorTest to the functional tests, because integration tests only bootstrap once

* Skip failing archive:restore test.

* Simplify LegacyServiceInstantiator; remove unnecessary containers.

* Code style

* Remove unused class DrushServiceModifier, and also remove some unused, already commented-out code.

* Remove container test, because Drush is no longer involved with container rebuilds.

* Move PSR-4 command discovery out of Generate command and into the ServiceManager class so that a referece to the autoloader is not needed (directly) by the Generate command.

* Remove unused FindCommandsCompilerPass

* Move command discovery to the service manager.

* Code style

* Fix typo in LegacyServiceFinder

* Use module handler instead of container.modules in DrupalBoot8.

* Move module discovery methods to the service manager

* Move discovery code out of DrupalKernelTrait and into LegacyServiceInstantiator.

* Code style

* Move bootstrap classes from Application to ServiceManager

* Declare FilterHooks in ServiceManager

* Explicitly instantiate command info alterers

* Add some docblock comments

* Throw Symfony ParameterNotFoundException when service cannot be initialized due to a missing required parameter.

* Typehints and docblock comments for service manager
  • Loading branch information
greg-1-anderson authored May 11, 2023
1 parent 047a3eb commit ce1042a
Show file tree
Hide file tree
Showing 17 changed files with 939 additions and 549 deletions.
138 changes: 11 additions & 127 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,14 @@

use Composer\Autoload\ClassLoader;
use Consolidation\AnnotatedCommand\AnnotatedCommand;
use Consolidation\AnnotatedCommand\CommandFileDiscovery;
use Consolidation\Filter\Hooks\FilterHooks;
use Consolidation\SiteAlias\SiteAliasManager;
use Drush\Boot\BootstrapManager;
use Drush\Boot\DrupalBootLevels;
use Drush\Command\RemoteCommandProxy;
use Drush\Commands\DrushCommands;
use Drush\Config\ConfigAwareTrait;
use Drush\Runtime\RedispatchHook;
use Drush\Runtime\TildeExpansionHook;
use Grasmash\YamlCli\Command\GetValueCommand;
use Grasmash\YamlCli\Command\LintCommand;
use Grasmash\YamlCli\Command\UnsetKeyCommand;
use Grasmash\YamlCli\Command\UpdateKeyCommand;
use Grasmash\YamlCli\Command\UpdateValueCommand;
use Drush\Runtime\ServiceManager;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Robo\ClassDiscovery\RelativeNamespaceDiscovery;
Expand Down Expand Up @@ -58,8 +51,8 @@ class Application extends SymfonyApplication implements LoggerAwareInterface, Co
/** @var TildeExpansionHook */
protected $tildeExpansionHook;

/** @var string[] */
protected array $bootstrapCommandClasses = [];
/** @var ServiceManager */
protected $serviceManager;

/**
* Add global options to the Application and their default values to Config.
Expand Down Expand Up @@ -137,6 +130,11 @@ public function setTildeExpansionHook(TildeExpansionHook $tildeExpansionHook)
$this->tildeExpansionHook = $tildeExpansionHook;
}

public function setServiceManager(ServiceManager $serviceManager)
{
$this->serviceManager = $serviceManager;
}

/**
* Return the framework uri selected by the user.
*/
Expand Down Expand Up @@ -184,11 +182,6 @@ public function selectUri($cwd)
return $this->bootstrapManager()->selectUri($cwd);
}

public function bootstrapCommandClasses(): array
{
return $this->bootstrapCommandClasses;
}

/**
* @inheritdoc
*/
Expand Down Expand Up @@ -325,28 +318,10 @@ public function configureAndRegisterCommands(InputInterface $input, OutputInterf
$this->configureIO($input, $output);

// Directly add the yaml-cli commands.
$this->addCommands($this->discoverYamlCliCommands());

$commandClasses = array_unique(array_merge(
$this->discoverCommandsFromConfiguration(),
$this->discoverCommands($commandfileSearchpath, '\Drush'),
$this->discoverPsr4Commands($classLoader),
[FilterHooks::class]
));

// If a command class has a static `create` method, then we will
// postpone instantiating it until after we bootstrap Drupal.
$this->bootstrapCommandClasses = array_filter($commandClasses, function (string $class): bool {
if (!method_exists($class, 'create')) {
return false;
}
$this->addCommands($this->serviceManager->instantiateYamlCliCommands());

$reflectionMethod = new \ReflectionMethod($class, 'create');
return $reflectionMethod->isStatic();
});

// Remove the command classes that we put into the bootstrap command classes.
$commandClasses = array_diff($commandClasses, $this->bootstrapCommandClasses);
// Find the command handlers that we can instantiate without bootstrapping Drupal
$commandClasses = $this->serviceManager->discover($commandfileSearchpath, '\Drush');

// Uncomment the lines below to use Console's built in help and list commands.
// unset($commandClasses[__DIR__ . '/Commands/help/HelpCommands.php']);
Expand All @@ -356,74 +331,6 @@ public function configureAndRegisterCommands(InputInterface $input, OutputInterf
Robo::register($this, $commandClasses);
}

protected function discoverCommandsFromConfiguration()
{
$commandList = [];
foreach ($this->config->get('drush.commands', []) as $key => $value) {
if (is_numeric($key)) {
$classname = $value;
$commandList[] = $classname;
} else {
$classname = ltrim($key, '\\');
$commandList[$value] = $classname;
}
}
$this->loadCommandClasses($commandList);
return array_values($commandList);
}

/**
* Ensure that any discovered class that is not part of the autoloader
* is, in fact, included.
*/
protected function loadCommandClasses($commandClasses)
{
foreach ($commandClasses as $file => $commandClass) {
if (!class_exists($commandClass)) {
include $file;
}
}
}

/**
* Discovers command classes.
*/
protected function discoverCommands(array $directoryList, string $baseNamespace): array
{
$discovery = new CommandFileDiscovery();
$discovery
->setIncludeFilesAtBase(true)
->setSearchDepth(3)
->ignoreNamespacePart('contrib', 'Commands')
->ignoreNamespacePart('custom', 'Commands')
->ignoreNamespacePart('src')
->setSearchLocations(['Commands', 'Hooks', 'Generators'])
->setSearchPattern('#.*(Command|Hook|Generator)s?.php$#');
$baseNamespace = ltrim($baseNamespace, '\\');
$commandClasses = $discovery->discover($directoryList, $baseNamespace);
$this->loadCommandClasses($commandClasses);
return array_values($commandClasses);
}

/**
* Discovers commands that are PSR4 auto-loaded.
*/
protected function discoverPsr4Commands(ClassLoader $classLoader): array
{
$classes = (new RelativeNamespaceDiscovery($classLoader))
->setRelativeNamespace('Drush\Commands')
->setSearchPattern('/.*DrushCommands\.php$/')
->getClasses();

return array_filter($classes, function (string $class): bool {
$reflectionClass = new \ReflectionClass($class);
return $reflectionClass->isSubclassOf(DrushCommands::class)
&& !$reflectionClass->isAbstract()
&& !$reflectionClass->isInterface()
&& !$reflectionClass->isTrait();
});
}

/**
* Renders a caught exception. Omits the command docs at end.
*/
Expand All @@ -443,27 +350,4 @@ public function renderThrowable(\Throwable $e, OutputInterface $output): void

$this->doRenderThrowable($e, $output);
}

private function discoverYamlCliCommands(): array
{
$classes_yaml = [
GetValueCommand::class,
LintCommand::class,
UpdateKeyCommand::class,
UnsetKeyCommand::class,
UpdateValueCommand::class
];

foreach ($classes_yaml as $class_yaml) {
/** @var Command $instance */
$instance = new $class_yaml();
// Namespace the commands.
$name = $instance->getName();
$instance->setName('yaml:' . $name);
$instance->setAliases(['y:' . $name]);
$instance->setHelp('See https://github.com/grasmash/yaml-cli for a README and bug reports.');
$instances[] = $instance;
}
return $instances;
}
}
Loading

0 comments on commit ce1042a

Please sign in to comment.