--- /dev/null
+<?php
+
+namespace Drush\Boot;
+
+use Drush\Drush;
+use Drush\Log\LogLevel;
+use Drush\Sql\SqlBase;
+use Psr\Log\LoggerInterface;
+use Drupal\user\Entity\User;
+use Symfony\Component\HttpFoundation\Request;
+use Webmozart\PathUtil\Path;
+
+abstract class DrupalBoot extends BaseBoot
+{
+ /**
+ * Select the best URI for the provided cwd. Only called
+ * if the user did not explicitly specify a URI.
+ */
+ public function findUri($root, $cwd)
+ {
+ if (Path::isBasePath($root, $cwd)) {
+ $siteDir = $this->scanUpForUri($root, $cwd);
+ if ($siteDir) {
+ return basename($siteDir);
+ }
+ }
+ return 'default';
+ }
+
+ protected function scanUpForUri($root, $scan)
+ {
+ $root = Path::canonicalize($root);
+ while (!empty($scan)) {
+ if (file_exists("$scan/settings.php")) {
+ return $scan;
+ }
+ // Use Path::getDirectory instead of dirname to
+ // avoid certain bugs. Returns a canonicalized path.
+ $next = Path::getDirectory($scan);
+ if ($next == $scan) {
+ return false;
+ }
+ $scan = $next;
+ if ($scan == $root) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public function validRoot($path)
+ {
+ }
+
+ public function getVersion($drupal_root)
+ {
+ }
+
+ public function confPath($require_settings = true, $reset = false)
+ {
+ return confPath($require_settings, $reset);
+ }
+
+ /**
+ * Bootstrap phases used with Drupal:
+ *
+ * DRUSH_BOOTSTRAP_DRUSH = Only Drush.
+ * DRUSH_BOOTSTRAP_DRUPAL_ROOT = Find a valid Drupal root.
+ * DRUSH_BOOTSTRAP_DRUPAL_SITE = Find a valid Drupal site.
+ * DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION = Load the site's settings.
+ * DRUSH_BOOTSTRAP_DRUPAL_DATABASE = Initialize the database.
+ * DRUSH_BOOTSTRAP_DRUPAL_FULL = Initialize Drupal fully.
+ *
+ * The value is the name of the method of the Boot class to
+ * execute when bootstrapping. Prior to bootstrapping, a "validate"
+ * method is called, if defined. The validate method name is the
+ * bootstrap method name with "_validate" appended.
+ */
+ public function bootstrapPhases()
+ {
+ return parent::bootstrapPhases() + [
+ DRUSH_BOOTSTRAP_DRUPAL_ROOT => 'bootstrapDrupalRoot',
+ DRUSH_BOOTSTRAP_DRUPAL_SITE => 'bootstrapDrupalSite',
+ DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION => 'bootstrapDrupalConfiguration',
+ DRUSH_BOOTSTRAP_DRUPAL_DATABASE => 'bootstrapDrupalDatabase',
+ DRUSH_BOOTSTRAP_DRUPAL_FULL => 'bootstrapDrupalFull',
+ ];
+ }
+
+ public function bootstrapPhaseMap()
+ {
+ return parent::bootstrapPhaseMap() + [
+ 'root' => DRUSH_BOOTSTRAP_DRUPAL_ROOT,
+ 'site' => DRUSH_BOOTSTRAP_DRUPAL_SITE,
+ 'config' => DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION,
+ 'configuration' => DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION,
+ 'db' => DRUSH_BOOTSTRAP_DRUPAL_DATABASE,
+ 'database' => DRUSH_BOOTSTRAP_DRUPAL_DATABASE,
+ 'full' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
+ ];
+ }
+
+ /**
+ * Validate the DRUSH_BOOTSTRAP_DRUPAL_ROOT phase.
+ *
+ * In this function, we will check if a valid Drupal directory is available.
+ * We also determine the value that will be stored in the DRUSH_DRUPAL_ROOT
+ * context and DRUPAL_ROOT constant if it is considered a valid option.
+ */
+ public function bootstrapDrupalRootValidate()
+ {
+ $drupal_root = Drush::bootstrapManager()->getRoot();
+
+ if (empty($drupal_root)) {
+ return drush_bootstrap_error('DRUSH_NO_DRUPAL_ROOT', dt("A Drupal installation directory could not be found"));
+ }
+ // TODO: Perhaps $drupal_root is now ALWAYS valid by the time we get here.
+ if (!$this->legacyValidRootCheck($drupal_root)) {
+ return drush_bootstrap_error('DRUSH_INVALID_DRUPAL_ROOT', dt("The directory !drupal_root does not contain a valid Drupal installation", ['!drupal_root' => $drupal_root]));
+ }
+
+ $version = drush_drupal_version($drupal_root);
+ $major_version = drush_drupal_major_version($drupal_root);
+ if ($major_version <= 6) {
+ return drush_set_error('DRUSH_DRUPAL_VERSION_UNSUPPORTED', dt('Drush !drush_version does not support Drupal !major_version.', ['!drush_version' => Drush::getMajorVersion(), '!major_version' => $major_version]));
+ }
+
+ drush_bootstrap_value('drupal_root', $drupal_root);
+
+ return true;
+ }
+
+ protected function legacyValidRootCheck($root)
+ {
+ $bootstrap_class = Drush::bootstrapManager()->bootstrapObjectForRoot($root);
+ return $bootstrap_class != null;
+ }
+
+ /**
+ * Bootstrap Drush with a valid Drupal Directory.
+ *
+ * In this function, the pwd will be moved to the root
+ * of the Drupal installation.
+ *
+ * We also now load the drush.yml for this specific Drupal site.
+ * We can now include files from the Drupal tree, and figure
+ * out more context about the codebase, such as the version of Drupal.
+ */
+ public function bootstrapDrupalRoot()
+ {
+
+ $drupal_root = drush_set_context('DRUSH_DRUPAL_ROOT', drush_bootstrap_value('drupal_root'));
+ chdir($drupal_root);
+ $this->logger->log(LogLevel::BOOTSTRAP, dt("Change working directory to !drupal_root", ['!drupal_root' => $drupal_root]));
+ $version = drush_drupal_version();
+ $major_version = drush_drupal_major_version();
+
+ $core = $this->bootstrapDrupalCore($drupal_root);
+
+ // DRUSH_DRUPAL_CORE should point to the /core folder in Drupal 8+ or to DRUPAL_ROOT
+ // in prior versions.
+ define('DRUSH_DRUPAL_CORE', $core);
+
+ $this->logger->log(LogLevel::BOOTSTRAP, dt("Initialized Drupal !version root directory at !drupal_root", ["!version" => $version, '!drupal_root' => $drupal_root]));
+ }
+
+ /**
+ * VALIDATE the DRUSH_BOOTSTRAP_DRUPAL_SITE phase.
+ *
+ * In this function we determine the URL used for the command,
+ * and check for a valid settings.php file.
+ */
+ public function bootstrapDrupalSiteValidate()
+ {
+ }
+
+ /**
+ * Called by bootstrapDrupalSite to do the main work
+ * of the drush drupal site bootstrap.
+ */
+ public function bootstrapDoDrupalSite()
+ {
+ drush_set_context('DRUSH_URI', $this->uri);
+ $site = drush_set_context('DRUSH_DRUPAL_SITE', drush_bootstrap_value('site'));
+ $confPath = drush_set_context('DRUSH_DRUPAL_SITE_ROOT', drush_bootstrap_value('confPath'));
+
+ $this->logger->log(LogLevel::BOOTSTRAP, dt("Initialized Drupal site !site at !site_root", ['!site' => $site, '!site_root' => $confPath]));
+ }
+
+ /**
+ * Initialize a site on the Drupal root.
+ *
+ * We now set various contexts that we determined and confirmed to be valid.
+ * Additionally we load an optional drush.yml file in the site directory.
+ */
+ public function bootstrapDrupalSite()
+ {
+ $this->bootstrapDoDrupalSite();
+ }
+
+ /**
+ * Initialize and load the Drupal configuration files.
+ */
+ public function bootstrapDrupalConfiguration()
+ {
+ }
+
+ /**
+ * Validate the DRUSH_BOOTSTRAP_DRUPAL_DATABASE phase
+ *
+ * Attempt to make a working database connection using the
+ * database credentials that were loaded during the previous
+ * phase.
+ */
+ public function bootstrapDrupalDatabaseValidate()
+ {
+ // Drupal requires PDO, and Drush requires php 5.6+ which ships with PDO
+ // but PHP may be compiled with --disable-pdo.
+ if (!class_exists('\PDO')) {
+ $this->logger->log(LogLevel::BOOTSTRAP, dt('PDO support is required.'));
+ return false;
+ }
+ try {
+ $sql = SqlBase::create();
+ // Drush requires a database client program during its Drupal bootstrap.
+ $command = $sql->command();
+ if (drush_program_exists($command) === false) {
+ $this->logger->warning(dt('The command \'!command\' is required for preflight but cannot be found. Please install it and retry.', ['!command' => $command]));
+ return false;
+ }
+ if (!$sql->query('SELECT 1;')) {
+ $message = dt("Drush was not able to start (bootstrap) the Drupal database.\n");
+ $message .= dt("Hint: This may occur when Drush is trying to:\n");
+ $message .= dt(" * bootstrap a site that has not been installed or does not have a configured database. In this case you can select another site with a working database setup by specifying the URI to use with the --uri parameter on the command line. See `drush topic docs-aliases` for details.\n");
+ $message .= dt(" * connect the database through a socket. The socket file may be wrong or the php-cli may have no access to it in a jailed shell. See http://drupal.org/node/1428638 for details.\n");
+ $message .= dt('More information may be available by running `drush status`');
+ $this->logger->log(LogLevel::BOOTSTRAP, $message);
+ return false;
+ }
+ } catch (\Exception $e) {
+ $this->logger->log(LogLevel::DEBUG, dt('Unable to validate DB: @e', ['@e' => $e->getMessage()]));
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Test to see if the Drupal database has a specified
+ * table or tables.
+ *
+ * This is a bootstrap helper function designed to be called
+ * from the bootstrapDrupalDatabaseValidate() methods of
+ * derived DrupalBoot classes. If a database exists, but is
+ * empty, then the Drupal database bootstrap will fail. To
+ * prevent this situation, we test for some table that is needed
+ * in an ordinary bootstrap, and return FALSE from the validate
+ * function if it does not exist, so that we do not attempt to
+ * start the database bootstrap.
+ *
+ * Note that we must manually do our own prefix testing here,
+ * because the existing wrappers we have for handling prefixes
+ * depend on bootstrapping to the "database" phase, and therefore
+ * are not available to validate this same phase.
+ *
+ * @param $required_tables
+ * Array of table names, or string with one table name
+ *
+ * @return TRUE if all required tables exist in the database.
+ */
+ public function bootstrapDrupalDatabaseHasTable($required_tables)
+ {
+ try {
+ $sql = SqlBase::create();
+ $spec = $sql->getDbSpec();
+ $prefix = isset($spec['prefix']) ? $spec['prefix'] : null;
+ if (!is_array($prefix)) {
+ $prefix = ['default' => $prefix];
+ }
+ foreach ((array)$required_tables as $required_table) {
+ $prefix_key = array_key_exists($required_table, $prefix) ? $required_table : 'default';
+ $table_name = $prefix[$prefix_key] . $required_table;
+ if (!$sql->alwaysQuery("SELECT 1 FROM $table_name LIMIT 1;")) {
+ $this->logger->notice('Missing database table: '. $table_name);
+ return false;
+ }
+ }
+ } catch (Exception $e) {
+ // Usually the checks above should return a result without
+ // throwing an exception, but we'll catch any that are
+ // thrown just in case.
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Boostrap the Drupal database.
+ */
+ public function bootstrapDrupalDatabase()
+ {
+ // We presume that our derived classes will connect and then
+ // either fail, or call us via parent::
+ $this->logger->log(LogLevel::BOOTSTRAP, dt("Successfully connected to the Drupal database."));
+ }
+
+ /**
+ * Attempt to load the full Drupal system.
+ */
+ public function bootstrapDrupalFull()
+ {
+ }
+}