bootstrap_and_dispatch(); } } } // TODO: Get rid of global variable access here, and just trust // the bootstrap object returned from drush_preflight(). This will // require some adjustments to Drush bootstrapping. // See: https://github.com/drush-ops/drush/pull/1303 if ($bootstrap = drush_get_bootstrap_object()) { $bootstrap->terminate(); } drush_postflight(); if (is_object($return)) { $return = 0; } // How strict are we? If we are very strict, turn 'ok' into 'error' // if there are any warnings in the log. if (($return == 0) && (drush_get_option('strict') > 1) && drush_log_has_errors()) { $return = 1; } // After this point the drush_shutdown function will run, // exiting with the correct exit code. return $return; } /** * Prepare Drush for preflight. * * Runs before drush_main(). * * @see drush_main() * @see drush.php */ function drush_preflight_prepare() { define('DRUSH_BASE_PATH', dirname(dirname(__FILE__))); // Local means that autoload.php is inside of Drush. That is, Drush is its own Composer project. // Global means autoload.php is outside of Drush. That is, Drush is a dependency of a bigger project. $local_vendor_path = DRUSH_BASE_PATH . '/vendor/autoload.php'; $global_vendor_path = DRUSH_BASE_PATH . '/../../../vendor/autoload.php'; // Check for a local composer install or a global composer install. Vendor dirs are in different spots). if (file_exists($local_vendor_path)) { $vendor_path = $local_vendor_path; } elseif (file_exists($global_vendor_path)) { $vendor_path = $global_vendor_path; } else { $msg = "Unable to load autoload.php. Run composer install to fetch dependencies and write this file (http://docs.drush.org/en/master/install-alternative/). Or if you prefer, use the drush.phar which already has dependencies included (http://docs.drush.org/en/master/install).\n"; fwrite(STDERR, $msg); return FALSE; } $classloader = require $vendor_path; require_once DRUSH_BASE_PATH . '/includes/startup.inc'; require_once DRUSH_BASE_PATH . '/includes/bootstrap.inc'; require_once DRUSH_BASE_PATH . '/includes/environment.inc'; require_once DRUSH_BASE_PATH . '/includes/annotationcommand_adapter.inc'; require_once DRUSH_BASE_PATH . '/includes/command.inc'; require_once DRUSH_BASE_PATH . '/includes/drush.inc'; require_once DRUSH_BASE_PATH . '/includes/engines.inc'; require_once DRUSH_BASE_PATH . '/includes/backend.inc'; require_once DRUSH_BASE_PATH . '/includes/batch.inc'; require_once DRUSH_BASE_PATH . '/includes/context.inc'; require_once DRUSH_BASE_PATH . '/includes/sitealias.inc'; require_once DRUSH_BASE_PATH . '/includes/exec.inc'; require_once DRUSH_BASE_PATH . '/includes/drupal.inc'; require_once DRUSH_BASE_PATH . '/includes/output.inc'; require_once DRUSH_BASE_PATH . '/includes/cache.inc'; require_once DRUSH_BASE_PATH . '/includes/filesystem.inc'; require_once DRUSH_BASE_PATH . '/includes/dbtng.inc'; require_once DRUSH_BASE_PATH . '/includes/array_column.inc'; // Stash our vendor path and classloader. drush_set_context('DRUSH_VENDOR_PATH', dirname($vendor_path)); drush_set_context('DRUSH_CLASSLOADER', $classloader); // Can't log until we have a logger, so we'll create this ASAP. _drush_create_default_logger(); // Terminate immediately unless invoked as a command line script if (!drush_verify_cli()) { return drush_set_error('DRUSH_REQUIREMENTS_ERROR', dt('Drush is designed to run via the command line.')); } // Check supported version of PHP. // Note: If this is adjusted, check other code that compares // PHP_VERSION, such as drush_json_encode(), runserver/runserver.drush.inc, and also // adjust _drush_environment_check_php_ini() and the php_prohibited_options // list in the drush script. See http://drupal.org/node/1748228 define('DRUSH_MINIMUM_PHP', '5.4.5'); if (version_compare(phpversion(), DRUSH_MINIMUM_PHP) < 0 && !getenv('DRUSH_NO_MIN_PHP')) { return drush_set_error('DRUSH_REQUIREMENTS_ERROR', dt('Your command line PHP installation is too old. Drush requires at least PHP !version. To suppress this check, set the environment variable DRUSH_NO_MIN_PHP=1', array('!version' => DRUSH_MINIMUM_PHP))); } if (!$return = _drush_environment_check_php_ini()) { return; // An error was logged. } $drush_info = drush_read_drush_info(); define('DRUSH_VERSION', $drush_info['drush_version']); $version_parts = explode('.', DRUSH_VERSION); define('DRUSH_MAJOR_VERSION', $version_parts[0]); define('DRUSH_MINOR_VERSION', $version_parts[1]); define('DRUSH_REQUEST_TIME', microtime(TRUE)); drush_set_context('argc', $GLOBALS['argc']); drush_set_context('argv', $GLOBALS['argv']); // Set an error handler and a shutdown function set_error_handler('drush_error_handler'); register_shutdown_function('drush_shutdown'); // We need some global options/arguments processed at this early stage. drush_parse_args(); // Process initial global options such as --debug. _drush_preflight_global_options(); drush_log(dt("Drush preflight prepare loaded autoloader at !autoloader", array('!autoloader' => realpath($vendor_path))), LogLevel::PREFLIGHT); } /** * During the initialization of Drush, this is the first * step where we load our configuration and commandfiles, * and select the site we are going to operate on; however, * we take no irreversible actions (e.g. site bootstrapping). * This allows commands that are declared with no bootstrap * to select a new site root and bootstrap it. * * In this step we will register the shutdown function, * parse the command line arguments and store them in their * related contexts. * * Configuration files (drushrc.php) that are * a) Specified on the command line * b) Stored in the root directory of drush.php * c) Stored in the home directory of the system user. * * Additionally the DRUSH_QUIET and DRUSH_BACKEND contexts, * will be evaluated now, as they need to be set very early in * the execution flow to be able to take affect. * * @return \Drush\Boot\Boot; */ function drush_preflight() { // Create an alias '@none' to represent no Drupal site _drush_sitealias_cache_alias('@none', array('root' => '', 'uri' => '')); // Discover terminal width for pretty output. _drush_preflight_columns(); // Display is tidy now that column width has been handled. drush_log(dt('Starting Drush preflight.'), LogLevel::PREFLIGHT); // Statically define a way to call drush again. define('DRUSH_COMMAND', drush_find_drush()); // prime the CWD cache drush_cwd(); // Set up base environment for system-wide file locations. _drush_preflight_base_environment(); // Setup global alias_paths[] in context system. if (!drush_get_option('local')) { _drush_preflight_alias_path(); } if (!drush_get_option('local')) { // Load a drushrc.php file in the drush.php's directory. drush_load_config('drush'); // Load a drushrc.php file in the $ETC_PREFIX/etc/drush directory. drush_load_config('system'); // Load a drushrc.php file at ~/.drushrc.php. drush_load_config('user'); // Load a drushrc.php file in the ~/.drush directory. drush_load_config('home.drush'); } // Load a custom config specified with the --config option. drush_load_config('custom'); _drush_preflight_global_options(); // Load all the commandfiles findable from any of the // scopes listed above. _drush_find_commandfiles_drush(); // Look up the alias identifier that the user wants to use, // either via an argument or via 'site-set'. $target_alias = drush_sitealias_check_arg_and_site_set(); // Process the site alias that specifies which instance // of Drush (local or remote) this command will operate on. // We must do this after we load our config files (so that // site aliases are available), but before the rest of // Drush preflight and Drupal root bootstrap phase are // done, since site aliases may set option values that // affect these phases. $alias_record = _drush_sitealias_set_context_by_name($target_alias); // Find the selected site based on --root, --uri or cwd drush_preflight_root(); // Preflight the selected site, and load any configuration and commandfiles associated with it. drush_preflight_site(); // Check to see if anything changed during the 'site' preflight // that might allow us to find our alias record now if (empty($alias_record)) { $alias_record = _drush_sitealias_set_context_by_name($target_alias); // If the site alias settings changed late in the preflight, // then run the preflight for the root and site contexts again. if (!empty($alias_record)) { $remote_host = drush_get_option('remote-host'); if (!isset($remote_host)) { drush_preflight_root(); drush_preflight_site(); } } } // Fail if we could not find the selected site alias. if ($target_alias && empty($alias_record)) { // We will automatically un-set the site-set alias if it could not be found. // Otherwise, we'd be stuck -- the user would only be able to execute Drush // commands again after `drush @none site-set @none`, and most folks would // have a hard time figuring that out. $site_env = drush_sitealias_site_get(); if ($site_env == $target_alias) { drush_sitealias_site_clear(); } return drush_set_error('DRUSH_BOOTSTRAP_NO_ALIAS', dt("Could not find the alias !alias", array('!alias' => $target_alias))); } // If applicable swaps in shell alias values. drush_shell_alias_replace($target_alias); // Copy global options to their respective contexts _drush_preflight_global_options(); // Set environment variables based on #env-vars. drush_set_environment_vars($alias_record); // Select the bootstrap object and return it. return drush_select_bootstrap_class(); } /** * If --root is provided, set context. */ function drush_preflight_root() { $root = drush_get_option('root'); if (!isset($root)) { $root = drush_locate_root(); } if ($root) { $root = realpath($root); } // @todo This context name should not mention Drupal. // @todo Drupal code should use DRUSH_DRUPAL_ROOT instead of this constant. drush_set_context('DRUSH_SELECTED_DRUPAL_ROOT', $root); // Load the config options from Drupal's /drush, ../drush, and sites/all/drush directories, // even prior to bootstrapping the root. drush_load_config('drupal'); // Search for commandfiles in the root locations $discovery = annotationcommand_adapter_get_discovery(); $searchpath = [dirname($root) . '/drush', "$root/drush", "$root/sites/all/drush"]; $drush_root_extensions = $discovery->discover($searchpath, '\Drush'); drush_set_context( 'DRUSH_ANNOTATED_COMMANDFILES', array_merge( drush_get_context('DRUSH_ANNOTATED_COMMANDFILES'), $drush_root_extensions ) ); } function drush_preflight_site() { // Load the Drupal site configuration options upfront. drush_load_config('site'); // Determine URI and set constants/contexts accordingly. Keep this after loading of drupal,site configs. _drush_preflight_uri(); // If someone set 'uri' in the 'site' context, then copy it // to the 'process' context (to give it a higher priority // than the 'cli' and 'alias' contexts) and reset our selected // site and @self alias. $uri = drush_get_option('uri'); if ($uri != drush_get_option('uri', $uri, 'site')) { drush_set_option('uri', drush_get_option('uri', $uri, 'site')); _drush_preflight_uri(); } // Create a @self site alias record. drush_sitealias_create_self_alias(); } function _drush_preflight_global_options() { // Debug implies verbose $verbose = drush_get_option('verbose', FALSE); $debug = drush_get_option('debug', FALSE); drush_set_context('DRUSH_VERBOSE', $verbose || $debug); drush_set_context('DRUSH_DEBUG', $debug); drush_set_context('DRUSH_DEBUG_NOTIFY', $verbose && $debug); drush_set_context('DRUSH_SIMULATE', drush_get_option('simulate', FALSE)); // Backend implies affirmative unless negative is explicitly specified drush_set_context('DRUSH_NEGATIVE', drush_get_option('no', FALSE)); drush_set_context('DRUSH_AFFIRMATIVE', drush_get_option(array('yes', 'pipe'), FALSE) || (drush_get_context('DRUSH_BACKEND') && !drush_get_context('DRUSH_NEGATIVE'))); // Pipe implies quiet. drush_set_context('DRUSH_QUIET', drush_get_option(array('quiet', 'pipe'))); drush_set_context('DRUSH_PIPE', drush_get_option('pipe')); // Suppress colored logging if --nocolor option is explicitly given or if // terminal does not support it. $nocolor = (drush_get_option('nocolor', FALSE)); if (!$nocolor) { // Check for colorless terminal. If there is no terminal, then // 'tput colors 2>&1' will return "tput: No value for $TERM and no -T specified", // which is not numeric and therefore will put us in no-color mode. $colors = exec('tput colors 2>&1'); $nocolor = !($colors === FALSE || (is_numeric($colors) && $colors >= 3)); } drush_set_context('DRUSH_NOCOLOR', $nocolor); } /** * Sets up basic environment that controls where Drush looks for files on a * system-wide basis. Important to call for "early" functions that need to * work with unit tests. */ function _drush_preflight_base_environment() { // Copy ETC_PREFIX and SHARE_PREFIX from environment variables if available. // This alters where we check for server-wide config and alias files. // Used by unit test suite to provide a clean environment. if (getenv('ETC_PREFIX')) drush_set_context('ETC_PREFIX', getenv('ETC_PREFIX')); if (getenv('SHARE_PREFIX')) drush_set_context('SHARE_PREFIX', getenv('SHARE_PREFIX')); drush_set_context('DOC_PREFIX', DRUSH_BASE_PATH); if (!file_exists(DRUSH_BASE_PATH . '/README.md') && file_exists(drush_get_context('SHARE_PREFIX', '/usr') . '/share/doc/drush' . '/README.md')) { drush_set_context('DOC_PREFIX', drush_get_context('SHARE_PREFIX', '/usr') . '/share/doc/drush'); } $default_prefix_configuration = drush_is_windows() ? getenv('ALLUSERSPROFILE') . '/Drush' : ''; $default_prefix_commandfile = drush_is_windows() ? getenv('ALLUSERSPROFILE') . '/Drush' : '/usr'; $site_wide_configuration_dir = drush_get_context('ETC_PREFIX', $default_prefix_configuration) . '/etc/drush'; $site_wide_commandfile_dir = drush_get_context('SHARE_PREFIX', $default_prefix_commandfile) . '/share/drush/commands'; drush_set_context('DRUSH_SITE_WIDE_CONFIGURATION', $site_wide_configuration_dir); drush_set_context('DRUSH_SITE_WIDE_COMMANDFILES', $site_wide_commandfile_dir); $server_home = drush_server_home(); if (isset($server_home)) { drush_set_context('DRUSH_PER_USER_CONFIGURATION', $server_home . '/.drush'); } } /* * Set the terminal width, used for wrapping table output. * Normally this is exported using tput in the drush script. * If this is not present we do an additional check using stty here. * On Windows in CMD and PowerShell is this exported using mode con. */ function _drush_preflight_columns() { if (!($columns = getenv('COLUMNS'))) { // Trying to export the columns using stty. exec('stty size 2>&1', $columns_output, $columns_status); if (!$columns_status) $columns = preg_replace('/\d+\s(\d+)/', '$1', $columns_output[0], -1, $columns_count); // If stty fails and Drush us running on Windows are we trying with mode con. if (($columns_status || !$columns_count) && drush_is_windows()) { $columns_output = array(); exec('mode con', $columns_output, $columns_status); if (!$columns_status && is_array($columns_output)) { $columns = (int)preg_replace('/\D/', '', $columns_output[4], -1, $columns_count); } else { drush_log(dt('Drush could not detect the console window width. Set a Windows Environment Variable of COLUMNS to the desired width.'), LogLevel::WARNING); } } // Failling back to default columns value if (empty($columns)) { $columns = 80; } } // If a caller wants to reserve some room to add additional // information to the drush output via post-processing, the // --reserve-margin flag can be used to declare how much // space to leave out. This only affects drush functions // such as drush_print_table() that wrap the output. $columns -= drush_get_option('reserve-margin', 0); drush_set_context('DRUSH_COLUMNS', $columns); } function _drush_preflight_alias_path() { $alias_path =& drush_get_context('ALIAS_PATH'); $default_prefix_configuration = drush_is_windows() ? getenv('ALLUSERSPROFILE') . '/Drush' : ''; $site_wide_configuration_dir = drush_get_context('ETC_PREFIX', $default_prefix_configuration) . '/etc/drush'; $alias_path[] = drush_sitealias_alias_base_directory($site_wide_configuration_dir); $alias_path[] = drush_sitealias_alias_base_directory(dirname(__FILE__) . '/..'); $server_home = drush_server_home(); if (isset($server_home)) { $alias_path[] = drush_sitealias_alias_base_directory($server_home . '/.drush'); } } /* * Set root and uri. */ function _drush_preflight_root_uri() { drush_preflight_root(); _drush_preflight_uri(); } /** * If --uri is provided, set context. */ function _drush_preflight_uri() { $uri = drush_get_option('uri', ''); drush_set_context('DRUSH_SELECTED_URI', $uri); } function _drush_find_commandfiles_drush() { // Core commands shipping with Drush $searchpath[] = dirname(__FILE__) . '/../commands/'; // User commands, specified by 'include' option $include = drush_get_context('DRUSH_INCLUDE', array()); foreach ($include as $path) { if (is_dir($path)) { drush_log('Include ' . $path, LogLevel::NOTICE); $searchpath[] = $path; } } if (!drush_get_option('local')) { // System commands, residing in $SHARE_PREFIX/share/drush/commands $share_path = drush_get_context('DRUSH_SITE_WIDE_COMMANDFILES'); if (is_dir($share_path)) { $searchpath[] = $share_path; } // User commands, residing in ~/.drush $per_user_config_dir = drush_get_context('DRUSH_PER_USER_CONFIGURATION'); if (!empty($per_user_config_dir)) { $searchpath[] = $per_user_config_dir; } } // @todo the zero parameter is a bit weird here. It's $phase. _drush_add_commandfiles($searchpath, 0); // Also discover Drush's own annotation commands. $discovery = annotationcommand_adapter_get_discovery(); $annotation_commandfiles = $discovery->discover(DRUSH_BASE_PATH . '/lib/Drush', '\Drush'); // And, finally, search for commandfiles in the $searchpath $searchpath = array_map( function ($item) { if (strtolower(basename($item)) == 'commands') { return dirname($item); } return $item; }, $searchpath ); $global_drush_extensions = $discovery->discover($searchpath, '\Drush'); $annotation_commandfiles += $global_drush_extensions; drush_set_context('DRUSH_ANNOTATED_COMMANDFILES', $annotation_commandfiles); } /** * Handle any command preprocessing that may need to be done, including * potentially redispatching the command immediately (e.g. for remote * commands). * * @return * TRUE if the command was handled remotely. */ function drush_preflight_command_dispatch() { $interactive = drush_get_option('interactive', FALSE); // The command will be executed remotely if the --remote-host flag // is set; note that if a site alias is provided on the command line, // and the site alias references a remote server, then the --remote-host // option will be set when the site alias is processed. // @see drush_sitealias_check_arg_and_site_set and _drush_sitealias_set_context_by_name $remote_host = drush_get_option('remote-host'); $site_list = drush_get_option('site-list'); // Get the command early so that we can allow commands to directly handle remote aliases if they wish $command = drush_parse_command(); drush_command_default_options($command); // If the command sets the 'strict-option-handling' flag, then we will remove // any cli options that appear after the command name from the 'cli' context. // The cli options that appear before the command name are stored in the // 'DRUSH_GLOBAL_CLI_OPTIONS' context, so we will just overwrite the cli context // with this, after doing the neccessary fixup from short-form to long-form options. // After we do that, we put back any local drush options identified by $command['options']. if (is_array($command) && !empty($command['strict-option-handling'])) { $cli_options = drush_get_context('DRUSH_GLOBAL_CLI_OPTIONS', array()); // Now we are going to sort out any options that exist in $command['options']; // we will remove these from DRUSH_COMMAND_ARGS and put them back into the // cli options. $cli_context = drush_get_context('cli'); $remove_from_command_args = array(); foreach ($command['options'] as $option => $info) { if (array_key_exists($option, $cli_context)) { $cli_options[$option] = $cli_context[$option]; $remove_from_command_args[$option] = $option; } } if (!empty($remove_from_command_args)) { $drush_command_args = array(); foreach (drush_get_context('DRUSH_COMMAND_ARGS') as $arg) { if (!_drush_should_remove_command_arg($arg, $remove_from_command_args)) { $drush_command_args[] = $arg; } } drush_set_context('DRUSH_COMMAND_ARGS', $drush_command_args); } drush_expand_short_form_options($cli_options); drush_set_context('cli', $cli_options); _drush_preflight_global_options(); } $args = drush_get_arguments(); $command_name = array_shift($args); $root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT'); $local_drush = drush_get_option('drush-script'); if (empty($local_drush) && !empty($root)) { $local_drush = find_wrapper_or_launcher($root); } $is_local = drush_get_option('local'); $values = NULL; if (!empty($root) && !empty($local_drush) && empty($is_local)) { if (!drush_is_absolute_path($local_drush)) { $local_drush = $root . DIRECTORY_SEPARATOR . $local_drush; } $local_drush = realpath($local_drush); $this_drush = drush_find_drush(); // If there is a local Drush selected, and it is not the // same Drush that is currently running, redispatch to it. // We assume that if the current Drush is nested inside // the current Drupal root (or, more specifically, the // current Drupal root's parent), then it is a site-local Drush. // We avoid redispatching in that instance to prevent an // infinite loop. if (file_exists($local_drush) && !drush_is_nested_directory(dirname($root), $this_drush)) { $uri = drush_get_context('DRUSH_SELECTED_URI'); $aditional_options = array( 'root' => $root, ); if (!empty($uri)) { $aditional_options['uri'] = $uri; } // We need to chdir to the Drupal root here, for the // benefit of the Drush wrapper. chdir($root); $values = drush_do_command_redispatch(is_array($command) ? $command : $command_name, $args, NULL, NULL, $local_drush, TRUE, $aditional_options); } } // If the command sets the 'handle-remote-commands' flag, then we will short-circuit // remote command dispatching and site-list command dispatching, and always let // the command handler run on the local machine. if (is_array($command) && !empty($command['handle-remote-commands'])) { return FALSE; } if (isset($remote_host)) { $remote_user = drush_get_option('remote-user'); // Force interactive mode if there is a single remote target. #interactive is added by drush_do_command_redispatch $user_interactive = drush_get_option('interactive'); drush_set_option('interactive', TRUE); $values = drush_do_command_redispatch(is_array($command) ? $command : $command_name, $args, $remote_host, $remote_user, $user_interactive); } // If the --site-list flag is set, then we will execute the specified // command once for every site listed in the site list. if (isset($site_list)) { if (!is_array($site_list)) { $site_list = explode(',', $site_list); } $site_record = array('site-list' => $site_list); $args = drush_get_arguments(); if (!drush_get_context('DRUSH_SIMULATE') && !$interactive && !drush_get_context('DRUSH_AFFIRMATIVE') && !drush_get_context('DRUSH_QUIET')) { drush_print(dt("You are about to execute '!command' non-interactively (--yes forced) on all of the following targets:", array('!command' => implode(" ", $args)))); foreach ($site_list as $one_destination) { drush_print(dt(' !target', array('!target' => $one_destination))); } if (drush_confirm('Continue? ') === FALSE) { drush_user_abort(); return TRUE; } } $command_name = array_shift($args); $multi_options = drush_redispatch_get_options(); $backend_options = array(); if (drush_get_option('pipe') || drush_get_option('interactive')) { $backend_options['interactive'] = TRUE; } if (drush_get_option('no-label', FALSE)) { $backend_options['no-label'] = TRUE; } // If the user specified a format, try to look up the // default list separator for the specified format. // If the user did not specify a different label separator, // then pass in the default as an option, so that the // separator between the items in the list and the site // name will be consistent. $format = drush_get_option('format', FALSE); if ($format && !array_key_exists('label-separator', $multi_options)) { $formatter = drush_load_engine('outputformat', $format); if ($formatter) { $list_separator = $formatter->get_info('list-separator'); if ($list_separator) { $multi_options['label-separator'] = $list_separator; } } } $values = drush_invoke_process($site_record, $command_name, $args, $multi_options, $backend_options); } if (isset($values)) { if (is_array($values) && ($values['error_status'] > 0)) { // Force an error result code. Note that drush_shutdown() will still run. drush_set_context('DRUSH_EXECUTION_COMPLETED', TRUE); exit($values['error_status']); } return TRUE; } return FALSE; } /** * Look for instances of arguments or parameters that * start with "~/". Replace these with "$HOME/". * * Note that this function is called _after_ Drush does * its redispatch checks; tildes are passed through * unmodified on a redispatch, and are only expanded when * a command is handled locally. */ function drush_preflight_tilde_expansion(&$command) { // Skip tilde expansion for commands that use // stict option handling, or those that explicitly // turn it off via $command['tilde-expansion'] = FALSE. if ($command['tilde-expansion'] && !$command['strict-option-handling']) { $cli =& drush_get_context('cli'); $match = '#^~/#'; $replacement = drush_server_home() . '/'; foreach ($cli as $key => $value) { if (is_string($value) && preg_match($match, $value)) { $cli[$key] = preg_replace($match, $replacement, $value); } } $command['arguments'] = array_map(function($value) use($match, $replacement) { return is_string($value) ? preg_replace($match, $replacement, $value) : $value; } , $command['arguments']); } } /** * We set this context to let the shutdown function know we reached the end of drush_main(). * * @see drush_main() */ function drush_postflight() { drush_set_context("DRUSH_EXECUTION_COMPLETED", TRUE); } /** * Shutdown function for use while Drush and Drupal are bootstrapping and to return any * registered errors. * * The shutdown command checks whether certain options are set to reliably * detect and log some common Drupal initialization errors. * * If the command is being executed with the --backend option, the script * will return a json string containing the options and log information * used by the script. * * The command will exit with '1' if it was successfully executed, and the * result of drush_get_error() if it wasn't. */ function drush_shutdown() { // Mysteriously make $user available during sess_write(). Avoids a NOTICE. global $user; if (!drush_get_context('DRUSH_EXECUTION_COMPLETED', FALSE) && !drush_get_context('DRUSH_USER_ABORT', FALSE)) { $php_error_message = ''; if ($error = error_get_last()) { $php_error_message = "\n" . dt('Error: !message in !file, line !line', array('!message' => $error['message'], '!file' => $error['file'], '!line' => $error['line'])); } // We did not reach the end of the drush_main function, // this generally means somewhere in the code a call to exit(), // was made. We catch this, so that we can trigger an error in // those cases. drush_set_error("DRUSH_NOT_COMPLETED", dt("Drush command terminated abnormally due to an unrecoverable error.!message", array('!message' => $php_error_message))); // Attempt to give the user some advice about how to fix the problem _drush_postmortem(); } // @todo Ask the bootstrap object (or maybe dispatch) how far we got. $phase = drush_get_context('DRUSH_BOOTSTRAP_PHASE'); if (drush_get_context('DRUSH_BOOTSTRAPPING')) { switch ($phase) { case DRUSH_BOOTSTRAP_DRUPAL_FULL : ob_end_clean(); _drush_log_drupal_messages(); drush_set_error('DRUSH_DRUPAL_BOOTSTRAP_ERROR'); break; } } if (drush_get_context('DRUSH_BACKEND', FALSE)) { drush_backend_output(); } elseif (drush_get_context('DRUSH_QUIET', FALSE)) { ob_end_clean(); // If we are in pipe mode, emit the compact representation of the command, if available. if (drush_get_context('DRUSH_PIPE')) { drush_pipe_output(); } } // This way drush_return_status() will always be the last shutdown function (unless other shutdown functions register shutdown functions...) // and won't prevent other registered shutdown functions (IE from numerous cron methods) from running by calling exit() before they get a chance. register_shutdown_function('drush_return_status'); } /** * Shutdown function to save code coverage data. */ function drush_coverage_shutdown() { if ($file_name = drush_get_context('DRUSH_CODE_COVERAGE', FALSE)) { $data = xdebug_get_code_coverage(); xdebug_stop_code_coverage(); // If coverage dump file contains anything, merge in the old data before // saving. This happens if the current drush command invoked another drush // command. if (file_exists($file_name) && $content = file_get_contents($file_name)) { $merge_data = unserialize($content); if (is_array($merge_data)) { foreach ($merge_data as $file => $lines) { if (!isset($data[$file])) { $data[$file] = $lines; } else { foreach ($lines as $num => $executed) { if (!isset($data[$file][$num])) { $data[$file][$num] = $executed; } else { $data[$file][$num] = ($executed == 1 ? $executed : $data[$file][$num]); } } } } } } file_put_contents($file_name, serialize($data)); } } function drush_return_status() { // If a specific exit code was set, then use it. $exit_code = drush_get_context('DRUSH_EXIT_CODE'); if (empty($exit_code)) { $exit_code = (drush_get_error()) ? DRUSH_FRAMEWORK_ERROR : DRUSH_SUCCESS; } exit($exit_code); }