From d77912610458755dc6a4994d1f20d8ea0f9682e8 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Tue, 3 Feb 2015 11:02:43 -0800 Subject: [PATCH 01/14] Preliminary implementation of drush_autoload() function. --- includes/environment.inc | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/includes/environment.inc b/includes/environment.inc index c77f5d8b26..8b3239b74a 100644 --- a/includes/environment.inc +++ b/includes/environment.inc @@ -133,6 +133,50 @@ function drush_cwd() { return $path; } +/** + * Automatically loads a Drush extension's autoload file. + * Does nothing if the autoload file was already included + * (e.g. if this drush extension was installed via the composer.json + * in the current Drupal site's project). + * + * Generally speaking, detecting prior autoloading should + * not be necessary. If the drush extension was installed via + * the project's composer.json file, then the extension should + * be located inside the Drupal project, should not be shared + * by any other Drupal project, and should not have its own + * 'vendor' directory. + * + * If the extension is standalone, on the other hand, then it + * might exist inside a module directory, or in some global + * location (e.g. ~/.drush). If it has its own composer.json, + * then it should also have its own vendor directory and + * autoload.php file. + * + * TODO: Test with composer_manager. We might need to no-op + * this function if it is in use. + * + * TODO: Determine if it makes sense for Drush to call + * 'composer install' for standalone Drush extensions that + * have a composer.json file, but no 'vendor' directory. + * Perhaps we would do this at 'drush dl' time. + * + * Usage: + * + * function mydrushextension_drush_init() + * drush_autoload(__DIR__); + * } + */ +function drush_autoload($base_dir) { + $candidates = array("vendor/autoload.php"); + + foreach ($candidates as $candidate) { + $autoload = $base_dir . '/' . $candidate; + if (file_exists(autoload)) { + include $autoload; + } + } +} + /** * Converts a Windows path (dir1\dir2\dir3) into a Unix path (dir1/dir2/dir3). * Also converts a cygwin "drive emulation" path (/cygdrive/c/dir1) into a From 785aaa92ab8bfe59cfb119eca6b87a9ce10e20bf Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 4 Feb 2015 15:22:43 -0800 Subject: [PATCH 02/14] Factor out drush_add_commandfile from _drush_add_commandfiles, and call it from drush_autoload. --- includes/command.inc | 77 ++++++++++------- includes/drupal.inc | 1 + includes/environment.inc | 171 ++++++++++++++++++++++++++++++++++---- includes/preflight.inc | 10 ++- tests/commandUnitTest.php | 8 +- 5 files changed, 214 insertions(+), 53 deletions(-) diff --git a/includes/command.inc b/includes/command.inc index b1ddb7405f..ad9216e03c 100644 --- a/includes/command.inc +++ b/includes/command.inc @@ -1368,11 +1368,44 @@ function drush_commandfile_list() { return drush_get_context('DRUSH_COMMAND_FILES', array()); } +/** + * Register a single commandfile with Drush. + * + * Called by `drush_autoload()` and `_drush_add_commandfiles()`. + */ +function drush_add_commandfile($commandfile) { + $load_command = FALSE; + $cache =& drush_get_context('DRUSH_COMMAND_FILES', array()); + $deferred =& drush_get_context('DRUSH_DEFERRED_COMMAND_FILES', array()); + $module = basename($commandfile); + $module = preg_replace('/\.*drush[0-9]*\.inc/', '', $module); + $module_versionless = preg_replace('/\.d([0-9]+)$/', '', $module); + if (!isset($cache[$module_versionless])) { + $drupal_version = ''; + if (preg_match('/\.d([0-9]+)$/', $module, $matches)) { + $drupal_version = $matches[1]; + } + if (empty($drupal_version) || ($drupal_version == drush_drupal_major_version())) { + $load_command = TRUE; + $cache[$module_versionless] = $commandfile; + require_once $commandfile; + unset($deferred[$module]); + } + elseif (!empty($drupal_version)) { + // Signal that we should try again on + // the next bootstrap phase. + $deferred[$module] = $commandfile; + } + } + return $load_command; +} + function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { static $evaluated = array(); - static $deferred = array(); + $needs_sort = FALSE; $cache =& drush_get_context('DRUSH_COMMAND_FILES', array()); + $deferred =& drush_get_context('DRUSH_DEFERRED_COMMAND_FILES', array()); if (count($searchpath)) { if (!$reset) { @@ -1423,42 +1456,22 @@ function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { // Check to see if the commandfile is valid for this version of Drupal // and is still present on filesystem (in case of cached commandfile list). foreach ($list as $module => $filename) { - $load_command = TRUE; - $module_versionless = preg_replace('/\.d([0-9]+)$/', '', $module); - if (!isset($cache[$module_versionless])) { - $drupal_version = ''; - if (preg_match('/\.d([0-9]+)$/', $module, $matches)) { - $drupal_version = $matches[1]; - } - if (!empty($drupal_version) && ($drupal_version != drush_drupal_major_version())) { - $load_command = FALSE; - } + // Only try to require if the file exists. If not, a file from the + // command file cache may not be available anymore, in which case + // we rebuild the cache for this phase. + if ($filepath = realpath($filename)) { + $load_command = drush_add_commandfile($filepath); if ($load_command) { - // Only try to require if the file exists. If not, a file from the - // command file cache may not be available anymore, in which case - // we rebuild the cache for this phase. - if ($filepath = realpath($filename)) { - $cache[$module_versionless] = $filename; - require_once $filepath; - unset($deferred[$module]); - } - elseif (!$reset) { - _drush_add_commandfiles($searchpath, $phase, TRUE); - } - } - else { - unset($list[$module]); - // Signal that we should try again on - // the next bootstrap phase. We set - // the flag to the filename of the first - // module we find so that only that one - // will be retried. - $deferred[$module] = $filename; + $needs_sort = TRUE; } } + elseif (!$reset) { + _drush_add_commandfiles($searchpath, $phase, TRUE); + $needs_sort = FALSE; + } } - if (count($list)) { + if ($needs_sort) { ksort($cache); } } diff --git a/includes/drupal.inc b/includes/drupal.inc index c2004fd899..f48eb03fe6 100644 --- a/includes/drupal.inc +++ b/includes/drupal.inc @@ -14,6 +14,7 @@ function drush_drupal_load_autoloader($drupal_root, $new_autoloader = NULL) { $autoloader = $new_autoloader; } if (!$autoloader) { + // Should this just be 'require'? See drush_preflight_prepare() $autoloader = require_once $drupal_root .'/core/vendor/autoload.php'; } return $autoloader; diff --git a/includes/environment.inc b/includes/environment.inc index 8b3239b74a..194c23b209 100644 --- a/includes/environment.inc +++ b/includes/environment.inc @@ -152,27 +152,168 @@ function drush_cwd() { * then it should also have its own vendor directory and * autoload.php file. * + * Scenarios: + * + * 1) Loaded in a global location + * $HOME/.drush/baz/baz.drush.inc + * /usr/share/drush/commands/baz/baz.drush.inc + * + * These extensions will NOT be in project's composer.json + * Must load autoload file if it exists + * It will be at vendor/autoload.php + * + * 2) Included with a module + * $ROOT/sites/all/modules/foo/foo.drush.inc + * $ROOT/sites/all/modules/foo/subfoo/subfoo.drush.inc + * + * These extensions MIGHT be in project's composer.json + * If they are not, there should be a vendor/autoload.php + * + * n.b. In this instance, the vendor/autoload might + * be an arbitrary number of folders above the commandfile + * e.g. if the module foo has sites/all/modules/foo/modules/subfoo/subfoo.drush.inc. + * We need to figure out what composer manager does in + * this instance, though, as modules that have composer.json + * must either use composer manager, or be using a site-local + * Drush. We do not support anything else. + * + * 3) In a Drupal site's 'drush' directory + * $ROOT/drush/baz/baz.drush.inc + * $ROOT/sites/all/drush/baz/baz.drush.inc + * + * These extensions MIGHT be in project's composer.json + * If they are not, there should be a vendor/autoload.php + * + * 4) In a Drupal site's 'vendor' directory + * $ROOT/sites/all/vendor/bar/baz/baz.drush.inc + * $ROOT/core/vendor/bar/baz/baz.drush.inc + * + * These extensions ARE in project's composer.json + * There will NEVER be a local 'vendor' directory. + * + * 5) Installed via composer in a global location + * $HOME/.drush/vendor/bar/baz/baz.drush.inc + * /usr/share/drush/commands/vendor/bar/baz/baz.drush.inc + * ^^^^ + * (configurable part -- defaults to ALLUSERSPROFILE/Drush on Windows) + * + * This is a bit of an odd duck, but folks do this because + * `drush dl` does not support downloading extensions from + * Packagist/GitHub/etc., so `cd ~/.drush && composer require ...` + * is the current workaround. + * + * These extensions will NOT be in project's composer.json. + * Must load autoload file if it exists; however, it might + * be in the same relative location as (4). + * + * Required actions: + * + * a) If a commandfile is in project's 'vendor' directory, + * then we must call drush_add_commandfile(__FILE__). + * If it is anywhere else, it will already have been added. + * + * Actually, if the commandfile is in the project's + * composer.json file, and a custom installer has + * routed it to some location such as ROOT/drush, + * then at drush_autoload() time, it will NOT YET be + * added. drush_add_commandfile() is idempotent; we + * could therefore always call it from drush_autoload(). + * We can even use the result of drush_add_commandfile() + * ('already loaded') as an indicator of whether the + * commandfile is in a Drupal site's project composer.json + * file. + * + * * If a commandfile has NOT been added when + * drush_autoload() is called, then it means that + * this commandfile was included by the Composer + * autoloader, which means that we DO NOT need to + * find and include the vendor/autoload.php file. + * + * b) If a commandfile is NOT in a project's composer.json, + * then we need to load the project's autoload file. + * + * If the autoload file is needed, it will usually be + * found at vendor/autoload.php. If the extension is + * in the project's composer.json file, then we should + * never find the local vendor/autoload.php file. + * + * * Include __DIR__/vendor/autoload.php if it exists + * * Include __DIR__/../../../vendor/autoload.php if it + * + * Performance optimization: + * + * In the case of ../../../vendor/autoload.php, + * AKA ../../autoload.php, then we MIGHT want + * to include it. In case (5), we want it, but in + * case (4) we do not. + * + * Including an autoload file twice is innocuous, + * so we could just invariantly include it. This + * might have some small performance impact, though. + * In case (5), we are guarenteed that the path will + * have /drush/ or /.drush/. In case (4) it should + * not, but this is not guarenteed. In case (4), + * we know where the Drupal root is, though, so we + * can use realpath to determine if our relative + * autoload.php is the project autoload file. + * + * * Do NOT include __DIR__/../../../vendor/autoload.php + * if it is one of: + * - $ROOT/sites/all/vendor/autoload.php + * - $ROOT/core/vendor/autoload.php + * + * Problem, though; we have not determined the + * site root at the time that Drush includes + * vendor/autoload.php, which is when composer + * will start including Drush commandfiles (presuming + * that the 'autoload' section of the Drush + * extension's composer.json file). If we are + * using a site-local Drush, though, then we know + * that the Drupal site's vendor/autoload.php file + * is the same as the Drush vendor/autoload.php + * file. We can therefore note the autoload file + * that Drush includes, and skip including the + * Drush extension's autoload file if it is at the + * same path as the Drush autoload file. + * + * Further Performance optimization: + * + * Some modules or drush extensions might have more than + * one *.drush.inc file, although this is rare. In these + * cases, though, each commandfile will call drush_autoload(__FILE__), + * although only the first one called will need to load + * the autoload.php file. We could just let them be included + * multiple times, which should be innocuous, or we could + * cache the list of locations where we have already included + * autoloaders from, and skip those on subsequent updates. + * + * This is, perhaps, unnecessary, as Drush will not include + * the autoloader for commandfiles that were managed by the + * Drush project's composer.json file, which is our ultimate + * goal for composer-managed sites. + * * TODO: Test with composer_manager. We might need to no-op * this function if it is in use. * - * TODO: Determine if it makes sense for Drush to call - * 'composer install' for standalone Drush extensions that - * have a composer.json file, but no 'vendor' directory. - * Perhaps we would do this at 'drush dl' time. - * * Usage: * - * function mydrushextension_drush_init() - * drush_autoload(__DIR__); - * } + * On the last line of any *.drush.inc commandfile, add: + * + * drush_autoload(__FILE__); */ -function drush_autoload($base_dir) { - $candidates = array("vendor/autoload.php"); - - foreach ($candidates as $candidate) { - $autoload = $base_dir . '/' . $candidate; - if (file_exists(autoload)) { - include $autoload; +function drush_autoload($commandfile) { + $already_added = drush_add_commandfile($commandfile); + + if (!$already_added) { + $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; + } } } } diff --git a/includes/preflight.inc b/includes/preflight.inc index 39a12ae7d8..88bc98ef67 100644 --- a/includes/preflight.inc +++ b/includes/preflight.inc @@ -20,16 +20,19 @@ function drush_preflight_prepare() { // Check for a local composer install or a global composer install. Vendor dirs are in different spots). if (file_exists($local_vendor_path)) { - require $local_vendor_path; + $vendor_path = $local_vendor_path; } elseif (file_exists($global_vendor_path)) { - require $global_vendor_path; + $vendor_path = $global_vendor_path; } else { $msg = "Unable to load autoload.php. Drush now requires Composer in order to install its dependencies and autoload classes. Please see README.md\n"; fwrite(STDERR, $msg); return FALSE; } + // drush_drupal_load_autoloader() uses require_once. Is this going to be + // a problem if $vendor_path == $drupal_root .'/core/vendor/autoload.php' + require $vendor_path; require_once DRUSH_BASE_PATH . '/includes/environment.inc'; require_once DRUSH_BASE_PATH . '/includes/command.inc'; @@ -46,6 +49,9 @@ function drush_preflight_prepare() { require_once DRUSH_BASE_PATH . '/includes/filesystem.inc'; require_once DRUSH_BASE_PATH . '/includes/dbtng.inc'; + // Cache our vendor path + drush_set_context('DRUSH_VENDOR_PATH', dirname(realpath($vendor_path))); + // Terminate immediately unless invoked as a command line script if (!drush_verify_cli()) { return drush_set_error('DRUSH_REQUIREMENTS_ERROR', dt('Drush is designed to run via the command line.')); diff --git a/tests/commandUnitTest.php b/tests/commandUnitTest.php index 9234103414..104458c93c 100644 --- a/tests/commandUnitTest.php +++ b/tests/commandUnitTest.php @@ -32,10 +32,10 @@ function testCommandVersionSpecific() { drush_set_context('DRUSH_INCLUDE', array($path)); drush_preflight(); $loaded = drush_commandfile_list(); - $this->assertTrue(in_array($files[0], $loaded), 'Loaded a version-specific command file.'); - $this->assertTrue(in_array($files[1], $loaded), 'Loaded a version-specific command directory.'); - $this->assertFalse(in_array($files[2], $loaded), 'Did not load a mismatched version-specific command file.'); - $this->assertFalse(in_array($files[3], $loaded), 'Did not load a a mismatched version-specific command directory.'); + $this->assertTrue(in_array(realpath($files[0]), $loaded), 'Loaded a version-specific command file.'); + $this->assertTrue(in_array(realpath($files[1]), $loaded), 'Loaded a version-specific command directory.'); + $this->assertFalse(in_array(realpath($files[2]), $loaded), 'Did not load a mismatched version-specific command file.'); + $this->assertFalse(in_array(realpath($files[3]), $loaded), 'Did not load a a mismatched version-specific command directory.'); } /** From 490c17b130aac6faac37eb8ceec690c58677b175 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 4 Feb 2015 15:52:01 -0800 Subject: [PATCH 03/14] Test drush_autoload --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d36f5ea3a5..31766891b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ branches: - master - 6.x - 5.x + - drush-autoload language: php php: # See master-fulltest branch for broader PHP version testing. From d4e8000118af473b415ba6d2c6d1582ca9752bb0 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Thu, 5 Feb 2015 18:41:23 -0800 Subject: [PATCH 04/14] #572: Move commandfile cache to its own class, so that composer libraries can interact with Drush via Drush\Drush::autoload(). --- includes/command.inc | 43 ++----------- includes/environment.inc | 3 +- includes/preflight.inc | 1 + lib/Drush/Boot/command.inc | 2 +- lib/Drush/Command/Commandfiles.php | 70 +++++++++++++++++++++ lib/Drush/Command/CommandfilesInterface.php | 10 +++ lib/Drush/Drush.php | 63 +++++++++++++++++++ 7 files changed, 151 insertions(+), 41 deletions(-) create mode 100644 lib/Drush/Command/Commandfiles.php create mode 100644 lib/Drush/Command/CommandfilesInterface.php create mode 100644 lib/Drush/Drush.php diff --git a/includes/command.inc b/includes/command.inc index ad9216e03c..b05219c847 100644 --- a/includes/command.inc +++ b/includes/command.inc @@ -1365,48 +1365,13 @@ function drush_command_normalize_name($command_name) { * command files. */ function drush_commandfile_list() { - return drush_get_context('DRUSH_COMMAND_FILES', array()); -} - -/** - * Register a single commandfile with Drush. - * - * Called by `drush_autoload()` and `_drush_add_commandfiles()`. - */ -function drush_add_commandfile($commandfile) { - $load_command = FALSE; - $cache =& drush_get_context('DRUSH_COMMAND_FILES', array()); - $deferred =& drush_get_context('DRUSH_DEFERRED_COMMAND_FILES', array()); - $module = basename($commandfile); - $module = preg_replace('/\.*drush[0-9]*\.inc/', '', $module); - $module_versionless = preg_replace('/\.d([0-9]+)$/', '', $module); - if (!isset($cache[$module_versionless])) { - $drupal_version = ''; - if (preg_match('/\.d([0-9]+)$/', $module, $matches)) { - $drupal_version = $matches[1]; - } - if (empty($drupal_version) || ($drupal_version == drush_drupal_major_version())) { - $load_command = TRUE; - $cache[$module_versionless] = $commandfile; - require_once $commandfile; - unset($deferred[$module]); - } - elseif (!empty($drupal_version)) { - // Signal that we should try again on - // the next bootstrap phase. - $deferred[$module] = $commandfile; - } - } - return $load_command; + return Drush\Drush::commandfiles_cache()->get(); } function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { static $evaluated = array(); $needs_sort = FALSE; - $cache =& drush_get_context('DRUSH_COMMAND_FILES', array()); - $deferred =& drush_get_context('DRUSH_DEFERRED_COMMAND_FILES', array()); - if (count($searchpath)) { if (!$reset) { // Assemble a cid specific to the bootstrap phase and searchpaths. @@ -1427,7 +1392,7 @@ function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { // Build a list of all of the modules to attempt to load. // Start with any modules deferred from a previous phase. - $list = $deferred; + $list = Drush\Drush::commandfiles_cache()->deferred(); if (isset($cached_list)) { $list = array_merge($list, $cached_list); } @@ -1460,7 +1425,7 @@ function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { // command file cache may not be available anymore, in which case // we rebuild the cache for this phase. if ($filepath = realpath($filename)) { - $load_command = drush_add_commandfile($filepath); + $load_command = Drush\Drush::commandfiles_cache()->add($filepath); if ($load_command) { $needs_sort = TRUE; } @@ -1472,7 +1437,7 @@ function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { } if ($needs_sort) { - ksort($cache); + Drush\Drush::commandfiles_cache()->sort(); } } } diff --git a/includes/environment.inc b/includes/environment.inc index 194c23b209..b4c4c2ce94 100644 --- a/includes/environment.inc +++ b/includes/environment.inc @@ -300,7 +300,7 @@ function drush_cwd() { * On the last line of any *.drush.inc commandfile, add: * * drush_autoload(__FILE__); - */ + * function drush_autoload($commandfile) { $already_added = drush_add_commandfile($commandfile); @@ -317,6 +317,7 @@ function drush_autoload($commandfile) { } } } + */ /** * Converts a Windows path (dir1\dir2\dir3) into a Unix path (dir1/dir2/dir3). diff --git a/includes/preflight.inc b/includes/preflight.inc index 88bc98ef67..025989f280 100644 --- a/includes/preflight.inc +++ b/includes/preflight.inc @@ -30,6 +30,7 @@ function drush_preflight_prepare() { fwrite(STDERR, $msg); return FALSE; } + // drush_drupal_load_autoloader() uses require_once. Is this going to be // a problem if $vendor_path == $drupal_root .'/core/vendor/autoload.php' require $vendor_path; diff --git a/lib/Drush/Boot/command.inc b/lib/Drush/Boot/command.inc index d864e66960..fc7b5a63ab 100644 --- a/lib/Drush/Boot/command.inc +++ b/lib/Drush/Boot/command.inc @@ -135,7 +135,7 @@ function drush_command_belongs_to_disabled_module() { } else { // The command does not define Drupal dependencies. Derive them. - $command_files = drush_get_context('DRUSH_COMMAND_FILES', array()); + $command_files = Drush\Drush::commandfiles_cache()->get(); $command_path = $commands[$command_name]['path'] . DIRECTORY_SEPARATOR . $commands[$command_name]['commandfile'] . '.drush.inc'; $modules = array_search($command_path, $command_files); } diff --git a/lib/Drush/Command/Commandfiles.php b/lib/Drush/Command/Commandfiles.php new file mode 100644 index 0000000000..0c944719a7 --- /dev/null +++ b/lib/Drush/Command/Commandfiles.php @@ -0,0 +1,70 @@ +cache = array(); + $this->deferred = array(); + } + + function get() { + return $this->cache; + } + + function deferred() { + return $this->deferred; + } + + function sort() { + ksort($this->cache); + } + + function add($commandfile) { + $load_command = FALSE; + + $module = basename($commandfile); + $module = preg_replace('/\.*drush[0-9]*\.inc/', '', $module); + $module_versionless = preg_replace('/\.d([0-9]+)$/', '', $module); + if (!isset($this->cache[$module_versionless])) { + $drupal_version = ''; + if (preg_match('/\.d([0-9]+)$/', $module, $matches)) { + $drupal_version = $matches[1]; + } + if (empty($drupal_version)) { + $load_command = TRUE; + } + else { + if (function_exists('drush_drupal_major_version') && ($drupal_version == drush_drupal_major_version())) { + $load_command = TRUE; + } + else { + // Signal that we should try again on + // the next bootstrap phase. + $this->deferred[$module] = $commandfile; + } + } + if ($load_command) { + $this->cache[$module_versionless] = $commandfile; + require_once $commandfile; + unset($this->deferred[$module]); + } + } + return $load_command; + } +} diff --git a/lib/Drush/Command/CommandfilesInterface.php b/lib/Drush/Command/CommandfilesInterface.php new file mode 100644 index 0000000000..acb7c2cdb6 --- /dev/null +++ b/lib/Drush/Command/CommandfilesInterface.php @@ -0,0 +1,10 @@ +add($commandfile); + + if (!$already_added) { + $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; + } + } + } + } + + // ====== Things used internally by Drush ====== + + static function set_commandfiles_cache($commandfiles_cache) { + self::$commandfiles_cache = $commandfiles_cache; + } + + static function commandfiles_cache() { + if (!isset(self::$commandfiles_cache)) { + self::$commandfiles_cache = new Command\Commandfiles(); + } + return self::$commandfiles_cache; + } +} From 2b831a910bef09ce04fbecb089fe6541ce052334 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Fri, 6 Feb 2015 11:53:37 -0800 Subject: [PATCH 05/14] Remove drush-autoload branch from travis.yml. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 31766891b6..d36f5ea3a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ branches: - master - 6.x - 5.x - - drush-autoload language: php php: # See master-fulltest branch for broader PHP version testing. From 6c983c5142dc7b1fcf1882ee7d33f09ee01a7035 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Fri, 6 Feb 2015 11:56:21 -0800 Subject: [PATCH 06/14] Remove drush_autoload function from environment.inc; this function is now Drush\Drush::autoload(). --- includes/environment.inc | 186 --------------------------------------- 1 file changed, 186 deletions(-) diff --git a/includes/environment.inc b/includes/environment.inc index b4c4c2ce94..c77f5d8b26 100644 --- a/includes/environment.inc +++ b/includes/environment.inc @@ -133,192 +133,6 @@ function drush_cwd() { return $path; } -/** - * Automatically loads a Drush extension's autoload file. - * Does nothing if the autoload file was already included - * (e.g. if this drush extension was installed via the composer.json - * in the current Drupal site's project). - * - * Generally speaking, detecting prior autoloading should - * not be necessary. If the drush extension was installed via - * the project's composer.json file, then the extension should - * be located inside the Drupal project, should not be shared - * by any other Drupal project, and should not have its own - * 'vendor' directory. - * - * If the extension is standalone, on the other hand, then it - * might exist inside a module directory, or in some global - * location (e.g. ~/.drush). If it has its own composer.json, - * then it should also have its own vendor directory and - * autoload.php file. - * - * Scenarios: - * - * 1) Loaded in a global location - * $HOME/.drush/baz/baz.drush.inc - * /usr/share/drush/commands/baz/baz.drush.inc - * - * These extensions will NOT be in project's composer.json - * Must load autoload file if it exists - * It will be at vendor/autoload.php - * - * 2) Included with a module - * $ROOT/sites/all/modules/foo/foo.drush.inc - * $ROOT/sites/all/modules/foo/subfoo/subfoo.drush.inc - * - * These extensions MIGHT be in project's composer.json - * If they are not, there should be a vendor/autoload.php - * - * n.b. In this instance, the vendor/autoload might - * be an arbitrary number of folders above the commandfile - * e.g. if the module foo has sites/all/modules/foo/modules/subfoo/subfoo.drush.inc. - * We need to figure out what composer manager does in - * this instance, though, as modules that have composer.json - * must either use composer manager, or be using a site-local - * Drush. We do not support anything else. - * - * 3) In a Drupal site's 'drush' directory - * $ROOT/drush/baz/baz.drush.inc - * $ROOT/sites/all/drush/baz/baz.drush.inc - * - * These extensions MIGHT be in project's composer.json - * If they are not, there should be a vendor/autoload.php - * - * 4) In a Drupal site's 'vendor' directory - * $ROOT/sites/all/vendor/bar/baz/baz.drush.inc - * $ROOT/core/vendor/bar/baz/baz.drush.inc - * - * These extensions ARE in project's composer.json - * There will NEVER be a local 'vendor' directory. - * - * 5) Installed via composer in a global location - * $HOME/.drush/vendor/bar/baz/baz.drush.inc - * /usr/share/drush/commands/vendor/bar/baz/baz.drush.inc - * ^^^^ - * (configurable part -- defaults to ALLUSERSPROFILE/Drush on Windows) - * - * This is a bit of an odd duck, but folks do this because - * `drush dl` does not support downloading extensions from - * Packagist/GitHub/etc., so `cd ~/.drush && composer require ...` - * is the current workaround. - * - * These extensions will NOT be in project's composer.json. - * Must load autoload file if it exists; however, it might - * be in the same relative location as (4). - * - * Required actions: - * - * a) If a commandfile is in project's 'vendor' directory, - * then we must call drush_add_commandfile(__FILE__). - * If it is anywhere else, it will already have been added. - * - * Actually, if the commandfile is in the project's - * composer.json file, and a custom installer has - * routed it to some location such as ROOT/drush, - * then at drush_autoload() time, it will NOT YET be - * added. drush_add_commandfile() is idempotent; we - * could therefore always call it from drush_autoload(). - * We can even use the result of drush_add_commandfile() - * ('already loaded') as an indicator of whether the - * commandfile is in a Drupal site's project composer.json - * file. - * - * * If a commandfile has NOT been added when - * drush_autoload() is called, then it means that - * this commandfile was included by the Composer - * autoloader, which means that we DO NOT need to - * find and include the vendor/autoload.php file. - * - * b) If a commandfile is NOT in a project's composer.json, - * then we need to load the project's autoload file. - * - * If the autoload file is needed, it will usually be - * found at vendor/autoload.php. If the extension is - * in the project's composer.json file, then we should - * never find the local vendor/autoload.php file. - * - * * Include __DIR__/vendor/autoload.php if it exists - * * Include __DIR__/../../../vendor/autoload.php if it - * - * Performance optimization: - * - * In the case of ../../../vendor/autoload.php, - * AKA ../../autoload.php, then we MIGHT want - * to include it. In case (5), we want it, but in - * case (4) we do not. - * - * Including an autoload file twice is innocuous, - * so we could just invariantly include it. This - * might have some small performance impact, though. - * In case (5), we are guarenteed that the path will - * have /drush/ or /.drush/. In case (4) it should - * not, but this is not guarenteed. In case (4), - * we know where the Drupal root is, though, so we - * can use realpath to determine if our relative - * autoload.php is the project autoload file. - * - * * Do NOT include __DIR__/../../../vendor/autoload.php - * if it is one of: - * - $ROOT/sites/all/vendor/autoload.php - * - $ROOT/core/vendor/autoload.php - * - * Problem, though; we have not determined the - * site root at the time that Drush includes - * vendor/autoload.php, which is when composer - * will start including Drush commandfiles (presuming - * that the 'autoload' section of the Drush - * extension's composer.json file). If we are - * using a site-local Drush, though, then we know - * that the Drupal site's vendor/autoload.php file - * is the same as the Drush vendor/autoload.php - * file. We can therefore note the autoload file - * that Drush includes, and skip including the - * Drush extension's autoload file if it is at the - * same path as the Drush autoload file. - * - * Further Performance optimization: - * - * Some modules or drush extensions might have more than - * one *.drush.inc file, although this is rare. In these - * cases, though, each commandfile will call drush_autoload(__FILE__), - * although only the first one called will need to load - * the autoload.php file. We could just let them be included - * multiple times, which should be innocuous, or we could - * cache the list of locations where we have already included - * autoloaders from, and skip those on subsequent updates. - * - * This is, perhaps, unnecessary, as Drush will not include - * the autoloader for commandfiles that were managed by the - * Drush project's composer.json file, which is our ultimate - * goal for composer-managed sites. - * - * TODO: Test with composer_manager. We might need to no-op - * this function if it is in use. - * - * Usage: - * - * On the last line of any *.drush.inc commandfile, add: - * - * drush_autoload(__FILE__); - * -function drush_autoload($commandfile) { - $already_added = drush_add_commandfile($commandfile); - - if (!$already_added) { - $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; - } - } - } -} - */ - /** * Converts a Windows path (dir1\dir2\dir3) into a Unix path (dir1/dir2/dir3). * Also converts a cygwin "drive emulation" path (/cygdrive/c/dir1) into a From f56c55af267ea3234c4efc8d295d8b8c3d960c8f Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Fri, 23 Jan 2015 18:04:47 -0800 Subject: [PATCH 07/14] Move bootstrap object creation into a separate function. --- drush.php | 5 +---- includes/preflight.inc | 7 +++++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drush.php b/drush.php index 00c4e7f6c1..f80c116478 100755 --- a/drush.php +++ b/drush.php @@ -45,9 +45,7 @@ function drush_main() { * - 'early' files can bootstrap when needed. * - bootstrap constants are available. */ - $bootstrap_class = drush_get_option('bootstrap_class', 'Drush\Boot\DrupalBoot'); - $bootstrap = new $bootstrap_class; - drush_set_context('DRUSH_BOOTSTRAP_OBJECT', $bootstrap); + $bootstrap = drush_preflight_create_bootstrap_object(); $bootstrap->preflight(); $return = ''; @@ -70,7 +68,6 @@ function drush_main() { // perhaps handling immediately. $command_handled = drush_preflight_command_dispatch(); if (!$command_handled) { - $bootstrap = drush_get_context('DRUSH_BOOTSTRAP_OBJECT'); $return = $bootstrap->bootstrap_and_dispatch(); } } diff --git a/includes/preflight.inc b/includes/preflight.inc index 025989f280..7b5039b7a1 100644 --- a/includes/preflight.inc +++ b/includes/preflight.inc @@ -233,6 +233,13 @@ function drush_preflight() { _drush_find_commandfiles_drush(); } +function drush_preflight_create_bootstrap_object() { + $bootstrap_class = drush_get_option('bootstrap_class', 'Drush\Boot\DrupalBoot'); + $bootstrap = new $bootstrap_class; + drush_set_context('DRUSH_BOOTSTRAP_OBJECT', $bootstrap); + return $bootstrap; +} + function _drush_preflight_global_options() { // Debug implies verbose drush_set_context('DRUSH_VERBOSE', drush_get_option(array('verbose', 'debug'), FALSE)); From cc9fedd4df3dd89bcd516c5c3b3338d57e573285 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Fri, 23 Jan 2015 21:56:37 -0800 Subject: [PATCH 08/14] Separate out drush_preflight_site() from drush_preflight(), moving site selection and all site-specific config file loading until after the bootstrap class has been found. Give commandfiles a chance to provide bootstrap candidates, but for now always use the built-in instance. --- drush.php | 14 ++++++++------ includes/preflight.inc | 25 ++++++++++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/drush.php b/drush.php index f80c116478..f1d53c460a 100755 --- a/drush.php +++ b/drush.php @@ -38,16 +38,18 @@ function drush_main() { register_shutdown_function('drush_coverage_shutdown'); } - // Load the Drush configuration files. + // Load the global Drush configuration files, and global Drush commands. drush_preflight(); - /* Set up bootstrap object, so that - * - 'early' files can bootstrap when needed. - * - bootstrap constants are available. - */ - $bootstrap = drush_preflight_create_bootstrap_object(); + // Get the list of candidate bootstrap classes available for different platforms + $bootstrap_candidates = drush_preflight_get_bootstrap_candidates(); + $bootstrap = array_pop($bootstrap_candidates); + drush_set_context('DRUSH_BOOTSTRAP_OBJECT', $bootstrap); $bootstrap->preflight(); + // Find the selected site, and load any configuration files associated with it. + drush_preflight_site(); + $return = ''; if (!drush_get_error()) { if ($file = drush_get_option('early', FALSE)) { diff --git a/includes/preflight.inc b/includes/preflight.inc index 7b5039b7a1..b847fe5e8b 100644 --- a/includes/preflight.inc +++ b/includes/preflight.inc @@ -184,6 +184,12 @@ function drush_preflight() { // Load a custom config specified with the --config option. drush_load_config('custom'); + _drush_preflight_global_options(); + + _drush_find_commandfiles_drush(); +} + +function drush_preflight_site() { // Determine if --root and --uri have been defined and set constants accordingly. _drush_preflight_root(); @@ -229,15 +235,20 @@ function drush_preflight() { } _drush_preflight_global_options(); - - _drush_find_commandfiles_drush(); } -function drush_preflight_create_bootstrap_object() { - $bootstrap_class = drush_get_option('bootstrap_class', 'Drush\Boot\DrupalBoot'); - $bootstrap = new $bootstrap_class; - drush_set_context('DRUSH_BOOTSTRAP_OBJECT', $bootstrap); - return $bootstrap; +function drush_preflight_get_bootstrap_candidates() { + // Give all commandfiles a chance to return candidates + $candidates = drush_command_invoke_all('bootstrap_candidates'); + // If a bootstrap class was specified on the command line, consider it first. + $bootstrap_class = drush_get_option('bootstrap_class', FALSE); + if ($bootstrap_class) { + array_unshift($candidates, new $bootstrap_class); + } + // Always consider our default bootstrap class last. + $default_bootstrap_class = 'Drush\Boot\DrupalBoot'; + $candidates[] = new $default_bootstrap_class; + return $candidates; } function _drush_preflight_global_options() { From d1ca77a6f7b5f7c2a5f1b66d493576f7b34e04b8 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Tue, 10 Feb 2015 14:53:21 -0800 Subject: [PATCH 09/14] Test the autoload-bootstrap branch --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d36f5ea3a5..bde2f358a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ branches: - master - 6.x - 5.x + - autoload-bootstrap language: php php: # See master-fulltest branch for broader PHP version testing. From 6c792522c02281f92b21c7e5db9df71d060c0241 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Tue, 10 Feb 2015 16:51:22 -0800 Subject: [PATCH 10/14] Straighten out bootstrap, so dependencies are executed in a more linear order. Move valid_root() function into DrupalBoot class. --- drush.php | 10 ++-- includes/bootstrap.inc | 103 ++++++++++++++++++++++++++++++++++ includes/environment.inc | 20 +++---- includes/preflight.inc | 102 +++++++++++++++++++-------------- lib/Drush/Boot/Boot.php | 2 + lib/Drush/Boot/DrupalBoot.php | 21 ++++++- lib/Drush/Boot/bootstrap.inc | 101 --------------------------------- 7 files changed, 198 insertions(+), 161 deletions(-) create mode 100644 includes/bootstrap.inc diff --git a/drush.php b/drush.php index f1d53c460a..ab01f9568c 100755 --- a/drush.php +++ b/drush.php @@ -41,13 +41,11 @@ function drush_main() { // Load the global Drush configuration files, and global Drush commands. drush_preflight(); - // Get the list of candidate bootstrap classes available for different platforms - $bootstrap_candidates = drush_preflight_get_bootstrap_candidates(); - $bootstrap = array_pop($bootstrap_candidates); - drush_set_context('DRUSH_BOOTSTRAP_OBJECT', $bootstrap); - $bootstrap->preflight(); + // Find the selected site based on --root, --uri or cwd, and + // return the bootstrap object that goes with it. + $bootstrap = _drush_preflight_root(); - // Find the selected site, and load any configuration files associated with it. + // Preflight the selected site, and load any configuration and commandfiles associated with it. drush_preflight_site(); $return = ''; diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc new file mode 100644 index 0000000000..9cf7325a93 --- /dev/null +++ b/includes/bootstrap.inc @@ -0,0 +1,103 @@ +valid_root($path)) { return $candidate; } } - return FALSE; + return null; } /** diff --git a/includes/preflight.inc b/includes/preflight.inc index b847fe5e8b..5e70708b58 100644 --- a/includes/preflight.inc +++ b/includes/preflight.inc @@ -35,6 +35,7 @@ function drush_preflight_prepare() { // a problem if $vendor_path == $drupal_root .'/core/vendor/autoload.php' require $vendor_path; + require_once DRUSH_BASE_PATH . '/includes/bootstrap.inc'; require_once DRUSH_BASE_PATH . '/includes/environment.inc'; require_once DRUSH_BASE_PATH . '/includes/command.inc'; require_once DRUSH_BASE_PATH . '/includes/drush.inc'; @@ -186,13 +187,55 @@ function drush_preflight() { _drush_preflight_global_options(); + // Load all the commandfiles findable from any of the + // scopes listed above. _drush_find_commandfiles_drush(); + + // Process the site alias that specifies which instance + // of Drush (local or remote) this command will operate on. + // We must do this after we load our config files (so that + // site aliases are available), but before the rest of + // Drush preflight and Drupal root bootstrap phase are + // done, since site aliases may set option values that + // affect these phases. + drush_sitealias_check_arg(); + + // Check to see if we 'use'd a site alias with site-set + drush_sitealias_check_site_env(); } -function drush_preflight_site() { - // Determine if --root and --uri have been defined and set constants accordingly. - _drush_preflight_root(); +/** + * If --root is provided, set context. + */ +function _drush_preflight_root() { + $root = drush_get_option('root'); + if (!isset($root)) { + $root = drush_locate_root(); + } + if ($root) { + $root = realpath($root); + } + // @todo This context name should not mention Drupal. + // @todo Drupal code should use DRUSH_DRUPAL_ROOT instead of this constant. + drush_set_context('DRUSH_SELECTED_DRUPAL_ROOT', $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); + // TEMPORARY: if we have not found a bootstrap class by this point, + // then take the last one and use it. This should be Drupal. + if ($bootstrap == NULL) { + $candidates = drush_preflight_get_bootstrap_candidates(); + $bootstrap = array_pop($candidates); + } + drush_set_context('DRUSH_BOOTSTRAP_CANDIDATES', array($bootstrap)); + drush_set_context('DRUSH_BOOTSTRAP_OBJECT', $bootstrap); + $bootstrap->preflight(); + + return $bootstrap; +} +function drush_preflight_site() { // Load the config options from Drupal's /drush and sites/all/drush directories, // even prior to bootstrapping the root. drush_load_config('drupal'); @@ -206,26 +249,13 @@ function drush_preflight_site() { // Create a @self site alias record. drush_sitealias_create_self_alias(); - // Process the site alias that specifies which instance - // of Drush (local or remote) this command will operate on. - // We must do this after we load our config files (so that - // site aliases are available), but before the rest of - // Drush preflight and Drupal root bootstrap phase are - // done, since site aliases may set option values that - // affect these phases. - drush_sitealias_check_arg(); - - // Check to see if we 'use'd a site alias with site-set - drush_sitealias_check_site_env(); - // If applicable swaps in shell alias value (or executes it). drush_shell_alias_replace(); - // @todo Move this code? // If drush_load_config defined a site alias that did not // exist before, then sitealias check arg might now match // against one of those aliases. - if ((drush_sitealias_check_arg() === TRUE) || (drush_sitealias_check_site_env() === TRUE)) { + if (drush_sitealias_check_arg() === TRUE) { $remote_host = drush_get_option('remote-host'); if (!isset($remote_host)) { // Load the config files for the "new" site. @@ -238,16 +268,20 @@ function drush_preflight_site() { } function drush_preflight_get_bootstrap_candidates() { - // Give all commandfiles a chance to return candidates - $candidates = drush_command_invoke_all('bootstrap_candidates'); - // If a bootstrap class was specified on the command line, consider it first. - $bootstrap_class = drush_get_option('bootstrap_class', FALSE); - if ($bootstrap_class) { - array_unshift($candidates, new $bootstrap_class); + $candidates = drush_get_context('DRUSH_BOOTSTRAP_CANDIDATES', FALSE); + if (!$candidates) { + // Give all commandfiles a chance to return candidates + $candidates = drush_command_invoke_all('bootstrap_candidates'); + // If a bootstrap class was specified on the command line, consider it first. + $bootstrap_class = drush_get_option('bootstrap_class', FALSE); + if ($bootstrap_class) { + array_unshift($candidates, new $bootstrap_class); + } + // Always consider our default bootstrap class last. + $default_bootstrap_class = 'Drush\Boot\DrupalBoot'; + $candidates[] = new $default_bootstrap_class; + drush_set_context('DRUSH_BOOTSTRAP_CANDIDATES', $candidates); } - // Always consider our default bootstrap class last. - $default_bootstrap_class = 'Drush\Boot\DrupalBoot'; - $candidates[] = new $default_bootstrap_class; return $candidates; } @@ -330,22 +364,6 @@ function _drush_preflight_root_uri() { _drush_preflight_uri(); } -/** - * If --root is provided, set context. - */ -function _drush_preflight_root() { - $root = drush_get_option('root'); - if (!isset($root)) { - $root = drush_locate_root(); - } - if ($root) { - $root = realpath($root); - } - // @todo This context name should not mention Drupal. - // @todo Drupal code should use DRUSH_DRUPAL_ROOT instead of this constant. - drush_set_context('DRUSH_SELECTED_DRUPAL_ROOT', $root); -} - /** * If --uri is provided, set context. */ diff --git a/lib/Drush/Boot/Boot.php b/lib/Drush/Boot/Boot.php index 49a0191774..c523febff4 100644 --- a/lib/Drush/Boot/Boot.php +++ b/lib/Drush/Boot/Boot.php @@ -6,6 +6,8 @@ * @todo Doc these methods. */ interface Boot { + function valid_root($path); + function bootstrap_and_dispatch(); function preflight(); diff --git a/lib/Drush/Boot/DrupalBoot.php b/lib/Drush/Boot/DrupalBoot.php index 7cad2daf37..6714d12454 100644 --- a/lib/Drush/Boot/DrupalBoot.php +++ b/lib/Drush/Boot/DrupalBoot.php @@ -5,7 +5,26 @@ class DrupalBoot implements Boot { function __construct() { - // @todo Find current version of Drupal and return more specific subclass. For now, there is only 1. + } + + // @todo We could split this into separate classes for Drupal 8, and Drupal 7 + 6, + // and give each one its own 'valid_root' implementation. To do this, just add + // another candidate in drush_preflight_get_bootstrap_candidates(). + function valid_root($path) { + if (!empty($path) && is_dir($path) && file_exists($path . '/index.php')) { + // Drupal 8 root. Additional check for the presence of core/composer.json to + // grant it is not a Drupal 7 site with a base folder named "core". + $candidate = 'core/includes/common.inc'; + if (file_exists($path . '/' . $candidate) && file_exists($path . '/core/misc/drupal.js') && file_exists($path . '/core/core.services.yml')) { + return $candidate; + } + // Drupal 7 root. + $candidate = 'includes/common.inc'; + if (file_exists($path . '/' . $candidate) && file_exists($path . '/misc/drupal.js')) { + return $candidate; + } + } + return FALSE; } function preflight() { diff --git a/lib/Drush/Boot/bootstrap.inc b/lib/Drush/Boot/bootstrap.inc index 956c753672..9791032820 100644 --- a/lib/Drush/Boot/bootstrap.inc +++ b/lib/Drush/Boot/bootstrap.inc @@ -18,107 +18,6 @@ use Symfony\Component\HttpFoundation\Request; use Drupal\Core\DrupalKernel; -/** - * No bootstrap. - * - * This constant is only used to indicate that the bootstrap process has - * not started yet. It is not possible to have no bootstrap. - */ -define('DRUSH_BOOTSTRAP_NONE', -1); - -/** - * Use drush_bootstrap_max instead of drush_bootstrap_to_phase - * - * This constant is only usable as the value of the 'bootstrap' - * item of a command object, or as the parameter to - * drush_bootstrap_to_phase. It is not a real bootstrap state. - */ -define('DRUSH_BOOTSTRAP_MAX', -2); - -/** - * @deprecated - * - * No longer used, but 0 remains reserved. Drush always runs preflight. - * Commands may alternatively use DRUSH_BOOTSTRAP_NONE. - */ -define('DRUSH_BOOTSTRAP_DRUSH', 0); - -/** - * Set up and test for a valid drupal root, either through the -r/--root options, - * or evaluated based on the current working directory. - * - * Any code that interacts with an entire Drupal installation, and not a specific - * site on the Drupal installation should use this bootstrap phase. - */ -define('DRUSH_BOOTSTRAP_DRUPAL_ROOT', 1); - -/** - * Set up a Drupal site directory and the correct environment variables to - * allow Drupal to find the configuration file. - * - * If no site is specified with the -l / --uri options, Drush will assume the - * site is 'default', which mimics Drupal's behaviour. - * - * If you want to avoid this behaviour, it is recommended that you use the - * DRUSH_BOOTSTRAP_DRUPAL_ROOT bootstrap phase instead. - * - * Any code that needs to modify or interact with a specific Drupal site's - * settings.php file should bootstrap to this phase. - */ -define('DRUSH_BOOTSTRAP_DRUPAL_SITE', 2); - -/** - * Load the settings from the Drupal sites directory. - * - * This phase is analagous to the DRUPAL_BOOTSTRAP_CONFIGURATION bootstrap phase in Drupal - * itself, and this is also the first step where Drupal specific code is included. - * - * This phase is commonly used for code that interacts with the Drupal install API, - * as both install.php and update.php start at this phase. - */ -define('DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION', 3); - -/** - * Connect to the Drupal database using the database credentials loaded - * during the previous bootstrap phase. - * - * This phase is analogous to the DRUPAL_BOOTSTRAP_DATABASE bootstrap phase in - * Drupal. - * - * Any code that needs to interact with the Drupal database API needs to - * be bootstrapped to at least this phase. - */ -define('DRUSH_BOOTSTRAP_DRUPAL_DATABASE', 4); - -/** - * Fully initialize Drupal. - * - * This is analogous to the DRUPAL_BOOTSTRAP_FULL bootstrap phase in - * Drupal. - * - * Any code that interacts with the general Drupal API should be - * bootstrapped to this phase. - */ -define('DRUSH_BOOTSTRAP_DRUPAL_FULL', 5); - -/** - * Log in to the initialiased Drupal site. - * - * This is the default bootstrap phase all commands will try to reach, - * unless otherwise specified. - * - * This bootstrap phase is used after the site has been - * fully bootstrapped. - * - * This phase will log you in to the drupal site with the username - * or user ID specified by the --user/ -u option. - * - * Use this bootstrap phase for your command if you need to have access - * to information for a specific user, such as listing nodes that might - * be different based on who is logged in. - */ -define('DRUSH_BOOTSTRAP_DRUPAL_LOGIN', 6); - /** * Helper function listing phases. * From bb7abcc41dfd43df413732fb3b5334cce6a7c66e Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Fri, 13 Feb 2015 16:26:45 -0800 Subject: [PATCH 11/14] Code style: use NULL instead of null. --- includes/environment.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/environment.inc b/includes/environment.inc index f8e062552d..571ba1fa9b 100644 --- a/includes/environment.inc +++ b/includes/environment.inc @@ -353,7 +353,7 @@ function drush_locate_root($start_path = NULL) { */ function drush_valid_drupal_root($path) { $bootstrap_class = drush_bootstrap_class_for_root($path); - return $bootstrap_class != null; + return $bootstrap_class != NULL; } function drush_bootstrap_class_for_root($path) { @@ -363,7 +363,7 @@ function drush_bootstrap_class_for_root($path) { return $candidate; } } - return null; + return NULL; } /** From 477ae81febe3cc2238335559f6c996656d3192e4 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Tue, 17 Feb 2015 14:19:41 -0800 Subject: [PATCH 12/14] Remove Drush\Drush class; replace with procedural functions that we can use until our DI strategy has been decided. --- includes/bootstrap.inc | 27 ++++++++++++++++ includes/command.inc | 17 +++++++--- lib/Drush/Boot/command.inc | 2 +- lib/Drush/Drush.php | 63 -------------------------------------- 4 files changed, 41 insertions(+), 68 deletions(-) delete mode 100644 lib/Drush/Drush.php diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 9cf7325a93..9e06f431e7 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -101,3 +101,30 @@ define('DRUSH_BOOTSTRAP_DRUPAL_FULL', 5); * be different based on who is logged in. */ define('DRUSH_BOOTSTRAP_DRUPAL_LOGIN', 6); + +/** + * Used by a Drush extension to request that its Composer autoload + * files be loaded by Drush, if they have not already been. + * + * Usage: + * + * function myextension_init() { + * drush_autoload(__FILE__) + * } + */ +function drush_autoload($commandfile) { + $already_added = commandfiles_cache()->add($commandfile); + + if (!$already_added) { + $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; + } + } + } +} diff --git a/includes/command.inc b/includes/command.inc index b05219c847..dd49a69f2a 100644 --- a/includes/command.inc +++ b/includes/command.inc @@ -1365,7 +1365,7 @@ function drush_command_normalize_name($command_name) { * command files. */ function drush_commandfile_list() { - return Drush\Drush::commandfiles_cache()->get(); + return commandfiles_cache()->get(); } function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { @@ -1392,7 +1392,7 @@ function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { // Build a list of all of the modules to attempt to load. // Start with any modules deferred from a previous phase. - $list = Drush\Drush::commandfiles_cache()->deferred(); + $list = commandfiles_cache()->deferred(); if (isset($cached_list)) { $list = array_merge($list, $cached_list); } @@ -1425,7 +1425,7 @@ function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { // command file cache may not be available anymore, in which case // we rebuild the cache for this phase. if ($filepath = realpath($filename)) { - $load_command = Drush\Drush::commandfiles_cache()->add($filepath); + $load_command = commandfiles_cache()->add($filepath); if ($load_command) { $needs_sort = TRUE; } @@ -1437,7 +1437,7 @@ function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) { } if ($needs_sort) { - Drush\Drush::commandfiles_cache()->sort(); + commandfiles_cache()->sort(); } } } @@ -1772,3 +1772,12 @@ function drush_shell_alias_replace() { _drush_preflight_global_options(); } } + +function commandfiles_cache() { + static $commandfiles_cache = NULL; + + if (!isset($commandfiles_cache)) { + $commandfiles_cache = new Drush\Command\Commandfiles(); + } + return $commandfiles_cache; +} diff --git a/lib/Drush/Boot/command.inc b/lib/Drush/Boot/command.inc index fc7b5a63ab..c36a9cf6ae 100644 --- a/lib/Drush/Boot/command.inc +++ b/lib/Drush/Boot/command.inc @@ -135,7 +135,7 @@ function drush_command_belongs_to_disabled_module() { } else { // The command does not define Drupal dependencies. Derive them. - $command_files = Drush\Drush::commandfiles_cache()->get(); + $command_files = commandfiles_cache()->get(); $command_path = $commands[$command_name]['path'] . DIRECTORY_SEPARATOR . $commands[$command_name]['commandfile'] . '.drush.inc'; $modules = array_search($command_path, $command_files); } diff --git a/lib/Drush/Drush.php b/lib/Drush/Drush.php deleted file mode 100644 index 130da33d85..0000000000 --- a/lib/Drush/Drush.php +++ /dev/null @@ -1,63 +0,0 @@ -add($commandfile); - - if (!$already_added) { - $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; - } - } - } - } - - // ====== Things used internally by Drush ====== - - static function set_commandfiles_cache($commandfiles_cache) { - self::$commandfiles_cache = $commandfiles_cache; - } - - static function commandfiles_cache() { - if (!isset(self::$commandfiles_cache)) { - self::$commandfiles_cache = new Command\Commandfiles(); - } - return self::$commandfiles_cache; - } -} From 3aef5a25b06f374480ae031ee14db2cd1659e90d Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 18 Feb 2015 10:16:59 -0800 Subject: [PATCH 13/14] Remove spurrious constant. --- lib/Drush/Command/Commandfiles.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Drush/Command/Commandfiles.php b/lib/Drush/Command/Commandfiles.php index 0c944719a7..516f71f3f2 100644 --- a/lib/Drush/Command/Commandfiles.php +++ b/lib/Drush/Command/Commandfiles.php @@ -14,7 +14,6 @@ * in Drush for the current command invocation. */ class Commandfiles implements CommandfilesInterface { - const FOO = 'my constant'; protected $cache; protected $deferred; From f99aadfce0287afea59fd5dc18cdb92713fb9065 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 25 Feb 2015 10:56:54 -0800 Subject: [PATCH 14/14] Remove testing of autload-bootstrap branch in .travis.yml. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bde2f358a4..d36f5ea3a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ branches: - master - 6.x - 5.x - - autoload-bootstrap language: php php: # See master-fulltest branch for broader PHP version testing.