Skip to content

Commit 98aabe6

Browse files
claudiu-cristeagreg-1-anderson
authored andcommitted
Allow registering of command info alterer services (#3447)
* Allow to register command info alterer services. * Remove unused statement. * Add debug logging. Document command info alter. * Remove LoggerAwareInterface, LoggerAwareTrait.
1 parent 0e953a3 commit 98aabe6

File tree

7 files changed

+109
-0
lines changed

7 files changed

+109
-0
lines changed

docs/commands.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ It is also possible to use [version ranges](https://getcomposer.org/doc/articles
4242

4343
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.
4444

45+
Altering Drush Command Info
46+
===========================
47+
48+
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.
49+
50+
In order to alter an existing command info, follow the next steps:
51+
52+
1. In the module that wants to alter a command info, add a service class that implements the `\Consolidation\AnnotatedCommand\CommandInfoAltererInterface`.
53+
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.
54+
1. In the class implement the alteration logic the `alterCommandInfo()` method.
55+
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.
56+
57+
For an example, see the alterer class provided by the testing 'woot' module: `tests/resources/modules/d8/woot/src/WootCommandInfoAlterer.php`.
58+
4559
Global Drush Commands
4660
==============================
4761

src/Boot/DrupalBoot8.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,17 @@ public function bootstrapDrupalFull()
192192
// The upshot is that the list of console commands is not available
193193
// until after $kernel->boot() is called.
194194
$container = \Drupal::getContainer();
195+
196+
// Set the command info alterers.
197+
if ($container->has(DrushServiceModifier::DRUSH_COMMAND_INFO_ALTERER_SERVICES)) {
198+
$serviceCommandInfoAltererlist = $container->get(DrushServiceModifier::DRUSH_COMMAND_INFO_ALTERER_SERVICES);
199+
$commandFactory = Drush::commandFactory();
200+
foreach ($serviceCommandInfoAltererlist->getCommandList() as $altererHandler) {
201+
$commandFactory->addCommandInfoAlterer($altererHandler);
202+
$this->logger->debug(dt('Commands are potentially altered in !class.', ['!class' => get_class($altererHandler)]));
203+
}
204+
}
205+
195206
$serviceCommandlist = $container->get(DrushServiceModifier::DRUSH_CONSOLE_SERVICES);
196207
if ($container->has(DrushServiceModifier::DRUSH_CONSOLE_SERVICES)) {
197208
foreach ($serviceCommandlist->getCommandList() as $command) {

src/Drupal/DrushServiceModifier.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class DrushServiceModifier implements ServiceModifierInterface
1212
const DRUSH_CONSOLE_SERVICES = 'drush.console.services';
1313
// Holds list of command classes implemented with annotated commands
1414
const DRUSH_COMMAND_SERVICES = 'drush.command.services';
15+
// Holds list of command info alterer classes.
16+
const DRUSH_COMMAND_INFO_ALTERER_SERVICES = 'drush.command_info_alterer.services';
1517
// Holds list of classes implementing Drupal Code Generator classes
1618
const DRUSH_GENERATOR_SERVICES = 'drush.generator.services';
1719

@@ -26,6 +28,8 @@ public function alter(ContainerBuilder $container)
2628
$container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_CONSOLE_SERVICES, 'console.command'));
2729
$container->register(self::DRUSH_COMMAND_SERVICES, 'Drush\Command\ServiceCommandlist');
2830
$container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_COMMAND_SERVICES, 'drush.command'));
31+
$container->register(self::DRUSH_COMMAND_INFO_ALTERER_SERVICES, 'Drush\Command\ServiceCommandlist');
32+
$container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_COMMAND_INFO_ALTERER_SERVICES, 'drush.command_info_alterer'));
2933
$container->register(self::DRUSH_GENERATOR_SERVICES, 'Drush\Command\ServiceCommandlist');
3034
$container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_GENERATOR_SERVICES, 'drush.generator'));
3135
}
@@ -42,6 +46,7 @@ public function check($container_definition)
4246
return
4347
isset($container_definition['services'][self::DRUSH_CONSOLE_SERVICES]) &&
4448
isset($container_definition['services'][self::DRUSH_COMMAND_SERVICES]) &&
49+
isset($container_definition['services'][self::DRUSH_COMMAND_INFO_ALTERER_SERVICES]) &&
4550
isset($container_definition['services'][self::DRUSH_GENERATOR_SERVICES]);
4651
}
4752
}

tests/CommandInfoAlterTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Unish;
4+
5+
use Webmozart\PathUtil\Path;
6+
7+
/**
8+
* @group commands
9+
*
10+
*/
11+
class CommandInfoAlterTest extends CommandUnishTestCase
12+
{
13+
use TestModuleHelperTrait;
14+
15+
/**
16+
* Tests command info alter.
17+
*/
18+
public function testCommandInfoAlter()
19+
{
20+
$this->setUpDrupal(1, true);
21+
$this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8'));
22+
$this->drush('pm-enable', ['woot']);
23+
$this->drush('woot:altered', [], ['help' => true, 'debug' => true]);
24+
$this->assertNotContains('woot-initial-alias', $this->getOutput());
25+
$this->assertContains('woot-new-alias', $this->getOutput());
26+
27+
// Check the debug messages.
28+
$this->assertContains('[debug] Commands are potentially altered in Drupal\woot\WootCommandInfoAlterer.', $this->getErrorOutput());
29+
$this->assertContains("[debug] Module 'woot' changed the alias of 'woot:altered' command into 'woot-new-alias' in Drupal\woot\WootCommandInfoAlterer::alterCommandInfo().", $this->getErrorOutput());
30+
31+
// Try to run the command with the initial alias.
32+
$this->drush('woot-initial-alias', [], [], null, null, self::EXIT_ERROR);
33+
// Run the command with the altered alias.
34+
$this->drush('woot-new-alias');
35+
}
36+
}

tests/resources/modules/d8/woot/drush.services.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,8 @@ services:
2121
arguments: ['@module_handler']
2222
tags:
2323
- { name: drush.generator }
24+
woot.command_info_alter:
25+
class: Drupal\woot\WootCommandInfoAlterer
26+
arguments: ['@logger.factory']
27+
tags:
28+
- { name: drush.command_info_alterer }

tests/resources/modules/d8/woot/src/Commands/WootCommands.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,14 @@ public function tryFormatters($options = ['format' => 'table', 'fields' => ''])
7070
];
7171
return new RowsOfFields($outputData);
7272
}
73+
74+
/**
75+
* This command info is altered.
76+
*
77+
* @command woot:altered
78+
* @aliases woot-initial-alias
79+
*/
80+
public function wootAltered()
81+
{
82+
}
7383
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Drupal\woot;
4+
5+
use Consolidation\AnnotatedCommand\CommandInfoAltererInterface;
6+
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
7+
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
8+
9+
class WootCommandInfoAlterer implements CommandInfoAltererInterface
10+
{
11+
/**
12+
* @var \Drupal\Core\Logger\LoggerChannelInterface
13+
*/
14+
protected $logger;
15+
16+
public function __construct(LoggerChannelFactoryInterface $loggerFactory)
17+
{
18+
$this->logger = $loggerFactory->get('drush');
19+
}
20+
21+
public function alterCommandInfo(CommandInfo $commandInfo, $commandFileInstance)
22+
{
23+
if ($commandInfo->getName() === 'woot:altered') {
24+
$commandInfo->setAliases('woot-new-alias');
25+
$this->logger->debug(dt("Module 'woot' changed the alias of 'woot:altered' command into 'woot-new-alias' in " . __METHOD__ . '().'));
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)