function _drush_invoke_hooks

8.0.x _drush_invoke_hooks($command, $args)
6.x _drush_invoke_hooks($command, $args)
7.x _drush_invoke_hooks($command, $args)
5.x _drush_invoke_hooks($command, $args, $defined_in_commandfile = NULL)
master _drush_invoke_hooks($command, $args)

Invoke Drush API calls, including all hooks.

This is an internal function; it is called from drush_dispatch via drush_command, but only if the command does not specify a 'callback' function. If a callback function is specified, it will be called instead of drush_command + _drush_invoke_hooks.

Executes the specified command with the specified arguments on the currently bootstrapped site using the current option contexts. Note that _drush_invoke_hooks will not bootstrap any further than the current command has already bootstrapped; therefore, you should only invoke commands that have the same (or lower) bootstrap requirements.

Call the correct hook for all the modules that implement it. Additionally, the ability to rollback when an error has been encountered is also provided. If at any point during execution, the drush_get_error() function returns anything but 0, drush_invoke() will trigger $hook_rollback for each of the hooks that implement it, in reverse order from how they were executed. Rollbacks are also triggered any time a hook function returns FALSE.

This function will also trigger pre_$hook and post_$hook variants of the hook and its rollbacks automatically.


The name of the hook is composed from the name of the command and the name of the command file that the command definition is declared in. The general form for the hook filename is:


In many cases, drush commands that are functionally part of a common collection of similar commands will all be declared in the same file, and every command defined in that file will start with the same command prefix. For example, the command file "" defines commands such as "pm-enable" and "pm-disable". In the case of "pm-enable", the command file is "pm", and and command name is "pm-enable". When the command name starts with the same sequence of characters as the command file, then the repeated sequence is dropped; thus, the command hook for "pm-enable" is "drush_pm_enable", not "drush_pm_pm_enable".


command: The drush command to execute.

args: An array of arguments to the command OR a single non-array argument.

defined_in_commandfile: The name of the commandfile that this command exists in. Will be looked up if not provided.

Return value

The return value will be passed along to the caller if --backend option is present. A boolean FALSE indicates failure and rollback will be intitated.

This function should not be called directly.

See also

drush_invoke() and @see drush_invoke_process()

Related topics

1 call to _drush_invoke_hooks()
drush_command in includes/
Entry point for commands into the drush_invoke() API


includes/, line 278
The drush command engine.


function _drush_invoke_hooks($command, $args, $defined_in_commandfile = NULL) {
  if ($defined_in_commandfile == NULL) {
    $defined_in_commandfile = drush_get_commandfile_for_command($command);
  // If someone passed a standalone arg, convert it to a single-element array
  if (!is_array($args)) {
    $args = array($args);
  // Include the external command file used by this command, if there is one.
  // Generate the base name for the hook by converting all
  // dashes in the command name to underscores.
  $hook = str_replace("-", "_", $command);

  // Call the hook init function, if it exists.
  // If a command needs to bootstrap, it is advisable
  // to do so in _init; otherwise, new commandfiles
  // will miss out on participating in any stage that
  // has passed or started at the time it was discovered.
  $func = 'drush_' . $hook . '_init';
  if (function_exists($func)) {
    drush_log(dt("Calling drush command init function: !func", array('!func' => $func)), 'bootstrap');
    call_user_func_array($func, $args);
    if (drush_get_error()) {
      drush_log(dt('The command @command could not be initialized.', array('@command' => $command)), 'error');
      return FALSE;

  $rollback = FALSE;
  $completed = array();
  $available_rollbacks = array();
  $all_available_hooks = array();

  // Iterate through the different hook variations
  $variations = array(
    $hook . "_pre_validate",
    $hook . "_validate",
  foreach ($variations as $var_hook) {
    // Get the list of command files.
    // We re-fetch the list every time through
    // the loop in case one of the hook function
    // does something that will add additional
    // commandfiles to the list (i.e. bootstrapping
    // to a higher phase will do this).
    $list = drush_commandfile_list();

    // Run all of the functions available for this variation
    foreach ($list as $commandfile => $filename) {
      $func = sprintf("drush_%s_%s", $commandfile, $var_hook);
      if (($defined_in_commandfile == $commandfile) && ($commandfile . "_" == substr($var_hook . "_", 0, strlen($commandfile) + 1))) {
        $func = sprintf("drush_%s", $var_hook);
      if (function_exists($func)) {
        $all_available_hooks[] = $func . ' [* Defined in ' . $filename . ']';
        $available_rollbacks[] = $func . '_rollback';
        $completed[] = $func;
        $result = call_user_func_array($func, $args);
        // Only the 'main' callback can send data to backend.
        if ($var_hook == $hook) {
          // If the hook already called drush_backend_set_result,
          // then return that value. If it did not, then the return
          // value from the hook will be the value returned from
          // this routine.
          $return = drush_backend_get_result();
          if (empty($return)) {
            $return = $result;
        if (drush_get_error() || ($result === FALSE)) {
          $rollback = TRUE;
          // break out of the foreach variations and foreach list
          break 2;
      else {
        $all_available_hooks[] = $func;

  // If no hook functions were found, print a warning.
  if (empty($completed)) {
    $default_command_hook = sprintf("drush_%s_%s", $defined_in_commandfile, $hook);
    if (($defined_in_commandfile . "_" == substr($hook . "_", 0, strlen($defined_in_commandfile) + 1))) {
      $default_command_hook = sprintf("drush_%s", $hook);
    $dt_args = array(
      '!command' => $command,
      '!default_func' => $default_command_hook,
    $message = "No hook functions were found for !command. The primary hook function is !default_func(). Please implement this function. Run with --show-invoke to see all available hooks.";
    $return = drush_set_error('DRUSH_FUNCTION_NOT_FOUND', dt($message, $dt_args));
  if (drush_get_option('show-invoke')) {
    // We show all available hooks up to and including the one that failed (or all, if there were no failures)
    drush_log(dt("Available drush_invoke() hooks for !command: !available", array('!command' => $command, '!available' => "\n" . implode("\n", $all_available_hooks))), 'ok');
  if (drush_get_option('show-invoke') && !empty($available_rollbacks)) {
    drush_log(dt("Available rollback hooks for !command: !rollback", array('!command' => $command, '!rollback' => "\n" . implode("\n", $available_rollbacks))), 'ok');

  // Something went wrong, we need to undo.
  if ($rollback) {
    if (drush_get_option('confirm-rollback', FALSE)) {
      // Optionally ask for confirmation, --yes and --no are ignored from here on as we are about to finish this process.
      drush_set_context('DRUSH_AFFIRMATIVE', FALSE);
      drush_set_context('DRUSH_NEGATIVE', FALSE);
      $rollback = drush_confirm(dt('Do you want to rollback? (manual cleanup might be required otherwise)'));

    if ($rollback) {
      foreach (array_reverse($completed) as $func) {
        $rb_func = $func . '_rollback';
        if (function_exists($rb_func)) {
          call_user_func_array($rb_func, $args);
          drush_log(dt("Changes made in !func have been rolled back.", array('!func' => $func)), 'rollback');
    $return = FALSE;

  if (isset($return)) {
    return $return;