13.11.2025 | Peter Majmesku

Drush 12+: Simplified Drush commands with PHP 8 attributes

HelloWorldCommands.php
<?php

declare(strict_types=1);

namespace Drupal\nice_module\Drush\Commands;

use Drupal\Component\DependencyInjection\ContainerInterface;
use Drupal\nice_module\HelloWorldService;
use Drush\Attributes as CLI;
use Drush\Commands\DrushCommands;

/**
 * A Drush command class for saying "hello" to the world.
 */
final class HelloWorldCommands extends DrushCommands {

  public function __construct(
    private readonly HelloWorldService $helloWorldService,
  ) {
    parent::__construct();
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): self {
    return new static($container->get('nice_module.hello_world_service'));
  }

  #[CLI\Command(name: 'nice_module:say-hello', aliases: ['smci'])]
  #[CLI\Argument(name: 'userName', description: 'The name of the user.')]
  public function sayHello(?string $userName = NULL): void {
    $this->helloWorldService->sayHello($userName, $this->output());
    $this->logger()->success('I said "hello" to the user.');
  }

}

Issue:

Before Drush version 12 we needed to write a drush.services.yml file for having dependency injection. This leads to errors when we're installing new custom Drupal modules, because the modules *.services.yml file must be instantiated before the drush.services.yml file. That makes a workaround necessary, where we do rename the drush.services.yml file, before we rebuild the cache for having the services from our modules *.services.yml instantiated. Also the old approach was not leveraging PHP attributes, which are available since PHP 8.

Solution:

The snippet file is meant to be located here: web/modules/custom/nice_module/src/Drush/Commands/HelloWorldCommands.php

By this approach there are no errors on new module installation, where we do have a modules `*.services.yml` and `drush.service.yml`. 

Via PHP attributes our command, argument, and usage descriptions are also validated with PHP. This makes our Drush commands less error prone.