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