Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ It is also possible to use [version ranges](https://getcomposer.org/doc/articles

In Drush 9, the default services file, `drush.services.yml`, will be used in instances where there is no `services` section in the Drush extras of the project's composer.json file. In Drush 10, however, the services section must exist, and must name the services file to be used. If a future Drush extension is written such that it only works with Drush 10 and later, then its entry would read `"drush.services.yml": "^10"`, and Drush 9 would not load the extension's commands. It is all the same recommended that Drush 9 extensions explicitly declare their services file with an appropriate version constraint.

Altering Drush Command Info
===========================

Drush command info (annotations) can be altered from other modules. This is done by creating and registering 'command info alterers'. Alterers are class services that are able to intercept and manipulate an existing command annotation.

In order to alter an existing command info, follow the next steps:

1. In the module that wants to alter a command info, add a service class that implements the `\Consolidation\AnnotatedCommand\CommandInfoAltererInterface`.
1. In the module `drush.services.yml` declare a service pointing to this class and tag the service with the `drush.command_info_alterer` tag.
1. In the class implement the alteration logic the `alterCommandInfo()` method.
1. Along with the alter code, it's strongly recommended to log a debug message explaining what exactly was altered. This would allow the easy debugging. Also it's a good practice to inject the the logger in the class constructor.

For an example, see the alterer class provided by the testing 'woot' module: `tests/resources/modules/d8/woot/src/WootCommandInfoAlterer.php`.

Global Drush Commands
==============================

Expand Down
11 changes: 11 additions & 0 deletions src/Boot/DrupalBoot8.php
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,17 @@ public function bootstrapDrupalFull()
// The upshot is that the list of console commands is not available
// until after $kernel->boot() is called.
$container = \Drupal::getContainer();

// Set the command info alterers.
if ($container->has(DrushServiceModifier::DRUSH_COMMAND_INFO_ALTERER_SERVICES)) {
$serviceCommandInfoAltererlist = $container->get(DrushServiceModifier::DRUSH_COMMAND_INFO_ALTERER_SERVICES);
$commandFactory = Drush::commandFactory();
foreach ($serviceCommandInfoAltererlist->getCommandList() as $altererHandler) {
$commandFactory->addCommandInfoAlterer($altererHandler);
Copy link
Member

Choose a reason for hiding this comment

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

We should probably write a debug log message if a module adds one of these.

$this->logger->debug(dt('Commands are potentially altered in !class.', ['!class' => get_class($altererHandler)]));
}
}

$serviceCommandlist = $container->get(DrushServiceModifier::DRUSH_CONSOLE_SERVICES);
if ($container->has(DrushServiceModifier::DRUSH_CONSOLE_SERVICES)) {
foreach ($serviceCommandlist->getCommandList() as $command) {
Expand Down
5 changes: 5 additions & 0 deletions src/Drupal/DrushServiceModifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class DrushServiceModifier implements ServiceModifierInterface
const DRUSH_CONSOLE_SERVICES = 'drush.console.services';
// Holds list of command classes implemented with annotated commands
const DRUSH_COMMAND_SERVICES = 'drush.command.services';
// Holds list of command info alterer classes.
const DRUSH_COMMAND_INFO_ALTERER_SERVICES = 'drush.command_info_alterer.services';
// Holds list of classes implementing Drupal Code Generator classes
const DRUSH_GENERATOR_SERVICES = 'drush.generator.services';

Expand All @@ -26,6 +28,8 @@ public function alter(ContainerBuilder $container)
$container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_CONSOLE_SERVICES, 'console.command'));
$container->register(self::DRUSH_COMMAND_SERVICES, 'Drush\Command\ServiceCommandlist');
$container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_COMMAND_SERVICES, 'drush.command'));
$container->register(self::DRUSH_COMMAND_INFO_ALTERER_SERVICES, 'Drush\Command\ServiceCommandlist');
$container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_COMMAND_INFO_ALTERER_SERVICES, 'drush.command_info_alterer'));
$container->register(self::DRUSH_GENERATOR_SERVICES, 'Drush\Command\ServiceCommandlist');
$container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_GENERATOR_SERVICES, 'drush.generator'));
}
Expand All @@ -42,6 +46,7 @@ public function check($container_definition)
return
isset($container_definition['services'][self::DRUSH_CONSOLE_SERVICES]) &&
isset($container_definition['services'][self::DRUSH_COMMAND_SERVICES]) &&
isset($container_definition['services'][self::DRUSH_COMMAND_INFO_ALTERER_SERVICES]) &&
isset($container_definition['services'][self::DRUSH_GENERATOR_SERVICES]);
}
}
36 changes: 36 additions & 0 deletions tests/CommandInfoAlterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Unish;

use Webmozart\PathUtil\Path;

/**
* @group commands
*
*/
class CommandInfoAlterTest extends CommandUnishTestCase
{
use TestModuleHelperTrait;

/**
* Tests command info alter.
*/
public function testCommandInfoAlter()
{
$this->setUpDrupal(1, true);
$this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8'));
$this->drush('pm-enable', ['woot']);
$this->drush('woot:altered', [], ['help' => true, 'debug' => true]);
$this->assertNotContains('woot-initial-alias', $this->getOutput());
$this->assertContains('woot-new-alias', $this->getOutput());

// Check the debug messages.
$this->assertContains('[debug] Commands are potentially altered in Drupal\woot\WootCommandInfoAlterer.', $this->getErrorOutput());
$this->assertContains("[debug] Module 'woot' changed the alias of 'woot:altered' command into 'woot-new-alias' in Drupal\woot\WootCommandInfoAlterer::alterCommandInfo().", $this->getErrorOutput());

// Try to run the command with the initial alias.
$this->drush('woot-initial-alias', [], [], null, null, self::EXIT_ERROR);
// Run the command with the altered alias.
$this->drush('woot-new-alias');
}
}
5 changes: 5 additions & 0 deletions tests/resources/modules/d8/woot/drush.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ services:
arguments: ['@module_handler']
tags:
- { name: drush.generator }
woot.command_info_alter:
class: Drupal\woot\WootCommandInfoAlterer
arguments: ['@logger.factory']
tags:
- { name: drush.command_info_alterer }
10 changes: 10 additions & 0 deletions tests/resources/modules/d8/woot/src/Commands/WootCommands.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,14 @@ public function tryFormatters($options = ['format' => 'table', 'fields' => ''])
];
return new RowsOfFields($outputData);
}

/**
* This command info is altered.
*
* @command woot:altered
* @aliases woot-initial-alias
*/
public function wootAltered()
{
}
}
28 changes: 28 additions & 0 deletions tests/resources/modules/d8/woot/src/WootCommandInfoAlterer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Drupal\woot;

use Consolidation\AnnotatedCommand\CommandInfoAltererInterface;
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;

class WootCommandInfoAlterer implements CommandInfoAltererInterface
{
/**
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;

public function __construct(LoggerChannelFactoryInterface $loggerFactory)
{
$this->logger = $loggerFactory->get('drush');
}

public function alterCommandInfo(CommandInfo $commandInfo, $commandFileInstance)
{
if ($commandInfo->getName() === 'woot:altered') {
$commandInfo->setAliases('woot-new-alias');
$this->logger->debug(dt("Module 'woot' changed the alias of 'woot:altered' command into 'woot-new-alias' in " . __METHOD__ . '().'));
}
}
}