From ba94445bb9401528ca048213252a514d6aab800d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 21 Jan 2021 15:06:39 +0100 Subject: [PATCH] Ensure InstalledVersions reports info about all currently registered class loaders --- .github/workflows/phpstan.yml | 2 +- src/Composer/Autoload/AutoloadGenerator.php | 2 +- src/Composer/Autoload/ClassLoader.php | 32 ++++ src/Composer/InstalledVersions.php | 147 +++++++++++++----- .../autoload_real_files_by_dependency.php | 2 +- .../Fixtures/autoload_real_functions.php | 2 +- ...load_real_functions_with_include_paths.php | 2 +- ...emoved_include_paths_and_autolad_files.php | 2 +- .../Fixtures/autoload_real_include_path.php | 2 +- .../Fixtures/autoload_real_target_dir.php | 2 +- 10 files changed, 146 insertions(+), 49 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 538868318..60b9afaf2 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -52,5 +52,5 @@ jobs: - name: Run PHPStan # Locked to phpunit 7.5 here as newer ones have void return types which break inheritance run: | - bin/composer require --dev phpstan/phpstan:^0.12.42 phpunit/phpunit:^7.5.20 --with-all-dependencies ${{ env.COMPOSER_FLAGS }} + bin/composer require --dev phpstan/phpstan:^0.12.69 phpunit/phpunit:^7.5.20 --with-all-dependencies ${{ env.COMPOSER_FLAGS }} vendor/bin/phpstan analyse --configuration=phpstan/config.neon diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 6ad5d94b0..21dc88df0 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -838,7 +838,7 @@ PLATFORM_CHECK; $file .= <<vendorDir = $vendorDir; + } + public function getPrefixes() { if (!empty($this->prefixesPsr0)) { @@ -300,6 +309,15 @@ class ClassLoader public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + //no-op + } elseif ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } } /** @@ -308,6 +326,10 @@ class ClassLoader public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } } /** @@ -367,6 +389,16 @@ class ClassLoader return $file; } + /** + * Returns the currently registered loaders indexed by their corresponding vendor directories. + * + * @return self[] + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + private function findFileWithExtension($class, $ext) { // PSR-4 lookup diff --git a/src/Composer/InstalledVersions.php b/src/Composer/InstalledVersions.php index e492f988a..e6909e934 100644 --- a/src/Composer/InstalledVersions.php +++ b/src/Composer/InstalledVersions.php @@ -12,6 +12,7 @@ namespace Composer; +use Composer\Autoload\ClassLoader; use Composer\Semver\VersionParser; /** @@ -22,6 +23,8 @@ use Composer\Semver\VersionParser; class InstalledVersions { private static $installed; + private static $canGetVendors; + private static $installedByVendor = array(); /** * Returns a list of all package names which are present, either by being installed, replaced or provided @@ -31,7 +34,17 @@ class InstalledVersions */ public static function getInstalledPackages() { - return array_keys(self::$installed['versions']); + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); } /** @@ -44,7 +57,13 @@ class InstalledVersions */ public static function isInstalled($packageName) { - return isset(self::$installed['versions'][$packageName]); + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return true; + } + } + + return false; } /** @@ -79,25 +98,29 @@ class InstalledVersions */ public static function getVersionRanges($packageName) { - if (!isset(self::$installed['versions'][$packageName])) { - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); } - $ranges = array(); - if (isset(self::$installed['versions'][$packageName]['pretty_version'])) { - $ranges[] = self::$installed['versions'][$packageName]['pretty_version']; - } - if (array_key_exists('aliases', self::$installed['versions'][$packageName])) { - $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']); - } - if (array_key_exists('replaced', self::$installed['versions'][$packageName])) { - $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']); - } - if (array_key_exists('provided', self::$installed['versions'][$packageName])) { - $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']); - } - - return implode(' || ', $ranges); + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** @@ -106,15 +129,19 @@ class InstalledVersions */ public static function getVersion($packageName) { - if (!isset(self::$installed['versions'][$packageName])) { - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; } - if (!isset(self::$installed['versions'][$packageName]['version'])) { - return null; - } - - return self::$installed['versions'][$packageName]['version']; + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** @@ -123,15 +150,19 @@ class InstalledVersions */ public static function getPrettyVersion($packageName) { - if (!isset(self::$installed['versions'][$packageName])) { - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; } - if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) { - return null; - } - - return self::$installed['versions'][$packageName]['pretty_version']; + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** @@ -140,15 +171,19 @@ class InstalledVersions */ public static function getReference($packageName) { - if (!isset(self::$installed['versions'][$packageName])) { - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; } - if (!isset(self::$installed['versions'][$packageName]['reference'])) { - return null; - } - - return self::$installed['versions'][$packageName]['reference']; + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** @@ -157,7 +192,9 @@ class InstalledVersions */ public static function getRootPackage() { - return self::$installed['root']; + $installed = self::getInstalled(); + + return $installed[0]['root']; } /** @@ -192,5 +229,33 @@ class InstalledVersions public static function reload($data) { self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + // @phpstan-ignore-next-line + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; + } + } + } + + $installed[] = self::$installed; + + return $installed; } } diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php index fdff2bd42..390bff3bd 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php @@ -23,7 +23,7 @@ class ComposerAutoloaderInitFilesAutoloadOrder } spl_autoload_register(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php index 67ce8ffe2..71397e2f5 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php @@ -23,7 +23,7 @@ class ComposerAutoloaderInitFilesAutoload } spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_include_paths.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_include_paths.php index 790029096..47bb48f7f 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_include_paths.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_include_paths.php @@ -23,7 +23,7 @@ class ComposerAutoloaderInitFilesAutoload } spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); $includePaths = require __DIR__ . '/include_paths.php'; diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_removed_include_paths_and_autolad_files.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_removed_include_paths_and_autolad_files.php index 7b7898df1..965542d83 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_removed_include_paths_and_autolad_files.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_removed_include_paths_and_autolad_files.php @@ -23,7 +23,7 @@ class ComposerAutoloaderInitFilesAutoload } spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php index 2747dbc0a..1bbf29cb2 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php @@ -23,7 +23,7 @@ class ComposerAutoloaderInitIncludePath } spl_autoload_register(array('ComposerAutoloaderInitIncludePath', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitIncludePath', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php index 8ec8cdd47..7b6b58bf9 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php @@ -23,7 +23,7 @@ class ComposerAutoloaderInitTargetDir } spl_autoload_register(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());