preflight.inc

  1. 8.0.x includes/preflight.inc
  2. 7.x includes/preflight.inc
  3. master includes/preflight.inc

Preflight, postflight and shutdown code.

Functions

Namesort descending Description
drush_coverage_shutdown Shutdown function to save code coverage data.
drush_postflight We set this context to let the shutdown function know we reached the end of drush_main().
drush_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…
drush_preflight_command_dispatch Handle any command preprocessing that may need to be done, including potentially redispatching the command immediately (e.g. for remote commands).
drush_preflight_prepare Prepare Drush for preflight.
drush_preflight_root If --root is provided, set context.
drush_preflight_site
drush_return_status
drush_shutdown Shutdown function for use while Drush and Drupal are bootstrapping and to return any registered errors.
_drush_find_commandfiles_drush
_drush_preflight_alias_path
_drush_preflight_base_environment 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.
_drush_preflight_columns
_drush_preflight_global_options
_drush_preflight_root_uri
_drush_preflight_uri If --uri is provided, set context.

File

includes/preflight.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Preflight, postflight and shutdown code.
  5. */
  6. use Drush\Log\LogLevel;
  7. /**
  8. * Prepare Drush for preflight.
  9. *
  10. * Runs before drush_main().
  11. *
  12. * @see drush_main()
  13. * @see drush.php
  14. */
  15. function drush_preflight_prepare() {
  16. define('DRUSH_BASE_PATH', dirname(dirname(__FILE__)));
  17. // Local means that autoload.php is inside of Drush. That is, Drush is its own Composer project.
  18. // Global means autoload.php is outside of Drush. That is, Drush is a dependency of a bigger project.
  19. $local_vendor_path = DRUSH_BASE_PATH . '/vendor/autoload.php';
  20. $global_vendor_path = DRUSH_BASE_PATH . '/../../../vendor/autoload.php';
  21. // Check for a local composer install or a global composer install. Vendor dirs are in different spots).
  22. if (file_exists($local_vendor_path)) {
  23. $vendor_path = $local_vendor_path;
  24. }
  25. elseif (file_exists($global_vendor_path)) {
  26. $vendor_path = $global_vendor_path;
  27. }
  28. else {
  29. $msg = "Unable to load autoload.php. Drush now requires Composer in order to install its dependencies and autoload classes. Please see README.md\n";
  30. fwrite(STDERR, $msg);
  31. return FALSE;
  32. }
  33. $classloader = require $vendor_path;
  34. require_once DRUSH_BASE_PATH . '/includes/bootstrap.inc';
  35. require_once DRUSH_BASE_PATH . '/includes/environment.inc';
  36. require_once DRUSH_BASE_PATH . '/includes/command.inc';
  37. require_once DRUSH_BASE_PATH . '/includes/drush.inc';
  38. require_once DRUSH_BASE_PATH . '/includes/engines.inc';
  39. require_once DRUSH_BASE_PATH . '/includes/backend.inc';
  40. require_once DRUSH_BASE_PATH . '/includes/batch.inc';
  41. require_once DRUSH_BASE_PATH . '/includes/context.inc';
  42. require_once DRUSH_BASE_PATH . '/includes/sitealias.inc';
  43. require_once DRUSH_BASE_PATH . '/includes/exec.inc';
  44. require_once DRUSH_BASE_PATH . '/includes/drupal.inc';
  45. require_once DRUSH_BASE_PATH . '/includes/output.inc';
  46. require_once DRUSH_BASE_PATH . '/includes/cache.inc';
  47. require_once DRUSH_BASE_PATH . '/includes/filesystem.inc';
  48. require_once DRUSH_BASE_PATH . '/includes/dbtng.inc';
  49. // Stash our vendor path and classloader.
  50. drush_set_context('DRUSH_VENDOR_PATH', dirname(realpath($vendor_path)));
  51. drush_set_context('DRUSH_CLASSLOADER', $classloader);
  52. // Can't log until we have a logger, so we'll create this ASAP.
  53. _drush_create_default_logger();
  54. // Terminate immediately unless invoked as a command line script
  55. if (!drush_verify_cli()) {
  56. return drush_set_error('DRUSH_REQUIREMENTS_ERROR', dt('Drush is designed to run via the command line.'));
  57. }
  58. // Check supported version of PHP.
  59. // Note: If this is adjusted, check other code that compares
  60. // PHP_VERSION, such as drush_json_encode(), runserver/runserver.drush.inc, and also
  61. // adjust _drush_environment_check_php_ini() and the php_prohibited_options
  62. // list in the drush script. See http://drupal.org/node/1748228
  63. define('DRUSH_MINIMUM_PHP', '5.3.0');
  64. if (version_compare(phpversion(), DRUSH_MINIMUM_PHP) < 0 && !getenv('DRUSH_NO_MIN_PHP')) {
  65. 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)));
  66. }
  67. if (!$return = _drush_environment_check_php_ini()) {
  68. return; // An error was logged.
  69. }
  70. $drush_info = drush_read_drush_info();
  71. define('DRUSH_VERSION', $drush_info['drush_version']);
  72. $version_parts = explode('.', DRUSH_VERSION);
  73. define('DRUSH_MAJOR_VERSION', $version_parts[0]);
  74. define('DRUSH_MINOR_VERSION', $version_parts[1]);
  75. define('DRUSH_REQUEST_TIME', microtime(TRUE));
  76. drush_set_context('argc', $GLOBALS['argc']);
  77. drush_set_context('argv', $GLOBALS['argv']);
  78. // Set an error handler and a shutdown function
  79. set_error_handler('drush_error_handler');
  80. register_shutdown_function('drush_shutdown');
  81. // We need some global options/arguments processed at this early stage.
  82. drush_parse_args();
  83. // Process initial global options such as --debug.
  84. _drush_preflight_global_options();
  85. drush_log(dt("Drush preflight prepare loaded autoloader at !autoloader", array('!autoloader' => realpath($vendor_path))), LogLevel::PREFLIGHT);
  86. }
  87. /**
  88. * During the initialization of Drush, this is the first
  89. * step where we load our configuration and commandfiles,
  90. * and select the site we are going to operate on; however,
  91. * we take no irreversible actions (e.g. site bootstrapping).
  92. * This allows commands that are declared with no bootstrap
  93. * to select a new site root and bootstrap it.
  94. *
  95. * In this step we will register the shutdown function,
  96. * parse the command line arguments and store them in their
  97. * related contexts.
  98. *
  99. * Configuration files (drushrc.php) that are
  100. * a) Specified on the command line
  101. * b) Stored in the root directory of drush.php
  102. * c) Stored in the home directory of the system user.
  103. *
  104. * Additionally the DRUSH_QUIET and DRUSH_BACKEND contexts,
  105. * will be evaluated now, as they need to be set very early in
  106. * the execution flow to be able to take affect.
  107. *
  108. * @return \Drush\Boot\Boot;
  109. */
  110. function drush_preflight() {
  111. // Create an alias '@none' to represent no Drupal site
  112. _drush_sitealias_cache_alias('@none', array('root' => '', 'uri' => ''));
  113. // Discover terminal width for pretty output.
  114. _drush_preflight_columns();
  115. // Display is tidy now that column width has been handled.
  116. drush_log(dt('Starting Drush preflight.'), LogLevel::PREFLIGHT);
  117. // Statically define a way to call drush again.
  118. define('DRUSH_COMMAND', drush_find_drush());
  119. // prime the CWD cache
  120. drush_cwd();
  121. // Set up base environment for system-wide file locations.
  122. _drush_preflight_base_environment();
  123. // Setup global alias_paths[] in context system.
  124. if (!drush_get_option('local')) {
  125. _drush_preflight_alias_path();
  126. }
  127. if (!drush_get_option('local')) {
  128. // Load a drushrc.php file in the drush.php's directory.
  129. drush_load_config('drush');
  130. // Load a drushrc.php file in the $ETC_PREFIX/etc/drush directory.
  131. drush_load_config('system');
  132. // Load a drushrc.php file at ~/.drushrc.php.
  133. drush_load_config('user');
  134. // Load a drushrc.php file in the ~/.drush directory.
  135. drush_load_config('home.drush');
  136. }
  137. // Load a custom config specified with the --config option.
  138. drush_load_config('custom');
  139. _drush_preflight_global_options();
  140. // Load all the commandfiles findable from any of the
  141. // scopes listed above.
  142. _drush_find_commandfiles_drush();
  143. // Look up the alias identifier that the user wants to use,
  144. // either via an arguement or via 'site-set'.
  145. $target_alias = drush_sitealias_check_arg_and_site_set();
  146. // Process the site alias that specifies which instance
  147. // of Drush (local or remote) this command will operate on.
  148. // We must do this after we load our config files (so that
  149. // site aliases are available), but before the rest of
  150. // Drush preflight and Drupal root bootstrap phase are
  151. // done, since site aliases may set option values that
  152. // affect these phases.
  153. $alias_record = _drush_sitealias_set_context_by_name($target_alias);
  154. // Find the selected site based on --root, --uri or cwd
  155. drush_preflight_root();
  156. // Preflight the selected site, and load any configuration and commandfiles associated with it.
  157. drush_preflight_site();
  158. // Check to see if anything changed during the 'site' preflight
  159. // that might allow us to find our alias record now
  160. if (empty($alias_record)) {
  161. $alias_record = _drush_sitealias_set_context_by_name($target_alias);
  162. // If the site alias settings changed late in the preflight,
  163. // then run the preflight for the root and site contexts again.
  164. if (!empty($alias_record)) {
  165. $remote_host = drush_get_option('remote-host');
  166. if (!isset($remote_host)) {
  167. drush_preflight_root();
  168. drush_preflight_site();
  169. }
  170. }
  171. }
  172. // Fail if we could not find the selected site alias.
  173. if ($target_alias && empty($alias_record)) {
  174. // We will automatically un-set the site-set alias if it could not be found.
  175. // Otherwise, we'd be stuck -- the user would only be able to execute Drush
  176. // commands again after `drush @none site-set @none`, and most folks would
  177. // have a hard time figuring that out.
  178. $site_env = drush_sitealias_site_get();
  179. if ($site_env == $target_alias) {
  180. drush_sitealias_site_clear();
  181. }
  182. return drush_set_error('DRUSH_BOOTSTRAP_NO_ALIAS', dt("Could not find the alias !alias", array('!alias' => $target_alias)));
  183. }
  184. // If applicable swaps in shell alias values.
  185. drush_shell_alias_replace($target_alias);
  186. // Copy global options to their respective contexts
  187. _drush_preflight_global_options();
  188. // Set environment variables based on #env-vars.
  189. drush_set_environment_vars($alias_record);
  190. // Select the bootstrap object and return it.
  191. return drush_select_bootstrap_class();
  192. }
  193. /**
  194. * If --root is provided, set context.
  195. */
  196. function drush_preflight_root() {
  197. $root = drush_get_option('root');
  198. if (!isset($root)) {
  199. $root = drush_locate_root();
  200. }
  201. if ($root) {
  202. $root = realpath($root);
  203. }
  204. // @todo This context name should not mention Drupal.
  205. // @todo Drupal code should use DRUSH_DRUPAL_ROOT instead of this constant.
  206. drush_set_context('DRUSH_SELECTED_DRUPAL_ROOT', $root);
  207. // Load the config options from Drupal's /drush and sites/all/drush directories,
  208. // even prior to bootstrapping the root.
  209. drush_load_config('drupal');
  210. }
  211. function drush_preflight_site() {
  212. // Load the Drupal site configuration options upfront.
  213. drush_load_config('site');
  214. // Determine URI and set constants/contexts accordingly. Keep this after loading of drupal,site configs.
  215. _drush_preflight_uri();
  216. // If someone set 'uri' in the 'site' context, then copy it
  217. // to the 'process' context (to give it a higher priority
  218. // than the 'cli' and 'alias' contexts) and reset our selected
  219. // site and @self alias.
  220. $uri = drush_get_option('uri');
  221. if ($uri != drush_get_option('uri', $uri, 'site')) {
  222. drush_set_option('uri', drush_get_option('uri', $uri, 'site'));
  223. _drush_preflight_uri();
  224. }
  225. // Create a @self site alias record.
  226. drush_sitealias_create_self_alias();
  227. }
  228. function _drush_preflight_global_options() {
  229. // Debug implies verbose
  230. drush_set_context('DRUSH_VERBOSE', drush_get_option(array('verbose', LogLevel::DEBUG), FALSE));
  231. drush_set_context('DRUSH_DEBUG', drush_get_option(LogLevel::DEBUG));
  232. drush_set_context('DRUSH_SIMULATE', drush_get_option('simulate', FALSE));
  233. // Backend implies affirmative unless negative is explicitly specified
  234. drush_set_context('DRUSH_NEGATIVE', drush_get_option('no', FALSE));
  235. drush_set_context('DRUSH_AFFIRMATIVE', drush_get_option(array('yes', 'pipe'), FALSE) || (drush_get_context('DRUSH_BACKEND') && !drush_get_context('DRUSH_NEGATIVE')));
  236. // Pipe implies quiet.
  237. drush_set_context('DRUSH_QUIET', drush_get_option(array('quiet', 'pipe')));
  238. drush_set_context('DRUSH_PIPE', drush_get_option('pipe'));
  239. // Suppress colored logging if --nocolor option is explicitly given or if
  240. // terminal does not support it.
  241. $nocolor = (drush_get_option('nocolor', FALSE));
  242. if (!$nocolor) {
  243. // Check for colorless terminal. If there is no terminal, then
  244. // 'tput colors 2>&1' will return "tput: No value for $TERM and no -T specified",
  245. // which is not numeric and therefore will put us in no-color mode.
  246. $colors = exec('tput colors 2>&1');
  247. $nocolor = !($colors === FALSE || (is_numeric($colors) && $colors >= 3));
  248. }
  249. drush_set_context('DRUSH_NOCOLOR', $nocolor);
  250. }
  251. /**
  252. * Sets up basic environment that controls where Drush looks for files on a
  253. * system-wide basis. Important to call for "early" functions that need to
  254. * work with unit tests.
  255. */
  256. function _drush_preflight_base_environment() {
  257. // Copy ETC_PREFIX and SHARE_PREFIX from environment variables if available.
  258. // This alters where we check for server-wide config and alias files.
  259. // Used by unit test suite to provide a clean environment.
  260. if (getenv('ETC_PREFIX')) drush_set_context('ETC_PREFIX', getenv('ETC_PREFIX'));
  261. if (getenv('SHARE_PREFIX')) drush_set_context('SHARE_PREFIX', getenv('SHARE_PREFIX'));
  262. drush_set_context('DOC_PREFIX', DRUSH_BASE_PATH);
  263. if (!file_exists(DRUSH_BASE_PATH . '/README.md') && file_exists(drush_get_context('SHARE_PREFIX', '/usr') . '/share/doc/drush' . '/README.md')) {
  264. drush_set_context('DOC_PREFIX', drush_get_context('SHARE_PREFIX', '/usr') . '/share/doc/drush');
  265. }
  266. $default_prefix_configuration = drush_is_windows() ? getenv('ALLUSERSPROFILE') . '/Drush' : '';
  267. $default_prefix_commandfile = drush_is_windows() ? getenv('ALLUSERSPROFILE') . '/Drush' : '/usr';
  268. $site_wide_configuration_dir = drush_get_context('ETC_PREFIX', $default_prefix_configuration) . '/etc/drush';
  269. $site_wide_commandfile_dir = drush_get_context('SHARE_PREFIX', $default_prefix_commandfile) . '/share/drush/commands';
  270. drush_set_context('DRUSH_SITE_WIDE_CONFIGURATION', $site_wide_configuration_dir);
  271. drush_set_context('DRUSH_SITE_WIDE_COMMANDFILES', $site_wide_commandfile_dir);
  272. $server_home = drush_server_home();
  273. if (isset($server_home)) {
  274. drush_set_context('DRUSH_PER_USER_CONFIGURATION', $server_home . '/.drush');
  275. }
  276. }
  277. /*
  278. * Set the terminal width, used for wrapping table output.
  279. * Normally this is exported using tput in the drush script.
  280. * If this is not present we do an additional check using stty here.
  281. * On Windows in CMD and PowerShell is this exported using mode con.
  282. */
  283. function _drush_preflight_columns() {
  284. if (!($columns = getenv('COLUMNS'))) {
  285. // Trying to export the columns using stty.
  286. exec('stty size 2>&1', $columns_output, $columns_status);
  287. if (!$columns_status) $columns = preg_replace('/\d+\s(\d+)/', '$1', $columns_output[0], -1, $columns_count);
  288. // If stty fails and Drush us running on Windows are we trying with mode con.
  289. if (($columns_status || !$columns_count) && drush_is_windows()) {
  290. $columns_output = array();
  291. exec('mode con', $columns_output, $columns_status);
  292. if (!$columns_status && is_array($columns_output)) {
  293. $columns = (int)preg_replace('/\D/', '', $columns_output[4], -1, $columns_count);
  294. }
  295. else {
  296. drush_log(dt('Drush could not detect the console window width. Set a Windows Environment Variable of COLUMNS to the desired width.'), LogLevel::WARNING);
  297. }
  298. }
  299. // Failling back to default columns value
  300. if (empty($columns)) {
  301. $columns = 80;
  302. }
  303. }
  304. // If a caller wants to reserve some room to add additional
  305. // information to the drush output via post-processing, the
  306. // --reserve-margin flag can be used to declare how much
  307. // space to leave out. This only affects drush functions
  308. // such as drush_print_table() that wrap the output.
  309. $columns -= drush_get_option('reserve-margin', 0);
  310. drush_set_context('DRUSH_COLUMNS', $columns);
  311. }
  312. function _drush_preflight_alias_path() {
  313. $alias_path =& drush_get_context('ALIAS_PATH');
  314. $default_prefix_configuration = drush_is_windows() ? getenv('ALLUSERSPROFILE') . '/Drush' : '';
  315. $site_wide_configuration_dir = drush_get_context('ETC_PREFIX', $default_prefix_configuration) . '/etc/drush';
  316. $alias_path[] = $site_wide_configuration_dir;
  317. $alias_path[] = dirname(__FILE__) . '/..';
  318. $server_home = drush_server_home();
  319. if (isset($server_home)) {
  320. $alias_path[] = $server_home . '/.drush';
  321. }
  322. }
  323. /*
  324. * Set root and uri.
  325. */
  326. function _drush_preflight_root_uri() {
  327. drush_preflight_root();
  328. _drush_preflight_uri();
  329. }
  330. /**
  331. * If --uri is provided, set context.
  332. */
  333. function _drush_preflight_uri() {
  334. $uri = drush_get_option('uri', '');
  335. drush_set_context('DRUSH_SELECTED_URI', $uri);
  336. }
  337. function _drush_find_commandfiles_drush() {
  338. // Core commands shipping with Drush
  339. $searchpath[] = realpath(dirname(__FILE__) . '/../commands/');
  340. // User commands, specified by 'include' option
  341. $include = drush_get_context('DRUSH_INCLUDE', array());
  342. foreach ($include as $path) {
  343. if (is_dir($path)) {
  344. drush_log('Include ' . $path, LogLevel::NOTICE);
  345. $searchpath[] = $path;
  346. }
  347. }
  348. if (!drush_get_option('local')) {
  349. // System commands, residing in $SHARE_PREFIX/share/drush/commands
  350. $share_path = drush_get_context('DRUSH_SITE_WIDE_COMMANDFILES');
  351. if (is_dir($share_path)) {
  352. $searchpath[] = $share_path;
  353. }
  354. // User commands, residing in ~/.drush
  355. $per_user_config_dir = drush_get_context('DRUSH_PER_USER_CONFIGURATION');
  356. if (!empty($per_user_config_dir)) {
  357. $searchpath[] = $per_user_config_dir;
  358. }
  359. }
  360. // @todo the zero parameter is a bit weird here. It's $phase.
  361. _drush_add_commandfiles($searchpath, 0);
  362. }
  363. /**
  364. * Handle any command preprocessing that may need to be done, including
  365. * potentially redispatching the command immediately (e.g. for remote
  366. * commands).
  367. *
  368. * @return
  369. * TRUE if the command was handled remotely.
  370. */
  371. function drush_preflight_command_dispatch() {
  372. $interactive = drush_get_option('interactive', FALSE);
  373. // The command will be executed remotely if the --remote-host flag
  374. // is set; note that if a site alias is provided on the command line,
  375. // and the site alias references a remote server, then the --remote-host
  376. // option will be set when the site alias is processed.
  377. // @see drush_sitealias_check_arg_and_site_set and _drush_sitealias_set_context_by_name
  378. $remote_host = drush_get_option('remote-host');
  379. $site_list = drush_get_option('site-list');
  380. // Get the command early so that we can allow commands to directly handle remote aliases if they wish
  381. $command = drush_parse_command();
  382. drush_command_default_options($command);
  383. // If the command sets the 'strict-option-handling' flag, then we will remove
  384. // any cli options that appear after the command name from the 'cli' context.
  385. // The cli options that appear before the command name are stored in the
  386. // 'DRUSH_GLOBAL_CLI_OPTIONS' context, so we will just overwrite the cli context
  387. // with this, after doing the neccessary fixup from short-form to long-form options.
  388. // After we do that, we put back any local drush options identified by $command['options'].
  389. if (is_array($command) && !empty($command['strict-option-handling'])) {
  390. $cli_options = drush_get_context('DRUSH_GLOBAL_CLI_OPTIONS', array());
  391. // Now we are going to sort out any options that exist in $command['options'];
  392. // we will remove these from DRUSH_COMMAND_ARGS and put them back into the
  393. // cli options.
  394. $cli_context = drush_get_context('cli');
  395. $remove_from_command_args = array();
  396. foreach ($command['options'] as $option => $info) {
  397. if (array_key_exists($option, $cli_context)) {
  398. $cli_options[$option] = $cli_context[$option];
  399. $remove_from_command_args[$option] = $option;
  400. }
  401. }
  402. if (!empty($remove_from_command_args)) {
  403. $drush_command_args = array();
  404. foreach (drush_get_context('DRUSH_COMMAND_ARGS') as $arg) {
  405. if (!_drush_should_remove_command_arg($arg, $remove_from_command_args)) {
  406. $drush_command_args[] = $arg;
  407. }
  408. }
  409. drush_set_context('DRUSH_COMMAND_ARGS', $drush_command_args);
  410. }
  411. drush_expand_short_form_options($cli_options);
  412. drush_set_context('cli', $cli_options);
  413. _drush_preflight_global_options();
  414. }
  415. $args = drush_get_arguments();
  416. $command_name = array_shift($args);
  417. $root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
  418. $local_drush = drush_get_option('drush-script');
  419. $is_local = drush_get_option('local');
  420. $values = NULL;
  421. if (!empty($root) && !empty($local_drush) && empty($is_local)) {
  422. if (!drush_is_absolute_path($local_drush)) {
  423. $local_drush = $root . '/' . $local_drush;
  424. }
  425. $local_drush = realpath($local_drush);
  426. $this_drush = drush_find_drush();
  427. // If there is a local drush selected, and it is not the
  428. // same drush that is currently running, redispatch to it.
  429. if (file_exists($local_drush) && ($this_drush != $local_drush)) {
  430. $uri = drush_get_context('DRUSH_SELECTED_URI');
  431. $aditional_options = array(
  432. 'root' => $root,
  433. 'local' => TRUE,
  434. );
  435. if (!empty($uri)) {
  436. $aditional_options['uri'] = $uri;
  437. }
  438. $values = drush_do_command_redispatch(is_array($command) ? $command : $command_name, $args, NULL, NULL, $local_drush, TRUE, $aditional_options);
  439. }
  440. }
  441. // If the command sets the 'handle-remote-commands' flag, then we will short-circuit
  442. // remote command dispatching and site-list command dispatching, and always let
  443. // the command handler run on the local machine.
  444. if (is_array($command) && !empty($command['handle-remote-commands'])) {
  445. return FALSE;
  446. }
  447. if (isset($remote_host)) {
  448. $remote_user = drush_get_option('remote-user');
  449. // Force interactive mode if there is a single remote target. #interactive is added by drush_do_command_redispatch
  450. $user_interactive = drush_get_option('interactive');
  451. drush_set_option('interactive', TRUE);
  452. $values = drush_do_command_redispatch(is_array($command) ? $command : $command_name, $args, $remote_host, $remote_user, $user_interactive);
  453. }
  454. // If the --site-list flag is set, then we will execute the specified
  455. // command once for every site listed in the site list.
  456. if (isset($site_list)) {
  457. if (!is_array($site_list)) {
  458. $site_list = explode(',', $site_list);
  459. }
  460. $site_record = array('site-list' => $site_list);
  461. $args = drush_get_arguments();
  462. if (!drush_get_context('DRUSH_SIMULATE') && !$interactive && !drush_get_context('DRUSH_AFFIRMATIVE') && !drush_get_context('DRUSH_QUIET')) {
  463. drush_print(dt("You are about to execute '!command' non-interactively (--yes forced) on all of the following targets:", array('!command' => implode(" ", $args))));
  464. foreach ($site_list as $one_destination) {
  465. drush_print(dt(' !target', array('!target' => $one_destination)));
  466. }
  467. if (drush_confirm('Continue? ') === FALSE) {
  468. drush_user_abort();
  469. return TRUE;
  470. }
  471. }
  472. $command_name = array_shift($args);
  473. $multi_options = drush_redispatch_get_options();
  474. $backend_options = array();
  475. if (drush_get_option('pipe') || drush_get_option('interactive')) {
  476. $backend_options['interactive'] = TRUE;
  477. }
  478. if (drush_get_option('no-label', FALSE)) {
  479. $backend_options['no-label'] = TRUE;
  480. }
  481. // If the user specified a format, try to look up the
  482. // default list separator for the specified format.
  483. // If the user did not specify a different label separator,
  484. // then pass in the default as an option, so that the
  485. // separator between the items in the list and the site
  486. // name will be consistent.
  487. $format = drush_get_option('format', FALSE);
  488. if ($format && !array_key_exists('label-separator', $multi_options)) {
  489. $formatter = drush_load_engine('outputformat', $format);
  490. if ($formatter) {
  491. $list_separator = $formatter->get_info('list-separator');
  492. if ($list_separator) {
  493. $multi_options['label-separator'] = $list_separator;
  494. }
  495. }
  496. }
  497. $values = drush_invoke_process($site_record, $command_name, $args, $multi_options, $backend_options);
  498. }
  499. if (isset($values)) {
  500. if (is_array($values) && ($values['error_status'] > 0)) {
  501. // Force an error result code. Note that drush_shutdown() will still run.
  502. drush_set_context('DRUSH_EXECUTION_COMPLETED', TRUE);
  503. exit($values['error_status']);
  504. }
  505. return TRUE;
  506. }
  507. return FALSE;
  508. }
  509. /**
  510. * We set this context to let the shutdown function know we reached the end of drush_main().
  511. *
  512. * @see drush_main()
  513. */
  514. function drush_postflight() {
  515. drush_set_context("DRUSH_EXECUTION_COMPLETED", TRUE);
  516. }
  517. /**
  518. * Shutdown function for use while Drush and Drupal are bootstrapping and to return any
  519. * registered errors.
  520. *
  521. * The shutdown command checks whether certain options are set to reliably
  522. * detect and log some common Drupal initialization errors.
  523. *
  524. * If the command is being executed with the --backend option, the script
  525. * will return a json string containing the options and log information
  526. * used by the script.
  527. *
  528. * The command will exit with '1' if it was successfully executed, and the
  529. * result of drush_get_error() if it wasn't.
  530. */
  531. function drush_shutdown() {
  532. // Mysteriously make $user available during sess_write(). Avoids a NOTICE.
  533. global $user;
  534. if (!drush_get_context('DRUSH_EXECUTION_COMPLETED', FALSE) && !drush_get_context('DRUSH_USER_ABORT', FALSE)) {
  535. $php_error_message = '';
  536. if ($error = error_get_last()) {
  537. $php_error_message = "\n" . dt('Error: !message in !file, line !line', array('!message' => $error['message'], '!file' => $error['file'], '!line' => $error['line']));
  538. }
  539. // We did not reach the end of the drush_main function,
  540. // this generally means somewhere in the code a call to exit(),
  541. // was made. We catch this, so that we can trigger an error in
  542. // those cases.
  543. drush_set_error("DRUSH_NOT_COMPLETED", dt("Drush command terminated abnormally due to an unrecoverable error.!message", array('!message' => $php_error_message)));
  544. // Attempt to give the user some advice about how to fix the problem
  545. _drush_postmortem();
  546. }
  547. // @todo Ask the bootstrap object (or maybe dispatch) how far we got.
  548. $phase = drush_get_context('DRUSH_BOOTSTRAP_PHASE');
  549. if (drush_get_context('DRUSH_BOOTSTRAPPING')) {
  550. switch ($phase) {
  551. case DRUSH_BOOTSTRAP_DRUPAL_FULL :
  552. ob_end_clean();
  553. _drush_log_drupal_messages();
  554. drush_set_error('DRUSH_DRUPAL_BOOTSTRAP_ERROR');
  555. break;
  556. }
  557. }
  558. if (drush_get_context('DRUSH_BACKEND', FALSE)) {
  559. drush_backend_output();
  560. }
  561. elseif (drush_get_context('DRUSH_QUIET', FALSE)) {
  562. ob_end_clean();
  563. // If we are in pipe mode, emit the compact representation of the command, if available.
  564. if (drush_get_context('DRUSH_PIPE')) {
  565. drush_pipe_output();
  566. }
  567. }
  568. // This way drush_return_status() will always be the last shutdown function (unless other shutdown functions register shutdown functions...)
  569. // and won't prevent other registered shutdown functions (IE from numerous cron methods) from running by calling exit() before they get a chance.
  570. register_shutdown_function('drush_return_status');
  571. }
  572. /**
  573. * Shutdown function to save code coverage data.
  574. */
  575. function drush_coverage_shutdown() {
  576. if ($file_name = drush_get_context('DRUSH_CODE_COVERAGE', FALSE)) {
  577. $data = xdebug_get_code_coverage();
  578. xdebug_stop_code_coverage();
  579. // If coverage dump file contains anything, merge in the old data before
  580. // saving. This happens if the current drush command invoked another drush
  581. // command.
  582. if (file_exists($file_name) && $content = file_get_contents($file_name)) {
  583. $merge_data = unserialize($content);
  584. if (is_array($merge_data)) {
  585. foreach ($merge_data as $file => $lines) {
  586. if (!isset($data[$file])) {
  587. $data[$file] = $lines;
  588. }
  589. else {
  590. foreach ($lines as $num => $executed) {
  591. if (!isset($data[$file][$num])) {
  592. $data[$file][$num] = $executed;
  593. }
  594. else {
  595. $data[$file][$num] = ($executed == 1 ? $executed : $data[$file][$num]);
  596. }
  597. }
  598. }
  599. }
  600. }
  601. }
  602. file_put_contents($file_name, serialize($data));
  603. }
  604. }
  605. function drush_return_status() {
  606. // If a specific exit code was set, then use it.
  607. $exit_code = drush_get_context('DRUSH_EXIT_CODE');
  608. if (empty($exit_code)) {
  609. $exit_code = (drush_get_error()) ? DRUSH_FRAMEWORK_ERROR : DRUSH_SUCCESS;
  610. }
  611. exit($exit_code);
  612. }