Drush Integration for your modules

By shane
Mon, 2011-05-23 14:00
9 comments

Share with Others

Drush is a very powerful command line utility for creating, managing, and maintaining your Drupal website. There are many things that you can do with Drush, but suffice it to say that if you develop/build Drupal websites and don't currently use Drush, you should most definitely check it out.

Note: If you are looking for help with Drush, check out this link - Drush: Getting started

Sometimes you may be building a module and need to make your Drupal site do things from the command line. In this case, writing custom Drush integration for your module will make your life easier when it comes to maintaining your Drupal site. Lets say you have some periodic (or sporadic) task that needs to be run, there are a few options.

  • Add the ability to perform the task from the Drupal administration back-end of the site
  • If it's a strictly scheduled task, you can use hook_cron() in your module to run code at specified intervals
  • Add Drush integration to your module to allow you to run the task from the command line

Each of the above has its place depending on the circumstances, however in this post we are only going to look at the Drush integration option. Below I will provide a very simple walkthrough for getting started with adding Drush integration to you module.

To add Drush integration to your custom module you need to start by creating a file in your module directory called "MYMODULE.drush.inc" (replacing MYMODULE with your module name). In this file we will need to declare a few hooks to register your command with Drush. The first hook is hook_drush_help():

/**
 * Implements hook_drush_help().
 */
function MYMODULE_drush_help($command) {
  switch ($command) {
    case 'drush:my-command':
      return dt('Run my command');
  }
}

This hook provides help to drush. The second hook we need to implement is hook_drush_command():

/**
 * Implements hook_drush_command().
 */
function MYMODULE_drush_command() {
  $items = array();
  $items['my-command'] = array(
    'description' => dt('Run my command.'),
    'arguments'   => array(
      'arg1'    => dt('An optional example argument'),
    ),
    'examples' => array(
      'Standard example' => 'drush my-command',
      'Argument example' => 'drush my-command 5',
    ),
    'aliases' => array('myc'),
  );
  return $items;
}

This hook defines the command called "my-command" to Drush. There are a few important things to note:

  • The dt() function is used to provide string translations. This is used in replacement of the Drupal t() function for Drush commands.
  • An array of arguments can be used to accept user input from the command line.
  • An array of examples can be added to provide additional help to the user.
  • Aliases are nice ways to shorten the Drush command. In the example created above, the drush my-command command can also be run using drush myc.

Now that we have the code in place, you should be able to view the help by running:


drush help my-command

or


drush help myc

Now it is time to write the callback function to specify exactly what we want this Drush command to do. This is where the bulk of your code will probably go. Often (but not always) this will include inserting, updating, or deleting data from the database. In this example I will just provide the skeleton code and you can fill in the function with the code you actually want to run for this Drush command.

/**
 * Callback function for drush my-command. 
 * Callback is called by using drush_hook_command() where
 * hook is the name of the module (MYMODULE) and command is the name of
 * the Drush command with all "-" characters converted to "_" characters (my_command)
 *
 * @param $arg1
 *   An optional argument
 */
function drush_MYMODULE_my_command($arg1 = NULL) {
  //check if the argument was passed in and just print it out
  if (isset($arg1)) {
   drush_print($arg1);
  }
 
  //log to the command line with an OK status
  drush_log('Running my-command', 'ok');
}

As you can see there are a few more Drush specific functions that you can use. I have provided a list of a few of them below with simple explanations.

drush_log('Log an event using drush', 'warning');
drush_set_error('Set an error with drush.');
dt('Translate strings with drush');
drush_print('Print to command line with drush');
drush_print_table($rows, TRUE); //print a command line table with drush
drush_confirm('Are you sure you want to continue?', $indent = 0); //Add drush confirmation

That should get you started adding Drush integration to your modules. Try it out and let me know if you run into any questions or need more detailed examples. I am beginning to migrate a lot of what I would normally put into hook_cron over to Drush commands so I can schedule it more flexibly using the server's cron. This works especially well for longer tasks as you can provide more memory and a higher time limit to command line PHP if required. This also provides the benefit of not creating additional overhead on the often overcrowded hook_cron().

Useful? Let me know what you think in the comments.

Comments

Calling a Drush function from a module might not be a good idea. When Drush runs, it is running from the command line and I think it might bootstrap Drupal differently. There are also Drush commands that wait for a response (like "drush cc"). How would you handle this?

Is this a Drush function that you are creating? If so, a better solution would be to just call a function from your module (in the [module-name].module file) inside the Drush function (in the drush.inc file). You could then also use that same function from within your module without any issues.

If you are trying to call a different drush function from a contributed module, look at what the Drush function actually does as many of them make use of reusable functions that you can simply implement in your module code.

If that still doesn't answer the question and you think you really need to call a drush function, perhaps you can explain your use case a little more in depth?

Very nice, well presented, clear, concise, a joy to follow with excellent examples. Five stars etc. Thank you.

To register options for your drush command you need to add define them in 'options' element of MYMODULE_drush_command $item['my-command']. According to the documentation (http://drush.ws/docs/commands.html) 'arguments' is only used by drush-help.

Then in your command callback you need to first get the option via drush_get_option('optionname'). It's not passed as an argument to the function header...

Updated Example:

/**
 * Implements hook_drush_command().
 */
function MYMODULE_drush_command() {
  $items = array();
  $items['my-command'] = array(
    // Used by drush-help
    'description' => dt('Run my command.'),
    'arguments'   => array(
      'arg1'    => dt('An optional example argument'),
    ),
    'examples' => array(
      'Standard example' => 'drush my-command',
      'Argument example' => 'drush my-command 5',
    ),
    //describe options
    'options' => array(
      'arg1' => array(
        'description' => dt('An optional example argument'),
        'example_value' => 5
        'value' => 'optional'
      ),
    ),
    'aliases' => array('myc'),
  );
  return $items;
}
 
/**
 * Callback function for drush my-command. 
 * Callback is called by using drush_hook_command() where
 * hook is the name of the module (MYMODULE) and command is the name of
 * the Drush command with all "-" characters converted to "_" characters (my_command)
 *
 * @param $arg1
 *   An optional argument
 */
function drush_MYMODULE_my_command() {
  // get arguement
  $arg1 = drush_get_option('arg1');
 
  //check if the argument was passed in and just print it out
  if (isset($arg1)) {
   drush_print($arg1);
  }
 
  //log to the command line with an OK status
  drush_log('Running my-command', 'ok');
}

According to the documentation, I think you are right... however the example you included also shows using the argument option (further down the page).

If you wanted to run the command using my example, it would be (using 5 as the argument)


drush myc 5

Using the example you provided, it would be


drush myc --arg1 5

If using your way, it would be best to remove the argument all together to prevent confusion. If you do have an argument, you will need to allow for that variable in the drush_MYMODULE_my_command function.

Overall, according the documentation it would seem as your way is the more correct approach. However, I do know my way works as far as functionality is concerned (that doesn't necessarily mean it is the correct way).

Perhaps some other Drush experts can weigh in here. If the way you provided is actually the preferred way, I will update my example.

Thanks for the comment.

Yes, Andrew, you can do it.

In the callback function drush_MYMODULE_my_command() just call a function of your module and there you go!

function drush_MYMODULE_my_command() {
  // call your function that is in your module
  myfunction_in_my_module();
 
  //log to the command line with an OK status
  drush_log('Running my-command with my function', 'ok');
}

While you can call commands directly from the drupal core, it's not always safe. One of the main reasons these are drush commands, not admin links, is their execution patterns. Some perform batch processing, not always compatible with a website's standard operation. Read the code carefully before attempting.

Can you please explain on the calling drush commands from module code?

I am working on a module which is supposed to call drush commands from the module code itself.

I have been using exec function. But is there any other way?

In reply to comment number 4
KarimB is calling a function which is defined in the module file maybe my_module.module file in the MYMODULE.drush.inc?
right?

Also what I want to do is call a function in the MYMODULE.drush.inc from my module as my_module.module?

how can we do that?
Please explain if it's even possible or not? I have been digging around a lot but only link i found regarding this was

https://drupal.org/node/2012028.

Is the information given here true? Kindly help.

Post new comment