3 use Drush\Log\LogLevel;
8 * Commands that only preflight, but do not bootstrap, should use
9 * a bootstrap level of DRUSH_BOOTSTRAP_NONE.
11 define('DRUSH_BOOTSTRAP_NONE', -1);
14 * Use drush_bootstrap_max instead of drush_bootstrap_to_phase
16 * This constant is only usable as the value of the 'bootstrap'
17 * item of a command object, or as the parameter to
18 * drush_bootstrap_to_phase. It is not a real bootstrap state.
20 define('DRUSH_BOOTSTRAP_MAX', -2);
25 * No longer used, but 0 remains reserved. Drush always runs preflight.
26 * Commands may alternatively use DRUSH_BOOTSTRAP_NONE.
28 define('DRUSH_BOOTSTRAP_DRUSH', 0);
30 // TODO: Move all of the constants below to a Drupal-specific file.
31 // We can't do this until commands are declaring which CMS they work
32 // with, because right now, commands that do not declare a 'bootstrap'
33 // level default to DRUSH_BOOTSTRAP_DRUPAL_LOGIN, so we need this constant,
34 // at least, available in non-Drupal contexts.
37 * Set up and test for a valid drupal root, either through the -r/--root options,
38 * or evaluated based on the current working directory.
40 * Any code that interacts with an entire Drupal installation, and not a specific
41 * site on the Drupal installation should use this bootstrap phase.
43 define('DRUSH_BOOTSTRAP_DRUPAL_ROOT', 1);
46 * Set up a Drupal site directory and the correct environment variables to
47 * allow Drupal to find the configuration file.
49 * If no site is specified with the -l / --uri options, Drush will assume the
50 * site is 'default', which mimics Drupal's behaviour.
52 * If you want to avoid this behaviour, it is recommended that you use the
53 * DRUSH_BOOTSTRAP_DRUPAL_ROOT bootstrap phase instead.
55 * Any code that needs to modify or interact with a specific Drupal site's
56 * settings.php file should bootstrap to this phase.
58 define('DRUSH_BOOTSTRAP_DRUPAL_SITE', 2);
61 * Load the settings from the Drupal sites directory.
63 * This phase is analagous to the DRUPAL_BOOTSTRAP_CONFIGURATION bootstrap phase in Drupal
64 * itself, and this is also the first step where Drupal specific code is included.
66 * This phase is commonly used for code that interacts with the Drupal install API,
67 * as both install.php and update.php start at this phase.
69 define('DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION', 3);
72 * Connect to the Drupal database using the database credentials loaded
73 * during the previous bootstrap phase.
75 * This phase is analogous to the DRUPAL_BOOTSTRAP_DATABASE bootstrap phase in
78 * Any code that needs to interact with the Drupal database API needs to
79 * be bootstrapped to at least this phase.
81 define('DRUSH_BOOTSTRAP_DRUPAL_DATABASE', 4);
84 * Fully initialize Drupal.
86 * This is analogous to the DRUPAL_BOOTSTRAP_FULL bootstrap phase in
89 * Any code that interacts with the general Drupal API should be
90 * bootstrapped to this phase.
92 define('DRUSH_BOOTSTRAP_DRUPAL_FULL', 5);
95 * Log in to the initialiased Drupal site.
97 * This is the default bootstrap phase all commands will try to reach,
98 * unless otherwise specified.
100 * This bootstrap phase is used after the site has been
101 * fully bootstrapped.
103 * This phase will log you in to the drupal site with the username
104 * or user ID specified by the --user/ -u option.
106 * Use this bootstrap phase for your command if you need to have access
107 * to information for a specific user, such as listing nodes that might
108 * be different based on who is logged in.
110 define('DRUSH_BOOTSTRAP_DRUPAL_LOGIN', 6);
113 * Return the list of bootstrap objects that are available for
114 * initializing a CMS with Drush. We insure that any given candidate
115 * class is instantiated only once.
117 * @return \Drush\Boot\Boot[]
119 function drush_get_bootstrap_candidates() {
120 $candidate_classes = drush_get_bootstrap_candidate_classnames();
122 $cache =& drush_get_context('DRUSH_BOOTSTRAP_CANDIDATE_OBJECTS');
125 foreach($candidate_classes as $candidate_class) {
126 if (array_key_exists($candidate_class, $cache)) {
127 $result[$candidate_class] = $cache[$candidate_class];
130 $result[$candidate_class] = new $candidate_class;
139 * Find the list of bootstrap classnames available for initializing a
144 function drush_get_bootstrap_candidate_classnames() {
145 // Give all commandfiles a chance to return candidates. They should
146 // return STRINGS with the class name of the bootstrap object they provide.
147 $candidates = drush_command_invoke_all('bootstrap_candidates');
148 // If a bootstrap class was specified on the command line, consider it first.
149 $bootstrap_class = drush_get_option('bootstrap_class', FALSE);
150 if ($bootstrap_class) {
151 array_unshift($candidates, $bootstrap_class);
153 // Add candidate bootstrap classes for Drupal
154 foreach (array('8', '7', '6') as $version) {
155 $drupal_bootstrap_class = 'Drush\Boot\DrupalBoot' . $version;
156 $candidates[] = $drupal_bootstrap_class;
158 // Always consider our default bootstrap class last.
159 $candidates[] = 'Drush\Boot\EmptyBoot';
165 * Look up the best bootstrap class for the given location
166 * from the set of available candidates.
168 function drush_bootstrap_class_for_root($path) {
169 drush_load_bootstrap_commandfile_at_path($path);
170 $candidates = drush_get_bootstrap_candidates();
171 foreach ($candidates as $candidate) {
172 if ($candidate->valid_root($path)) {
180 * Check to see if there is a bootstrap class available
181 * at the specified location; if there is, load it.
183 function drush_load_bootstrap_commandfile_at_path($path) {
184 static $paths = array();
186 if (!empty($path) && (!array_key_exists($path, $paths))) {
187 $paths[$path] = TRUE;
188 // Check to see if we have any bootstrap classes in this location.
189 $bootstrap_class_dir = $path . '/drush/bootstrap';
190 if (is_dir($bootstrap_class_dir)) {
191 _drush_add_commandfiles(array($bootstrap_class_dir), DRUSH_BOOTSTRAP_NONE);
197 * Select the bootstrap class to use. If this is called multiple
198 * times, the bootstrap class returned might change on subsequent
199 * calls, if the root directory changes. Once the bootstrap object
200 * starts changing the state of the system, however, it will
201 * be 'latched', and further calls to drush_select_bootstrap_class()
202 * will always return the same object.
204 function drush_select_bootstrap_class() {
205 $root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
207 // Once we have selected a Drupal root, we will reduce our bootstrap
208 // candidates down to just the one used to select this site root.
209 $bootstrap = drush_bootstrap_class_for_root($root);
210 // If we have not found a bootstrap class by this point,
211 // then take the last one and use it. This should be our
212 // default bootstrap class. The default bootstrap class
213 // should pass through all calls without doing anything that
214 // changes state in a CMS-specific way.
215 if ($bootstrap == NULL) {
216 $candidates = drush_get_bootstrap_candidates();
217 $bootstrap = array_pop($candidates);
224 * Don't allow the bootstrap object to change once we start bootstrapping
226 function drush_latch_bootstrap_object($bootstrap) {
227 drush_set_context('DRUSH_BOOTSTRAP_OBJECT', $bootstrap);
231 * Get the appropriate bootstrap object. We'll search for a new
232 * bootstrap object every time someone asks for one until we start
233 * bootstrapping; then we'll returned the same cached one every time.
235 * @return \Drush\Boot\Boot
237 function drush_get_bootstrap_object() {
238 $bootstrap = drush_get_context('DRUSH_BOOTSTRAP_OBJECT', FALSE);
240 $bootstrap = drush_select_bootstrap_class();
246 * Find the URI that has been selected by the cwd
247 * if it was not previously set via the --uri / -l option
249 function _drush_bootstrap_selected_uri() {
250 $uri = drush_get_context('DRUSH_SELECTED_URI');
252 $site_path = drush_site_path();
253 $elements = explode('/', $site_path);
254 $current = array_pop($elements);
256 $current = 'default';
258 $uri = 'http://'. $current;
259 $uri = drush_set_context('DRUSH_SELECTED_URI', $uri);
260 drush_sitealias_create_self_alias();
267 * Helper function to store any context settings that are being validated.
269 function drush_bootstrap_value($context, $value = null) {
270 $values =& drush_get_context('DRUSH_BOOTSTRAP_VALUES', array());
273 $values[$context] = $value;
276 if (array_key_exists($context, $values)) {
277 return $values[$context];
284 * Returns an array that determines what bootstrap phases
285 * are necessary to bootstrap the CMS.
287 * @param bool $function_names
288 * (optional) If TRUE, return an array of method names index by their
289 * corresponding phase values. Otherwise return an array of phase values.
293 * @see \Drush\Boot\Boot::bootstrap_phases()
295 function _drush_bootstrap_phases($function_names = FALSE) {
298 if ($bootstrap = drush_get_bootstrap_object()) {
299 $result = $bootstrap->bootstrap_phases();
300 if (!$function_names) {
301 $result = array_keys($result);
308 * Bootstrap Drush to the desired phase.
310 * This function will sequentially bootstrap each
311 * lower phase up to the phase that has been requested.
314 * The bootstrap phase to bootstrap to.
315 * @param int $phase_max
316 * (optional) The maximum level to boot to. This does not have a use in this
317 * function itself but can be useful for other code called from within this
318 * function, to know if e.g. a caller is in the process of booting to the
319 * specified level. If specified, it should never be lower than $phase.
322 * TRUE if the specified bootstrap phase has completed.
324 * @see \Drush\Boot\Boot::bootstrap_phases()
326 function drush_bootstrap($phase, $phase_max = FALSE) {
327 $bootstrap = drush_get_bootstrap_object();
328 $phases = _drush_bootstrap_phases(TRUE);
331 // If the requested phase does not exist in the list of available
332 // phases, it means that the command requires bootstrap to a certain
333 // level, but no site root could be found.
334 if (!isset($phases[$phase])) {
335 $result = drush_bootstrap_error('DRUSH_NO_SITE', dt("We could not find an applicable site for that command."));
338 // Once we start bootstrapping past the DRUSH_BOOTSTRAP_DRUSH phase, we
339 // will latch the bootstrap object, and prevent it from changing.
340 if ($phase > DRUSH_BOOTSTRAP_DRUSH) {
341 drush_latch_bootstrap_object($bootstrap);
344 drush_set_context('DRUSH_BOOTSTRAPPING', TRUE);
345 foreach ($phases as $phase_index => $current_phase) {
346 $bootstrapped_phase = drush_get_context('DRUSH_BOOTSTRAP_PHASE', -1);
347 if ($phase_index > $phase) {
350 if ($phase_index > $bootstrapped_phase) {
351 if ($result = drush_bootstrap_validate($phase_index)) {
352 if (method_exists($bootstrap, $current_phase) && !drush_get_error()) {
353 drush_log(dt("Drush bootstrap phase : !function()", array('!function' => $current_phase)), LogLevel::BOOTSTRAP);
354 $bootstrap->{$current_phase}();
356 // Reset commandfile cache and find any new command files that are available during this bootstrap phase.
357 drush_get_commands(TRUE);
358 _drush_find_commandfiles($phase_index, $phase_max);
360 drush_set_context('DRUSH_BOOTSTRAP_PHASE', $phase_index);
364 drush_set_context('DRUSH_BOOTSTRAPPING', FALSE);
365 if (!$result || drush_get_error()) {
366 $errors = drush_get_context('DRUSH_BOOTSTRAP_ERRORS', array());
367 foreach ($errors as $code => $message) {
368 drush_set_error($code, $message);
371 return !drush_get_error();
375 * Determine whether a given bootstrap phase has been completed
377 * This function name has a typo which makes me laugh so we choose not to
378 * fix it. Take a deep breath, and smile. See
379 * http://en.wikipedia.org/wiki/HTTP_referer
383 * The bootstrap phase to test
386 * TRUE if the specified bootstrap phase has completed.
388 function drush_has_boostrapped($phase) {
389 $phase_index = drush_get_context('DRUSH_BOOTSTRAP_PHASE');
391 return isset($phase_index) && ($phase_index >= $phase);
395 * Validate whether a bootstrap phase can be reached.
397 * This function will validate the settings that will be used
398 * during the actual bootstrap process, and allow commands to
399 * progressively bootstrap to the highest level that can be reached.
401 * This function will only run the validation function once, and
402 * store the result from that execution in a local static. This avoids
403 * validating phases multiple times.
406 * The bootstrap phase to validate to.
409 * TRUE if bootstrap is possible, FALSE if the validation failed.
411 * @see \Drush\Boot\Boot::bootstrap_phases()
413 function drush_bootstrap_validate($phase) {
414 $bootstrap = drush_get_bootstrap_object();
415 $phases = _drush_bootstrap_phases(TRUE);
416 static $result_cache = array();
418 if (!array_key_exists($phase, $result_cache)) {
419 drush_set_context('DRUSH_BOOTSTRAP_ERRORS', array());
420 drush_set_context('DRUSH_BOOTSTRAP_VALUES', array());
422 foreach ($phases as $phase_index => $current_phase) {
423 $validated_phase = drush_get_context('DRUSH_BOOTSTRAP_VALIDATION_PHASE', -1);
424 if ($phase_index > $phase) {
427 if ($phase_index > $validated_phase) {
428 $current_phase .= '_validate';
429 if (method_exists($bootstrap, $current_phase)) {
430 $result_cache[$phase_index] = $bootstrap->{$current_phase}();
433 $result_cache[$phase_index] = TRUE;
435 drush_set_context('DRUSH_BOOTSTRAP_VALIDATION_PHASE', $phase_index);
439 return $result_cache[$phase];
443 * Bootstrap to the specified phase.
445 * @param int $max_phase_index
446 * Only attempt bootstrap to the specified level.
449 * TRUE if the specified bootstrap phase has completed.
451 function drush_bootstrap_to_phase($max_phase_index) {
452 if ($max_phase_index == DRUSH_BOOTSTRAP_MAX) {
453 // Bootstrap as far as we can without throwing an error, but log for
454 // debugging purposes.
455 drush_log(dt("Trying to bootstrap as far as we can."), 'debug');
456 drush_bootstrap_max();
460 drush_log(dt("Bootstrap to phase !phase.", array('!phase' => $max_phase_index)), LogLevel::BOOTSTRAP);
461 $phases = _drush_bootstrap_phases();
464 // Try to bootstrap to the maximum possible level, without generating errors
465 foreach ($phases as $phase_index) {
466 if ($phase_index > $max_phase_index) {
467 // Stop trying, since we achieved what was specified.
471 if (drush_bootstrap_validate($phase_index)) {
472 if ($phase_index > drush_get_context('DRUSH_BOOTSTRAP_PHASE', DRUSH_BOOTSTRAP_NONE)) {
473 $result = drush_bootstrap($phase_index, $max_phase_index);
486 * Bootstrap to the highest level possible, without triggering any errors.
488 * @param int $max_phase_index
489 * (optional) Only attempt bootstrap to the specified level.
492 * The maximum phase to which we bootstrapped.
494 function drush_bootstrap_max($max_phase_index = FALSE) {
495 $phases = _drush_bootstrap_phases(TRUE);
496 if (!$max_phase_index) {
497 $max_phase_index = count($phases);
500 // Try to bootstrap to the maximum possible level, without generating errors.
501 foreach ($phases as $phase_index => $current_phase) {
502 if ($phase_index > $max_phase_index) {
503 // Stop trying, since we achieved what was specified.
507 if (drush_bootstrap_validate($phase_index)) {
508 if ($phase_index > drush_get_context('DRUSH_BOOTSTRAP_PHASE')) {
509 drush_bootstrap($phase_index, $max_phase_index);
513 // drush_bootstrap_validate() only logs successful validations. For us,
514 // knowing what failed can also be important.
515 $previous = drush_get_context('DRUSH_BOOTSTRAP_PHASE');
516 drush_log(dt("Bootstrap phase !function() failed to validate; continuing at !current().", array('!function' => $current_phase, '!current' => $phases[$previous])), 'debug');
521 return drush_get_context('DRUSH_BOOTSTRAP_PHASE');
525 * Bootstrap the specified site alias. The site alias must
526 * be a valid alias to a local site.
528 * @param $site_record
529 * The alias record for the given site alias.
530 * @see drush_sitealias_get_record().
531 * @param $max_phase_index
532 * Only attempt bootstrap to the specified level.
533 * @returns TRUE if attempted to bootstrap, or FALSE
534 * if no bootstrap attempt was made.
536 function drush_bootstrap_max_to_sitealias($site_record, $max_phase_index = NULL) {
537 if ((array_key_exists('root', $site_record) && !array_key_exists('remote-host', $site_record))) {
538 drush_sitealias_set_alias_context($site_record);
539 drush_bootstrap_max($max_phase_index);
546 * Helper function to collect any errors that occur during the bootstrap process.
547 * Always returns FALSE, for convenience.
549 function drush_bootstrap_error($code, $message = null) {
550 $errors = drush_get_context('DRUSH_BOOTSTRAP_ERRORS');
551 $errors[$code] = $message;
552 drush_set_context('DRUSH_BOOTSTRAP_ERRORS', $errors);
556 function _drush_bootstrap_output_prepare() {
557 // Note that as soon as we set the DRUSH_BACKEND context, we change
558 // the behavior of drush_log(). It is therefore important that we
559 // should not set this context until immediately before we call ob_start
560 // (i.e., in this function).
561 $backend = drush_set_context('DRUSH_BACKEND', drush_get_option('backend'));
562 $quiet = drush_get_context('DRUSH_QUIET');
565 // Load options passed as a JSON encoded string through STDIN.
566 $stdin_options = _drush_backend_get_stdin();
567 if (is_array($stdin_options)) {
568 drush_set_context('stdin', $stdin_options);
570 // Add an output buffer handler to collect output/pass through backend
571 // packets. Using a chunksize of 2 ensures that each line is flushed
574 // Pass through of backend packets, discard regular output.
575 ob_start('drush_backend_output_discard', 2);
579 ob_start('drush_backend_output_collect', 2);
583 // In non-backend quiet mode we start buffering and discards it on command
585 if ($quiet && !$backend) {
591 * Used by a Drush extension to request that its Composer autoload
592 * files be loaded by Drush, if they have not already been.
596 * function mycommandfile_drush_init() {
597 * drush_autoload(__FILE__)
601 function drush_autoload($commandfile) {
602 $already_added = commandfiles_cache()->add($commandfile);
604 $dir = dirname($commandfile);
605 $candidates = array("vendor/autoload.php", "../../../vendor/autoload.php");
606 $drush_autoload_file = drush_get_context('DRUSH_VENDOR_PATH', '');
608 foreach ($candidates as $candidate) {
609 $autoload = $dir . '/' . $candidate;
610 if (file_exists($autoload) && (realpath($autoload) != $drush_autoload_file)) {