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