valid_root($path)) { return $candidate; } } return NULL; } /** * Check to see if there is a bootstrap class available * at the specified location; if there is, load it. */ function drush_load_bootstrap_commandfile_at_path($path) { static $paths = array(); if (!empty($path) && (!array_key_exists($path, $paths))) { $paths[$path] = TRUE; // Check to see if we have any bootstrap classes in this location. $bootstrap_class_dir = $path . '/drush/bootstrap'; if (is_dir($bootstrap_class_dir)) { _drush_add_commandfiles(array($bootstrap_class_dir), DRUSH_BOOTSTRAP_NONE); } } } /** * Select the bootstrap class to use. If this is called multiple * times, the bootstrap class returned might change on subsequent * calls, if the root directory changes. Once the bootstrap object * starts changing the state of the system, however, it will * be 'latched', and further calls to drush_select_bootstrap_class() * will always return the same object. */ function drush_select_bootstrap_class() { $root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT'); // Once we have selected a Drupal root, we will reduce our bootstrap // candidates down to just the one used to select this site root. $bootstrap = drush_bootstrap_class_for_root($root); // If we have not found a bootstrap class by this point, // then take the last one and use it. This should be our // default bootstrap class. The default bootstrap class // should pass through all calls without doing anything that // changes state in a CMS-specific way. if ($bootstrap == NULL) { $candidates = drush_get_bootstrap_candidates(); $bootstrap = array_pop($candidates); } return $bootstrap; } /** * Don't allow the bootstrap object to change once we start bootstrapping */ function drush_latch_bootstrap_object($bootstrap) { drush_set_context('DRUSH_BOOTSTRAP_OBJECT', $bootstrap); } /** * Get the appropriate bootstrap object. We'll search for a new * bootstrap object every time someone asks for one until we start * bootstrapping; then we'll returned the same cached one every time. * * @return \Drush\Boot\Boot */ function drush_get_bootstrap_object() { $bootstrap = drush_get_context('DRUSH_BOOTSTRAP_OBJECT', FALSE); if (!$bootstrap) { $bootstrap = drush_select_bootstrap_class(); } return $bootstrap; } /** * Find the URI that has been selected by the cwd * if it was not previously set via the --uri / -l option */ function _drush_bootstrap_selected_uri() { $uri = drush_get_context('DRUSH_SELECTED_URI'); if (empty($uri)) { $site_path = drush_site_path(); $elements = explode('/', $site_path); $current = array_pop($elements); if (!$current) { $current = 'default'; } $uri = 'http://'. $current; $uri = drush_set_context('DRUSH_SELECTED_URI', $uri); drush_sitealias_create_self_alias(); } return $uri; } /** * Helper function to store any context settings that are being validated. */ function drush_bootstrap_value($context, $value = null) { $values =& drush_get_context('DRUSH_BOOTSTRAP_VALUES', array()); if (isset($value)) { $values[$context] = $value; } if (array_key_exists($context, $values)) { return $values[$context]; } return null; } /** * Returns an array that determines what bootstrap phases * are necessary to bootstrap the CMS. * * @param bool $function_names * (optional) If TRUE, return an array of method names index by their * corresponding phase values. Otherwise return an array of phase values. * * @return array * * @see \Drush\Boot\Boot::bootstrap_phases() */ function _drush_bootstrap_phases($function_names = FALSE) { $result = array(); if ($bootstrap = drush_get_bootstrap_object()) { $result = $bootstrap->bootstrap_phases(); if (!$function_names) { $result = array_keys($result); } } return $result; } /** * Bootstrap Drush to the desired phase. * * This function will sequentially bootstrap each * lower phase up to the phase that has been requested. * * @param int $phase * The bootstrap phase to bootstrap to. * @param int $phase_max * (optional) The maximum level to boot to. This does not have a use in this * function itself but can be useful for other code called from within this * function, to know if e.g. a caller is in the process of booting to the * specified level. If specified, it should never be lower than $phase. * * @return bool * TRUE if the specified bootstrap phase has completed. * * @see \Drush\Boot\Boot::bootstrap_phases() */ function drush_bootstrap($phase, $phase_max = FALSE) { $bootstrap = drush_get_bootstrap_object(); $phases = _drush_bootstrap_phases(TRUE); $result = TRUE; // If the requested phase does not exist in the list of available // phases, it means that the command requires bootstrap to a certain // level, but no site root could be found. if (!isset($phases[$phase])) { $result = drush_bootstrap_error('DRUSH_NO_SITE', dt("We could not find an applicable site for that command.")); } // Once we start bootstrapping past the DRUSH_BOOTSTRAP_DRUSH phase, we // will latch the bootstrap object, and prevent it from changing. if ($phase > DRUSH_BOOTSTRAP_DRUSH) { drush_latch_bootstrap_object($bootstrap); } drush_set_context('DRUSH_BOOTSTRAPPING', TRUE); foreach ($phases as $phase_index => $current_phase) { $bootstrapped_phase = drush_get_context('DRUSH_BOOTSTRAP_PHASE', -1); if ($phase_index > $phase) { break; } if ($phase_index > $bootstrapped_phase) { if ($result = drush_bootstrap_validate($phase_index)) { if (method_exists($bootstrap, $current_phase) && !drush_get_error()) { drush_log(dt("Drush bootstrap phase : !function()", array('!function' => $current_phase)), LogLevel::BOOTSTRAP); $bootstrap->{$current_phase}(); // Reset commandfile cache and find any new command files that are available during this bootstrap phase. drush_get_commands(TRUE); _drush_find_commandfiles($phase_index, $phase_max); } drush_set_context('DRUSH_BOOTSTRAP_PHASE', $phase_index); } } } drush_set_context('DRUSH_BOOTSTRAPPING', FALSE); if (!$result || drush_get_error()) { $errors = drush_get_context('DRUSH_BOOTSTRAP_ERRORS', array()); foreach ($errors as $code => $message) { drush_set_error($code, $message); } } return !drush_get_error(); } /** * Determine whether a given bootstrap phase has been completed * * This function name has a typo which makes me laugh so we choose not to * fix it. Take a deep breath, and smile. See * http://en.wikipedia.org/wiki/HTTP_referer * * * @param int $phase * The bootstrap phase to test * * @return bool * TRUE if the specified bootstrap phase has completed. */ function drush_has_boostrapped($phase) { $phase_index = drush_get_context('DRUSH_BOOTSTRAP_PHASE'); return isset($phase_index) && ($phase_index >= $phase); } /** * Validate whether a bootstrap phase can be reached. * * This function will validate the settings that will be used * during the actual bootstrap process, and allow commands to * progressively bootstrap to the highest level that can be reached. * * This function will only run the validation function once, and * store the result from that execution in a local static. This avoids * validating phases multiple times. * * @param int $phase * The bootstrap phase to validate to. * * @return bool * TRUE if bootstrap is possible, FALSE if the validation failed. * * @see \Drush\Boot\Boot::bootstrap_phases() */ function drush_bootstrap_validate($phase) { $bootstrap = drush_get_bootstrap_object(); $phases = _drush_bootstrap_phases(TRUE); static $result_cache = array(); if (!array_key_exists($phase, $result_cache)) { drush_set_context('DRUSH_BOOTSTRAP_ERRORS', array()); drush_set_context('DRUSH_BOOTSTRAP_VALUES', array()); foreach ($phases as $phase_index => $current_phase) { $validated_phase = drush_get_context('DRUSH_BOOTSTRAP_VALIDATION_PHASE', -1); if ($phase_index > $phase) { break; } if ($phase_index > $validated_phase) { $current_phase .= '_validate'; if (method_exists($bootstrap, $current_phase)) { $result_cache[$phase_index] = $bootstrap->{$current_phase}(); } else { $result_cache[$phase_index] = TRUE; } drush_set_context('DRUSH_BOOTSTRAP_VALIDATION_PHASE', $phase_index); } } } return $result_cache[$phase]; } /** * Bootstrap to the specified phase. * * @param int $max_phase_index * Only attempt bootstrap to the specified level. * * @return bool * TRUE if the specified bootstrap phase has completed. */ function drush_bootstrap_to_phase($max_phase_index) { if ($max_phase_index == DRUSH_BOOTSTRAP_MAX) { // Bootstrap as far as we can without throwing an error, but log for // debugging purposes. drush_log(dt("Trying to bootstrap as far as we can."), 'debug'); drush_bootstrap_max(); return TRUE; } drush_log(dt("Bootstrap to phase !phase.", array('!phase' => $max_phase_index)), LogLevel::BOOTSTRAP); $phases = _drush_bootstrap_phases(); $result = TRUE; // Try to bootstrap to the maximum possible level, without generating errors foreach ($phases as $phase_index) { if ($phase_index > $max_phase_index) { // Stop trying, since we achieved what was specified. break; } if (drush_bootstrap_validate($phase_index)) { if ($phase_index > drush_get_context('DRUSH_BOOTSTRAP_PHASE', DRUSH_BOOTSTRAP_NONE)) { $result = drush_bootstrap($phase_index, $max_phase_index); } } else { $result = FALSE; break; } } return $result; } /** * Bootstrap to the highest level possible, without triggering any errors. * * @param int $max_phase_index * (optional) Only attempt bootstrap to the specified level. * * @return int * The maximum phase to which we bootstrapped. */ function drush_bootstrap_max($max_phase_index = FALSE) { $phases = _drush_bootstrap_phases(TRUE); if (!$max_phase_index) { $max_phase_index = count($phases); } // Try to bootstrap to the maximum possible level, without generating errors. foreach ($phases as $phase_index => $current_phase) { if ($phase_index > $max_phase_index) { // Stop trying, since we achieved what was specified. break; } if (drush_bootstrap_validate($phase_index)) { if ($phase_index > drush_get_context('DRUSH_BOOTSTRAP_PHASE')) { drush_bootstrap($phase_index, $max_phase_index); } } else { // drush_bootstrap_validate() only logs successful validations. For us, // knowing what failed can also be important. $previous = drush_get_context('DRUSH_BOOTSTRAP_PHASE'); drush_log(dt("Bootstrap phase !function() failed to validate; continuing at !current().", array('!function' => $current_phase, '!current' => $phases[$previous])), 'debug'); break; } } return drush_get_context('DRUSH_BOOTSTRAP_PHASE'); } /** * Bootstrap the specified site alias. The site alias must * be a valid alias to a local site. * * @param $site_record * The alias record for the given site alias. * @see drush_sitealias_get_record(). * @param $max_phase_index * Only attempt bootstrap to the specified level. * @returns TRUE if attempted to bootstrap, or FALSE * if no bootstrap attempt was made. */ function drush_bootstrap_max_to_sitealias($site_record, $max_phase_index = NULL) { if ((array_key_exists('root', $site_record) && !array_key_exists('remote-host', $site_record))) { drush_sitealias_set_alias_context($site_record); drush_bootstrap_max($max_phase_index); return TRUE; } return FALSE; } /** * Helper function to collect any errors that occur during the bootstrap process. * Always returns FALSE, for convenience. */ function drush_bootstrap_error($code, $message = null) { $errors = drush_get_context('DRUSH_BOOTSTRAP_ERRORS'); $errors[$code] = $message; drush_set_context('DRUSH_BOOTSTRAP_ERRORS', $errors); return FALSE; } function _drush_bootstrap_output_prepare() { // Note that as soon as we set the DRUSH_BACKEND context, we change // the behavior of drush_log(). It is therefore important that we // should not set this context until immediately before we call ob_start // (i.e., in this function). $backend = drush_set_context('DRUSH_BACKEND', drush_get_option('backend')); $quiet = drush_get_context('DRUSH_QUIET'); if ($backend) { // Load options passed as a JSON encoded string through STDIN. $stdin_options = _drush_backend_get_stdin(); if (is_array($stdin_options)) { drush_set_context('stdin', $stdin_options); } // Add an output buffer handler to collect output/pass through backend // packets. Using a chunksize of 2 ensures that each line is flushed // straight away. if ($quiet) { // Pass through of backend packets, discard regular output. ob_start('drush_backend_output_discard', 2); } else { // Collect output. ob_start('drush_backend_output_collect', 2); } } // In non-backend quiet mode we start buffering and discards it on command // completion. if ($quiet && !$backend) { ob_start(); } } /** * Used by a Drush extension to request that its Composer autoload * files be loaded by Drush, if they have not already been. * * Usage: * * function mycommandfile_drush_init() { * drush_autoload(__FILE__) * } * */ function drush_autoload($commandfile) { $already_added = commandfiles_cache()->add($commandfile); $dir = dirname($commandfile); $candidates = array("vendor/autoload.php", "../../../vendor/autoload.php"); $drush_autoload_file = drush_get_context('DRUSH_VENDOR_PATH', ''); foreach ($candidates as $candidate) { $autoload = $dir . '/' . $candidate; if (file_exists($autoload) && (realpath($autoload) != $drush_autoload_file)) { include $autoload; } } }