Making a Drush command

Drush provides an API that allows developers to write their own commands. These commands can be part of a module and loaded through a Drupal installation, or they can be placed in the local user's Drush folder for general purposes.

Often, contributed modules create commands to automate user interface operations. However, creating a custom Drush command can be useful for specific operations. In this recipe, we will create a command that loads all the users who have not logged in in the last 10 days and resets their password.

Getting ready

For this recipe, you need to have Drush installed. We will be creating a command in a local user directory.

How to do it…

  1. Create a file named disable_users.drush.inc in the ~/.drush folder for your user:
    <?php
    
    /**
     * @file
     * Loads all users who have not logged in within 10 days and disables them.
     */
  2. Add the Drush command hook that will allow Drush to discover our commands provided by the file:
    /**
     * Implements hook_drush_command().
     **/
    function disable_users_drush_command() {
      $items = [];
      $items['disable-users'] = [
        'description' => 'Disables users after 10 days of inactivity',
      ];
      return $items;
    }
  3. This hook returns an array of command configurations; the hook should be prefixed with the part of the file before .drush.inc.
  4. Next, we will create the command callback function, which will end up holding all of our logic:
    /**
     * Implements drush_hook_COMMAND().
     */
    function drush_disable_users_disable_users() {
      
    }
  5. Since our filename is disable_users.drush.inc and our command is disable-users, the hook turns out to be drush_disable_users_disable_users.
  6. Update the function to create a DateTime object, representing 10 days ago. We will use this to generate a timestamp for our query:
    /**
     * Implements drush_hook_COMMAND().
     */
    function drush_disable_users_disable_users() {
      // Get the default timezone and make a DateTime object for 10 days ago.
      $system_date = Drupal::config('system.date');
      $default_timezone = $system_date->get('timezone.default') ?: date_default_timezone_get();
      $now = new DateTime('now', new DateTimeZone($default_timezone));
      $now->modify('-10 days');
    }
  7. We load the system.date configuration object to get the default time zone and properly construct a DateTime object, modified 10 days ago.
  8. Now, we will add our query, which will query all the user entities who have a login timestamp greater than 10 days:
    /**
     * Implements drush_hook_COMMAND().
     */
    function drush_disable_users_disable_users() {
      // Get the default timezone and make a DateTime object for 10 days ago.
      $system_date = Drupal::config('system.date');
      $default_timezone = $system_date->get('timezone.default') ?: date_default_timezone_get();
      $now = new DateTime('now', new DateTimeZone($default_timezone));
      $now->modify('-10 days');
    
      $query = Drupal::entityQuery('user')
        ->condition('login', $now->getTimestamp(), '>');
      $results = $query->execute();
    
      if (empty($results)) {
        drush_print('No users to disable!');
      }
    }
  9. If there are no results, an empty array will be returned.
  10. Next, we will iterate over the results and mark the user as disabled:
    /**
     * Implements drush_hook_COMMAND().
     */
    function drush_disable_users_disable_users() {
      // Get the default timezone and make a DateTime object for 10 days ago.
      $system_date = Drupal::config('system.date');
      $default_timezone = $system_date->get('timezone.default') ?: date_default_timezone_get();
      $now = new DateTime('now', new DateTimeZone($default_timezone));
      $now->modify('-10 days');
    
      $query = Drupal::entityQuery('user')
        ->condition('login', $now->getTimestamp(), '>');
      $results = $query->execute();
    
      if (empty($results)) {
        drush_print('No users to disable!');
      }
    
      foreach ($results as $uid) {
        /** @var DrupaluserEntityUser $user */
        $user = DrupaluserEntityUser::load($uid);
        $user->block();
        $user->save();
      }
    
      drush_print(dt('Disabled !count users', ['!count' => count($results)]));
    }
  11. The result is an array of user IDs. We loop over them to load the user, mark them as disabled, and then save them to commit the changes.
  12. Drush's cache will need to be cleared in order to discover your new command:
    $ drush cache-clear drush
    
  13. Check whether the command exists:
    $ drush disable-users --help
    Disables users after 10 days of inactivity
    

How it works…

Drush works by scanning specific directories for files that follow the COMMANDFILE.drush.inc pattern. You can think of COMMANDFILE for Drush as a representation of a module name in Drupal's hook system. When implementing a Drush hook, in the HOOK_drush format, you need to replace HOOK with your COMMANDFILE name, just as you would do in Drupal with a module name.

In this recipe, we created a disable_users.drush.inc file. This means that all hooks and commands in the file need to use disable_users for hook invocations. Drush uses this to load the hook_drush_command hook that returns our command information.

We then provide the functionality of our logic in the drush_hook_command hook. For this hook, we replace hook with our commandfile name. This was disable_users, giving us drush_disable_users_command. We replace command with the command that we defined in hook_drush_command, which was disable-users. We then have our final drush_disable_users_disable_users hook.

There's more…

Drush commands have additional options that can be specified in their definitions. We explore their abilities to control the required level of Drupal integration for a command.

Specifying the level of Drupal's bootstrap

Drush commands have the ability to specify the level of Drupal's bootstrap before being executed. Drupal has several bootstrap levels in which only specific parts of the system are loaded. By default, a command's bootstrap is at DRUSH_BOOTSTRAP_DRUPAL_LOGIN, which is at the same level as accessing Drupal over the web.

Commands, depending on their purpose, can choose to avoid bootstrapping Drupal at all or only until the database system is loaded. Drush commands that are utilities, such as the Git Release Notes module, provide a Drush command that does not interact with Drupal. It specifies a bootstrap of DRUSH_BOOTSTRAP_DRUSH, as it only interacts with repositories to generate change logs based on git tags and commits.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset