How to write custom Drush 9 commands for Drupal 8

Tags: Drupal 8, drupal development



Drush 9 is finally here with some much-awaited changes. Drush 9 commands are now classes and .inc files are obsolete. Drush commands will now be based on the Annotated Command format. This will change the fundamental structure of custom Drush commands. Drush 9.x is the only supported version of Drush in Drupal 8.4.x and later versions, so it is imperative to learn how to write custom Drush commands for Drush 9.

Alright, so how do we write custom Drush 9 commands for Drupal 8?

We will need to create four files:

1. drush9_example.info.yml

name: Drush 9 Commands Example
description: Provides examples for writing custom Drush 9 commands.
core: 8.x
type: module
package: Examples

 
This is the default info file for the module.

2. drush.services.yml

services:
  drush9_example.commands:
    class: \Drupal\drush9_example\Commands\Drush9ExampleCommands
    tags:
      - { name: drush.command }

 
Drush expects you to use a separate drush.services.yml file instead of the module’s usual services.yml file. If you use the default services.yml file, it will lead to a php error:

Fatal error: Class 'Drush\Commands\DrushCommands' not found in Drush9ExampleCommands

Create this file in the root of your directory and define the Drush commands service here. You can also use this file to inject dependencies into your class.

3. composer.json

{
  "name": "drupal/drush9_example",
  "description": "A example module for Drush 9 commands.",
  "type": "drupal-module",
  "autoload": {
    "psr-4": {
      "Drupal\\drush9_example\\": "src/"
    }
  },
  "extra": {
    "drush": {
      "services": {
        "drush.services.yml": "^9"
      }
    }
  }
}

 
The drush.services.yml file needs to be declared in your composer.json for Drush 9 commands to work properly. Here, you can also define multiple services files to be used for different versions of Drush. In Drush 9, defining the services files explicitly in the composer.json file is not mandatory, and if the file is not defined, the default drush.services.yml file will be loaded.

However, from Drush 10 onwards, the services section in the composer.json file must exist, and it should name the services file to be used. Therefore, it is recommended to declare the services file in Drush 9 too.

4. Drush9ExampleCommands.php

<?php

namespace Drupal\drush9_example\Commands;

use Drush\Commands\DrushCommands;

/**
 * A Drush commandfile.
 *
 * In addition to this file, you need a drush.services.yml
 * in root of your module, and a composer.json file that provides the name
 * of the services file to use.
 */
class Drush9ExampleCommands extends DrushCommands {
  /**
   * Echos back hello with the argument provided.
   *
   * @param string $name
   *   Argument provided to the drush command.
   *
   * @command drush9_example:hello
   * @aliases d9-hello
   * @options arr An option that takes multiple values.
   * @options msg Whether or not an extra message should be displayed to the user.
   * @usage drush9_example:hello akanksha --msg
   *   Display 'Hello Akanksha!' and a message.
   */
  public function hello($name, $options = ['msg' => FALSE]) {
    if ($options['msg']) {
      $this->output()->writeln('Hello ' . $name . '! This is your first Drush 9 command.');
    }
    else {
      $this->output()->writeln('Hello ' . $name . '!');
    }
  }

}

 
This file uses the Annotated method for commands, which means that each command is now a separate function with annotations to define its name, alias, arguments, etc. This file can can also be used to define hooks with @hook annotation. Some of the annotations available for use are:

@command: This annotation is used to define the Drush command. Make sure that you follow Symfony’s module:command structure for all your commands.

@aliases: An alias for your command.

@param: Defines the input parameters. For example, @param: integer $number

@option: Defines the options available for the commands. This should be an associative array where the name of the option is the key and the value could be - false, true, string, InputOption::VALUE_REQUIRED, InputOption::VALUE_OPTIONAL or an empty array. 

@default: Defines the default value for options.

@usage: Demonstrates how the command should be used. For example, @usage: mymodule:command --option

@hook: Defines a hook to be fired. The default format is @hook type target, where type determines when the hook is called and target determines where the hook is called.

For a complete list of all the hooks available and their usage, please refer to: https://github.com/consolidation/annotated-command

Your first custom Drush 9 Drupal 8 command is ready to be tested. Add the module to the modules directory of your Drupal installation and run ‘drush en -y drush9_example’. Once the module gets successfully installed, rebuild the Drupal cache and you should be able to use your new custom command.

Blog 28_1

Source code for the module: https://github.com/akanksha-hp/DrupalExamples/tree/master/drush9_example

Akanksha Singh, Drupal Engineer
Posted on May 16, 2018 1:43:38 AM by

Akanksha Singh, Drupal Engineer

Voracious bibliophile. When she's not working, she reads; when she's not reading, she works. 100+ books annually, eight-plus monthly, two weekly—cover to cover, no excuses.