Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / vendor / drush / drush / src / Boot / BootstrapManager.php
diff --git a/vendor/drush/drush/src/Boot/BootstrapManager.php b/vendor/drush/drush/src/Boot/BootstrapManager.php
new file mode 100644 (file)
index 0000000..b24fd2f
--- /dev/null
@@ -0,0 +1,517 @@
+<?php
+
+namespace Drush\Boot;
+
+use Consolidation\AnnotatedCommand\AnnotationData;
+use Robo\Common\ConfigAwareTrait;
+use DrupalFinder\DrupalFinder;
+use Drush\Log\LogLevel;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
+use Robo\Contract\ConfigAwareInterface;
+
+class BootstrapManager implements LoggerAwareInterface, AutoloaderAwareInterface, ConfigAwareInterface
+{
+    use LoggerAwareTrait;
+    use AutoloaderAwareTrait;
+    use ConfigAwareTrait;
+
+    /**
+     * @var DrupalFinder
+     */
+    protected $drupalFinder;
+
+    /**
+     * @var \Drush\Boot\Boot[]
+     */
+    protected $bootstrapCandidates = [];
+
+    /**
+     * @var \Drush\Boot\Boot
+     */
+    protected $defaultBootstrapObject;
+
+    /**
+     * @var \Drush\Boot\Boot
+     */
+    protected $bootstrap;
+
+    /**
+     * @var string
+     */
+    protected $root;
+
+    /**
+     * @var string
+     */
+    protected $uri;
+
+    /**
+     * Constructor.
+     *
+     * @param \Drush\Boot\Boot
+     *   The default bootstrap object to use when there are
+     *   no viable candidates to use (e.g. no selected site)
+     */
+    public function __construct(Boot $default)
+    {
+        $this->defaultBootstrapObject = $default;
+
+        // Reset our bootstrap phase to the beginning
+        drush_set_context('DRUSH_BOOTSTRAP_PHASE', DRUSH_BOOTSTRAP_NONE);
+    }
+
+    /**
+     * Add a bootstrap object to the list of candidates
+     *
+     * @param \Drush\Boot\Boot|Array
+     *   List of boot candidates
+     */
+    public function add($candidateList)
+    {
+        foreach (func_get_args() as $candidate) {
+            $this->bootstrapCandidates[] = $candidate;
+        }
+    }
+
+    public function drupalFinder()
+    {
+        if (!isset($this->drupalFinder)) {
+            $this->drupalFinder = new DrupalFinder();
+        }
+        return $this->drupalFinder;
+    }
+
+    public function setDrupalFinder(DrupalFinder $drupalFinder)
+    {
+        $this->drupalFinder = $drupalFinder;
+    }
+
+    /**
+     * Return the framework root selected by the user.
+     */
+    public function getRoot()
+    {
+        return $this->drupalFinder()->getDrupalRoot();
+    }
+
+    /**
+     * Return the composer root for the selected Drupal site.
+     */
+    public function getComposerRoot()
+    {
+        return $this->drupalFinder()->getComposerRoot();
+    }
+
+    public function locateRoot($root, $start_path = null)
+    {
+        // TODO: Throw if we already bootstrapped a framework?
+
+        if (!isset($root)) {
+            $root = $this->getConfig()->cwd();
+        }
+        if (!$this->drupalFinder()->locateRoot($root)) {
+            //    echo ' Drush must be executed within a Drupal site.'. PHP_EOL;
+            //    exit(1);
+        }
+    }
+
+    /**
+     * Return the framework uri selected by the user.
+     */
+    public function getUri()
+    {
+        return $this->uri;
+    }
+
+    /**
+     * This method is called by the Application iff the user
+     * did not explicitly provide a URI.
+     */
+    public function selectUri($cwd)
+    {
+        $uri = $this->bootstrap()->findUri($this->getRoot(), $cwd);
+        $this->setUri($uri);
+        return $uri;
+    }
+
+    public function setUri($uri)
+    {
+        // TODO: Throw if we already bootstrapped a framework?
+        // n.b. site-install needs to set the uri.
+        $this->uri = $uri;
+        if ($this->bootstrap) {
+            $this->bootstrap->setUri($this->getUri());
+        }
+    }
+
+    /**
+     * Return the bootstrap object in use.  This will
+     * be the latched bootstrap object if we have started
+     * bootstrapping; otherwise, it will be whichever bootstrap
+     * object is best for the selected root.
+     *
+     * @return \Drush\Boot\Boot
+     */
+    public function bootstrap()
+    {
+        if ($this->bootstrap) {
+            return $this->bootstrap;
+        }
+        return $this->selectBootstrapClass();
+    }
+
+    /**
+     * Look up the best bootstrap class for the given location
+     * from the set of available candidates.
+     *
+     * @return \Drush\Boot\Boot
+     */
+    public function bootstrapObjectForRoot($path)
+    {
+        foreach ($this->bootstrapCandidates as $candidate) {
+            if ($candidate->validRoot($path)) {
+                // This is not necessary when the autoloader is inflected
+                // TODO: The autoloader is inflected in the symfony dispatch, but not the traditional Drush dispatcher
+                if ($candidate instanceof AutoloaderAwareInterface) {
+                    $candidate->setAutoloader($this->autoloader());
+                }
+                $candidate->setUri($this->getUri());
+                return $candidate;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 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::bootstrapf()
+     * will always return the same object.
+     */
+    protected function selectBootstrapClass()
+    {
+        // 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 = $this->bootstrapObjectForRoot($this->getRoot());
+        // If we have not found a bootstrap class by this point,
+        // then return our default bootstrap object.  The default bootstrap object
+        // should pass through all calls without doing anything that
+        // changes state in a CMS-specific way.
+        if ($bootstrap == null) {
+            $bootstrap = $this->defaultBootstrapObject;
+        }
+
+        return $bootstrap;
+    }
+
+    /**
+     * Once bootstrapping has started, we stash the bootstrap
+     * object being used, and do not allow it to change any
+     * longer.
+     */
+    public function latch($bootstrap)
+    {
+        $this->bootstrap = $bootstrap;
+    }
+
+    /**
+     * 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::bootstrapPhases()
+     */
+    public function bootstrapPhases($function_names = false)
+    {
+        $result = [];
+
+        if ($bootstrap = $this->bootstrap()) {
+            $result = $bootstrap->bootstrapPhases();
+            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.
+     * @param \Consolidation\AnnotatedCommand\AnnotationData $annotationData
+     *   Optional annotation data from the command.
+     *
+     * @return bool
+     *   TRUE if the specified bootstrap phase has completed.
+     *
+     * @see \Drush\Boot\Boot::bootstrapPhases()
+     */
+    public function doBootstrap($phase, $phase_max = false, AnnotationData $annotationData = null)
+    {
+        $bootstrap = $this->bootstrap();
+        $phases = $this->bootstrapPhases(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) {
+            $this->latch($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 = $this->bootstrapValidate($phase_index)) {
+                    if (method_exists($bootstrap, $current_phase) && !drush_get_error()) {
+                        $this->logger->log(LogLevel::BOOTSTRAP, 'Drush bootstrap phase: {function}()', ['function' => $current_phase]);
+                        $bootstrap->{$current_phase}($annotationData);
+                    }
+                    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', []);
+            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.
+     */
+    public function hasBootstrapped($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::bootstrapPhases()
+     */
+    public function bootstrapValidate($phase)
+    {
+        $bootstrap = $this->bootstrap();
+        $phases = $this->bootstrapPhases(true);
+        static $result_cache = [];
+
+        if (!array_key_exists($phase, $result_cache)) {
+            drush_set_context('DRUSH_BOOTSTRAP_ERRORS', []);
+            drush_set_context('DRUSH_BOOTSTRAP_VALUES', []);
+
+            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 string $bootstrapPhase
+     *   Name of phase to bootstrap to. Will be converted to appropriate index.
+     * @param \Consolidation\AnnotatedCommand\AnnotationData $annotationData
+     *   Optional annotation data from the command.
+     *
+     * @return bool
+     *   TRUE if the specified bootstrap phase has completed.
+     *
+     * @throws \Exception
+     *   Thrown when an unknown bootstrap phase is passed in the annotation
+     *   data.
+     */
+    public function bootstrapToPhase($bootstrapPhase, AnnotationData $annotationData = null)
+    {
+        $this->logger->log(LogLevel::BOOTSTRAP, 'Bootstrap to {phase}', ['phase' => $bootstrapPhase]);
+        $phase = $this->bootstrap()->lookUpPhaseIndex($bootstrapPhase);
+        if (!isset($phase)) {
+            throw new \Exception(dt('Bootstrap phase !phase unknown.', ['!phase' => $bootstrapPhase]));
+        }
+        // Do not attempt to bootstrap to a phase that is unknown to the selected bootstrap object.
+        $phases = $this->bootstrapPhases();
+        if (!array_key_exists($phase, $phases) && ($phase >= 0)) {
+            return false;
+        }
+        return $this->bootstrapToPhaseIndex($phase, $annotationData);
+    }
+
+    protected function maxPhaseLimit($bootstrap_str)
+    {
+        $bootstrap_words = explode(' ', $bootstrap_str);
+        array_shift($bootstrap_words);
+        if (empty($bootstrap_words)) {
+            return null;
+        }
+        $stop_phase_name = array_shift($bootstrap_words);
+        return $this->bootstrap()->lookUpPhaseIndex($stop_phase_name);
+    }
+
+    /**
+     * Bootstrap to the specified phase.
+     *
+     * @param int $max_phase_index
+     *   Only attempt bootstrap to the specified level.
+     * @param \Consolidation\AnnotatedCommand\AnnotationData $annotationData
+     *   Optional annotation data from the command.
+     *
+     * @return bool
+     *   TRUE if the specified bootstrap phase has completed.
+     */
+    public function bootstrapToPhaseIndex($max_phase_index, AnnotationData $annotationData = null)
+    {
+        if ($max_phase_index == DRUSH_BOOTSTRAP_MAX) {
+            // Try get a max phase.
+            $bootstrap_str = $annotationData->get('bootstrap');
+            $stop_phase = $this->maxPhaseLimit($bootstrap_str);
+            $this->bootstrapMax($stop_phase);
+            return true;
+        }
+
+        $this->logger->log(LogLevel::BOOTSTRAP, 'Drush bootstrap phase {phase}', ['phase' => $max_phase_index]);
+        $phases = $this->bootstrapPhases();
+        $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;
+            }
+
+            $this->logger->log(LogLevel::BOOTSTRAP, 'Try to validate bootstrap phase {phase}', ['phase' => $max_phase_index]);
+
+            if ($this->bootstrapValidate($phase_index)) {
+                if ($phase_index > drush_get_context('DRUSH_BOOTSTRAP_PHASE', DRUSH_BOOTSTRAP_NONE)) {
+                    $this->logger->log(LogLevel::BOOTSTRAP, 'Try to bootstrap at phase {phase}', ['phase' => $max_phase_index]);
+                    $result = $this->doBootstrap($phase_index, $max_phase_index, $annotationData);
+                }
+            } else {
+                $this->logger->log(LogLevel::BOOTSTRAP, 'Could not bootstrap at phase {phase}', ['phase' => $max_phase_index]);
+                $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.
+     * @param \Consolidation\AnnotatedCommand\AnnotationData $annotationData
+     *   Optional annotation data from the command.
+     *
+     * @return int
+     *   The maximum phase to which we bootstrapped.
+     */
+    public function bootstrapMax($max_phase_index = false, AnnotationData $annotationData = null)
+    {
+        // Bootstrap as far as we can without throwing an error, but log for
+        // debugging purposes.
+
+        $phases = $this->bootstrapPhases(true);
+        if (!$max_phase_index) {
+            $max_phase_index = count($phases);
+        }
+
+        if ($max_phase_index >= count($phases)) {
+            $this->logger->log(LogLevel::DEBUG, 'Trying to bootstrap as far as we can');
+        }
+
+        // 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 ($this->bootstrapValidate($phase_index)) {
+                if ($phase_index > drush_get_context('DRUSH_BOOTSTRAP_PHASE')) {
+                    $this->doBootstrap($phase_index, $max_phase_index, $annotationData);
+                }
+            } else {
+                // $this->bootstrapValidate() only logs successful validations. For us,
+                // knowing what failed can also be important.
+                $previous = drush_get_context('DRUSH_BOOTSTRAP_PHASE');
+                $this->logger->log(LogLevel::DEBUG, 'Bootstrap phase {function}() failed to validate; continuing at {current}()', ['function' => $current_phase, 'current' => $phases[$previous]]);
+                break;
+            }
+        }
+
+        return drush_get_context('DRUSH_BOOTSTRAP_PHASE');
+    }
+}