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.
For this recipe, you need to have Drush installed. We will be creating a command in a local user directory.
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. */
/** * Implements hook_drush_command(). **/ function disable_users_drush_command() { $items = []; $items['disable-users'] = [ 'description' => 'Disables users after 10 days of inactivity', ]; return $items; }
.drush.inc
./** * Implements drush_hook_COMMAND(). */ function drush_disable_users_disable_users() { }
disable_users.drush.inc
and our command is disable-users
, the hook turns out to be drush_disable_users_disable_users
.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'); }
system.date
configuration object to get the default time zone and properly construct a DateTime
object, modified 10 days ago./** * 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!'); } }
/** * 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)])); }
$ drush cache-clear drush
$ drush disable-users --help Disables users after 10 days of inactivity
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.
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.
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.