diff --git a/src/Composer/InstalledVersions.php b/src/Composer/InstalledVersions.php index 07b32ed6e..6d29bff66 100644 --- a/src/Composer/InstalledVersions.php +++ b/src/Composer/InstalledVersions.php @@ -32,6 +32,11 @@ class InstalledVersions */ private static $installed; + /** + * @var bool + */ + private static $installedIsLocalDir; + /** * @var bool|null */ @@ -309,6 +314,12 @@ class InstalledVersions { self::$installed = $data; self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; } /** @@ -325,7 +336,9 @@ class InstalledVersions $copiedLocalDir = false; if (self::$canGetVendors) { + $selfDir = strtr(__DIR__, '\\', '/'); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { @@ -333,11 +346,14 @@ class InstalledVersions $required = require $vendorDir.'/composer/installed.php'; self::$installedByVendor[$vendorDir] = $required; $installed[] = $required; - if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { self::$installed = $required; - $copiedLocalDir = true; + self::$installedIsLocalDir = true; } } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } } } diff --git a/tests/Composer/Test/InstalledVersionsTest.php b/tests/Composer/Test/InstalledVersionsTest.php index fdcf28fbb..295b16882 100644 --- a/tests/Composer/Test/InstalledVersionsTest.php +++ b/tests/Composer/Test/InstalledVersionsTest.php @@ -266,4 +266,31 @@ class InstalledVersionsTest extends TestCase self::assertSame('/foo/bar/vendor/c/c', \Composer\InstalledVersions::getInstallPath('c/c')); self::assertNull(\Composer\InstalledVersions::getInstallPath('foo/impl')); } + + public function testWithClassLoaderLoaded(): void + { + // disable multiple-ClassLoader-based checks of InstalledVersions by making it seem like no + // class loaders are registered + $prop = new \ReflectionProperty(ClassLoader::class, 'registeredLoaders'); + $prop->setAccessible(true); + $prop->setValue(null, array_slice(self::$previousRegisteredLoaders, 0, 1, true)); + + $prop2 = new \ReflectionProperty(InstalledVersions::class, 'installedIsLocalDir'); + $prop2->setAccessible(true); + $prop2->setValue(null, true); + + self::assertFalse(InstalledVersions::isInstalled('foo/bar')); + InstalledVersions::reload([ + 'root' => InstalledVersions::getRootPackage(), + 'versions' => [ + 'foo/bar' => [ + 'version' => '1.0.0', + 'dev_requirement' => false, + ], + ], + ]); + self::assertTrue(InstalledVersions::isInstalled('foo/bar')); + + $prop->setValue(null, []); + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 05b88f2f6..2213d7421 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -10,7 +10,6 @@ * file that was distributed with this source code. */ -use Composer\InstalledVersions; use Composer\Util\Platform; error_reporting(E_ALL); @@ -20,10 +19,10 @@ if (function_exists('date_default_timezone_set') && function_exists('date_defaul } require __DIR__.'/../src/bootstrap.php'; - -if (!class_exists(InstalledVersions::class, false)) { - require __DIR__.'/../src/Composer/InstalledVersions.php'; -} +// ensure we always use the latest InstalledVersions.php even if an older composer ran the install, but we need +// to have it included from vendor dir and not from src/ otherwise some gated check in the code will not work +copy(__DIR__.'/../src/Composer/InstalledVersions.php', __DIR__.'/../vendor/composer/InstalledVersions.php'); +require __DIR__.'/../vendor/composer/InstalledVersions.php'; Platform::putEnv('COMPOSER_TESTS_ARE_RUNNING', '1');