Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load the autoloader as soon as the Drupal root is identified. #2865

Merged
merged 7 commits into from
Aug 9, 2017
Merged
17 changes: 10 additions & 7 deletions includes/drupal.inc
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ use Drush\Drush;
function drush_drupal_load_autoloader($drupal_root) {
static $autoloader = FALSE;

if (!$autoloader) {
$autoloader = require $drupal_root .'/autoload.php';
if ($autoloader === TRUE) {
// The autoloader was already require(). Assume that Drush and Drupal share an autoloader per
// "Point autoload.php to the proper vendor directory" - https://www.drupal.org/node/2404989
$autoloader = drush_get_context('DRUSH_CLASSLOADER');
}
$autoloadFilePath = $drupal_root .'/autoload.php';
if (!$autoloader && file_exists($autoloadFilePath)) {
$autoloader = require $autoloadFilePath;
}

if ($autoloader === TRUE) {
// The autoloader was already required. Assume that Drush and Drupal share an autoloader per
// "Point autoload.php to the proper vendor directory" - https://www.drupal.org/node/2404989
$autoloader = drush_get_context('DRUSH_CLASSLOADER');
}

return $autoloader;
}

Expand Down
53 changes: 30 additions & 23 deletions includes/preflight.inc
Original file line number Diff line number Diff line change
Expand Up @@ -497,19 +497,9 @@ function drush_preflight() {
drush_preflight_site();

// Check to see if anything changed during the 'site' preflight
// that might allow us to find our alias record now
// that might allow us to find our alias record now.
if (empty($alias_record)) {
$alias_record = _drush_sitealias_set_context_by_name($target_alias);

// If the site alias settings changed late in the preflight,
// then run the preflight for the root and site contexts again.
if (!empty($alias_record)) {
$remote_host = drush_get_option('remote-host');
if (!isset($remote_host)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edge case: a site alias defined in an alias file in a local Drupal site (identified via --root or the cwd), where the root defined in the alias is different than the root for the Drupal site containing the alias.

This code re-runs the preflight steps that sets up to bootstrap the selected Drupal site. I feel this makes the preflight harder to maintain, and prefer to remove it.

drush_preflight_root();
drush_preflight_site();
}
}
}

// Fail if we could not find the selected site alias.
Expand Down Expand Up @@ -544,6 +534,11 @@ function drush_preflight() {
function drush_preflight_root() {
$root = drush_get_option('root');
Drush::bootstrapManager()->locateRoot($root);
$root = Drush::bootstrapManager()->getRoot();

// Load the autoload file and provide it to the bootstrap manager.
$siteAutoloader = drush_drupal_load_autoloader($root);
Drush::bootstrapManager()->setAutoloader($siteAutoloader);

// Load the config options from Drupal's /drush, ../drush, and sites/all/drush directories,
// even prior to bootstrapping the root.
Expand Down Expand Up @@ -799,14 +794,21 @@ function drush_preflight_command_dispatch() {
$args = drush_get_arguments();
$command_name = array_shift($args);
$root = Drush::bootstrapManager()->getRoot();
$local_drush = drush_get_option('drush-script');
$is_local = drush_get_option('local');
$values = NULL;
if (!empty($root) && !empty($local_drush) && empty($is_local)) {

$local_drush = drush_get_option('drush-script');
if (!empty($local_drush)) {
if (!drush_is_absolute_path($local_drush)) {
$local_drush = $root . DIRECTORY_SEPARATOR . $local_drush;
}
$local_drush = realpath($local_drush);
}

$drupal_root_from_alias = drush_get_option('root', $root, 'alias');
$shouldRedispatch = (!empty($root)) && ($root != $drupal_root_from_alias);

if (!empty($root) && !empty($local_drush) && empty($is_local)) {
$this_drush = drush_find_drush();
// If there is a local Drush selected, and it is not the
// same Drush that is currently running, redispatch to it.
Expand All @@ -817,18 +819,23 @@ function drush_preflight_command_dispatch() {
// infinite loop.
if (file_exists($local_drush) && !drush_is_nested_directory(dirname($root), $this_drush)) {
$uri = drush_get_context('DRUSH_SELECTED_URI');
$aditional_options = array(
'root' => $root,
);
if (!empty($uri)) {
$aditional_options['uri'] = $uri;
}
// We need to chdir to the Drupal root here, for the
// benefit of the Drush wrapper.
chdir($root);
$values = drush_do_command_redispatch(is_array($command) ? $command : $command_name, $args, NULL, NULL, $local_drush, TRUE, $aditional_options);
$shouldRedispatch = true;
}
}

if ($shouldRedispatch) {
$aditional_options = [
'root' => $drupal_root_from_alias,
];
if (!empty($uri)) {
$aditional_options['uri'] = $uri;
}
// We need to chdir to the Drupal root here, for the
// benefit of the Drush wrapper.
chdir($root);
$values = drush_do_command_redispatch(is_array($command) ? $command : $command_name, $args, NULL, NULL, $local_drush, TRUE, $aditional_options);
}

// If the command sets the 'handle-remote-commands' flag, then we will short-circuit
// remote command dispatching and site-list command dispatching, and always let
// the command handler run on the local machine.
Expand Down
2 changes: 0 additions & 2 deletions includes/sitealias.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1772,8 +1772,6 @@ function _drush_sitealias_set_context_by_name($alias, $prefix = '') {
}

_drush_sitealias_cache_alias('@self', $site_alias_settings);
// Change the selected site to match the new --root and --uri, if any were set.
_drush_preflight_root_uri();
}
return $site_alias_settings;
}
Expand Down
11 changes: 11 additions & 0 deletions src/Boot/AutoloaderAwareInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
namespace Drush\Boot;

interface AutoloaderAwareInterface
{
public function setAutoloader($loader);

public function autoloader();

public function hasAutoloader();
}
22 changes: 22 additions & 0 deletions src/Boot/AutoloaderAwareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
namespace Drush\Boot;

trait AutoloaderAwareTrait
{
protected $loader;

public function setAutoloader($loader)
{
$this->loader = $loader;
}

public function autoloader()
{
return $this->loader;
}

public function hasAutoloader()
{
return isset($this->loader);
}
}
6 changes: 5 additions & 1 deletion src/Boot/BootstrapManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;

class BootstrapManager implements LoggerAwareInterface
class BootstrapManager implements LoggerAwareInterface, AutoloaderAwareInterface
{
use LoggerAwareTrait;
use AutoloaderAwareTrait;

/**
* @var DrupalFinder
Expand Down Expand Up @@ -137,6 +138,9 @@ public function bootstrapObjectForRoot($path)
{
foreach ($this->bootstrapCandidates as $candidate) {
if ($candidate->validRoot($path)) {
if ($candidate instanceof AutoloaderAwareInterface) {
$candidate->setAutoloader($this->autoloader());
}
return $candidate;
}
}
Expand Down
11 changes: 7 additions & 4 deletions src/Boot/DrupalBoot8.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@

use Drush\Log\LogLevel;

class DrupalBoot8 extends DrupalBoot
class DrupalBoot8 extends DrupalBoot implements AutoloaderAwareInterface
{
use AutoloaderAwareTrait;

/**
* @var \Drupal\Core\DrupalKernelInterface
Expand Down Expand Up @@ -40,8 +41,10 @@ public function validRoot($path)

public function getVersion($drupal_root)
{
// Load the autoloader so we can access the class constants.
drush_drupal_load_autoloader($drupal_root);
// Are the class constants available?
if (!$this->hasAutoloader()) {
throw new \Exception('Cannot access Drupal 8 class constants - Drupal autoloader not loaded yet.');
}
// Drush depends on bootstrap being loaded at this point.
require_once $drupal_root .'/core/includes/bootstrap.inc';
if (defined('\Drupal::VERSION')) {
Expand Down Expand Up @@ -129,7 +132,7 @@ public function bootstrapDrupalDatabase()
public function bootstrapDrupalConfiguration()
{
$this->request = Request::createFromGlobals();
$classloader = drush_drupal_load_autoloader(DRUPAL_ROOT);
$classloader = $this->autoloader();
// @todo - use Request::create() and then no need to set PHP superglobals
$kernelClass = new \ReflectionClass('\Drupal\Core\DrupalKernel');
if ($kernelClass->hasMethod('addServiceModifier')) {
Expand Down
63 changes: 63 additions & 0 deletions tests/siteAliasTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,69 @@
* @group base
*/
class saCase extends CommandUnishTestCase {
/**
* Covers the following responsibilities:
* - Dispatching a Drush command via an alias that is defined in a
* site-local alias file. The target alias points to a different
* Drupal site at a different docroot on the same system.
*/
function testSiteLocalAliasDispatch() {
$sites = $this->setUpDrupal(2, TRUE);

// Make a separate copy of the stage site so that we can test
// to see if we can switch to a separate site via an site-local alias.
$dev_root = $sites['dev']['root'];
$drush_sut = dirname($dev_root);
$other_sut = dirname($drush_sut) . '/drush-other-sut';
$other_root = $other_sut . '/web';
@mkdir($other_sut);
self::recursive_copy($dev_root, $other_root);

if (!file_exists($drush_sut . '/composer.json') || !file_exists($drush_sut . '/composer.lock')) {
$this->markTestSkipped('This test does not run in the highest / lowest configurations.');
}

copy($drush_sut . '/composer.json', $other_sut . '/composer.json');
copy($drush_sut . '/composer.lock', $other_sut . '/composer.lock');

// Hopefully this will run quickly from the cache.
passthru("composer --working-dir=$other_sut install");

$aliasPath = $dev_root . '/drush';
$aliasFile = "$aliasPath/aliases.drushrc.php";
$aliasContents = <<<EOD
<?php
// Written by Unish. This file is safe to delete.

\$aliases["other"] = array (
'root' => '$other_root',
'uri' => 'stage',
);
EOD;
if (!is_dir($aliasPath)) {
mkdir($aliasPath);
}
file_put_contents($aliasFile, $aliasContents);

// Ensure that we can access the 'other' alias from the context
// of the 'dev' site, and that it has the right drupal root.
$options = [
];
$this->drush('sa', array('@other'), $options, '@dev');
$output = $this->getOutput();
$this->assertContains("root: $other_root", $output);

// Ensure that we can get status on the 'other' alias when the
// root of the dev site is provided (to allow Drush to find the 'other' alias)
$options = [
'root' => $dev_root,
'format' => 'yaml',
];
$this->drush('core-status', [], $options, '@other');
$output = $this->getOutput();
$this->assertContains("root: $other_root", $output);
}

/**
* Covers the following responsibilities.
* - Dispatching a Drush command that uses strict option handling
Expand Down