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