diff --git a/src/ComposerRequireChecker/Exception/DependenciesNotInstalledException.php b/src/ComposerRequireChecker/Exception/DependenciesNotInstalledException.php new file mode 100644 index 00000000..5206c7df --- /dev/null +++ b/src/ComposerRequireChecker/Exception/DependenciesNotInstalledException.php @@ -0,0 +1,7 @@ + $vendorRequiredVersion) { + $vendorDirs[$vendorName] = $packageDir . '/' . $configVendorDir . '/' . $vendorName; + }; + + $installedPackages = $this->getInstalledPackages($packageDir . '/' . $configVendorDir); + + foreach ($vendorDirs as $vendorName => $vendorDir) { + if (!array_key_exists($vendorName, $installedPackages)) { continue; } - $composerData = (new JsonLoader($vendorDir . '/composer.json'))->getData(); + yield from (new LocateComposerPackageSourceFiles())->__invoke($installedPackages[$vendorName], $vendorDir); + } + } + - yield from (new LocateComposerPackageSourceFiles())->__invoke($composerData, $vendorDir); + /** + * Lookup each vendor package's composer.json info from installed.json + * + * @param string $vendorDir + * + * @return array Keys are the package name and value is the composer.json as an array + * @throws DependenciesNotInstalledException When composer install/update has not been run + */ + private function getInstalledPackages(string $vendorDir): array + { + try { + $installedData = (new JsonLoader($vendorDir . '/composer/installed.json'))->getData(); + } catch (NotReadableException $e) { + $message = 'The composer dependencies have not been installed, run composer install/update first'; + throw new DependenciesNotInstalledException($message); } + + $installedPackages = []; + + $packages = $installedData['packages'] ?? $installedData; + foreach ($packages as $vendorJson) { + $vendorName = $vendorJson['name']; + $installedPackages[$vendorName] = $vendorJson; + } + + return $installedPackages; } } diff --git a/test/ComposerRequireCheckerTest/BinaryTest.php b/test/ComposerRequireCheckerTest/BinaryTest.php index 7267c3de..f022d8bd 100644 --- a/test/ComposerRequireCheckerTest/BinaryTest.php +++ b/test/ComposerRequireCheckerTest/BinaryTest.php @@ -34,6 +34,6 @@ public function testInvalidConfiguration() $path = __DIR__ . "/../fixtures/validJson.json"; exec("{$this->bin} check {$path} 2>&1", $output, $return); $this->assertSame(1, $return); - $this->assertContains("please check your configuration", implode("\n", $output)); + $this->assertContains("dependencies have not been installed", implode("\n", $output)); } } diff --git a/test/ComposerRequireCheckerTest/FileLocator/LocateComposerPackageDirectDependenciesSourceFilesTest.php b/test/ComposerRequireCheckerTest/FileLocator/LocateComposerPackageDirectDependenciesSourceFilesTest.php index 3c437edd..71ade619 100644 --- a/test/ComposerRequireCheckerTest/FileLocator/LocateComposerPackageDirectDependenciesSourceFilesTest.php +++ b/test/ComposerRequireCheckerTest/FileLocator/LocateComposerPackageDirectDependenciesSourceFilesTest.php @@ -27,9 +27,16 @@ protected function setUp() public function testNoDependencies() { - $composerJson = vfsStream::newFile('composer.json')->at($this->root)->withContent('{}')->url(); + vfsStream::create([ + 'composer.json' => '{}', + 'vendor' => [ + 'composer' => [ + 'installed.json' => '{"packages":[]}', + ], + ], + ]); - $files = $this->locate($composerJson); + $files = $this->locate($this->root->getChild('composer.json')->url()); $this->assertCount(0, $files); } @@ -39,9 +46,11 @@ public function testSingleDependency() vfsStream::create([ 'composer.json' => '{"require":{"foo/bar": "^1.0"}}', 'vendor' => [ + 'composer' => [ + 'installed.json' => '{"packages":[{"name": "foo/bar", "autoload":{"psr-4":{"":"src"}}}]}', + ], 'foo' => [ 'bar' => [ - 'composer.json' => '{"autoload":{"psr-4":{"":"src"}}}', 'src' => [ 'MyClass.php' => '', ], @@ -64,6 +73,9 @@ public function testVendorDirsWithoutComposerFilesAreIgnored() vfsStream::create([ 'composer.json' => '{"require": {"foo/bar": "^1.0"}}', 'vendor' => [ + 'composer' => [ + 'installed.json' => '{"packages":[]}', + ], 'foo' => [ 'bar' => [ 'src' => [ @@ -84,9 +96,11 @@ public function testVendorConfigSettingIsBeingUsed() vfsStream::create([ 'composer.json' => '{"require":{"foo/bar": "^1.0"},"config":{"vendor-dir":"alternate-vendor"}}', 'alternate-vendor' => [ + 'composer' => [ + 'installed.json' => '{"packages":[{"name": "foo/bar", "autoload":{"psr-4":{"":"src"}}}]}', + ], 'foo' => [ 'bar' => [ - 'composer.json' => '{"autoload":{"psr-4":{"":"src"}}}', 'src' => [ 'MyClass.php' => '', ], @@ -104,6 +118,70 @@ public function testVendorConfigSettingIsBeingUsed() $this->assertSame($expectedFile, $actualFile); } + public function testInstalledJsonUsedAsFallback() + { + vfsStream::create([ + 'composer.json' => '{"require":{"foo/bar": "^1.0"}}', + 'vendor' => [ + 'composer' => [ + 'installed.json' => '{"packages": [{"name": "foo/bar", "autoload":{"psr-4":{"":"src"}}}]}', + ], + 'foo' => [ + 'bar' => [ + 'src' => [ + 'MyClass.php' => '', + ], + ], + ], + ], + ]); + + $files = $this->locate($this->root->getChild('composer.json')->url()); + + $this->assertCount(1, $files); + + $expectedFile = $this->root->getChild('vendor/foo/bar/src/MyClass.php')->url(); + $actualFile = str_replace('\\', '/', reset($files)); + $this->assertSame($expectedFile, $actualFile); + + # Ensure we didn't leave our temporary composer.json lying around + $this->assertFalse($this->root->hasChild('vendor/foo/bar/composer.json')); + } + + + /** + * https://github.com/composer/composer/pull/7999 + */ + public function testOldInstalledJsonUsedAsFallback() + { + vfsStream::create([ + 'composer.json' => '{"require":{"foo/bar": "^1.0"}}', + 'vendor' => [ + 'composer' => [ + 'installed.json' => '[{"name": "foo/bar", "autoload":{"psr-4":{"":"src"}}}]', + ], + 'foo' => [ + 'bar' => [ + 'src' => [ + 'MyClass.php' => '', + ], + ], + ], + ], + ]); + + $files = $this->locate($this->root->getChild('composer.json')->url()); + + $this->assertCount(1, $files); + + $expectedFile = $this->root->getChild('vendor/foo/bar/src/MyClass.php')->url(); + $actualFile = str_replace('\\', '/', reset($files)); + $this->assertSame($expectedFile, $actualFile); + + # Ensure we didn't leave our temporary composer.json lying around + $this->assertFalse($this->root->hasChild('vendor/foo/bar/composer.json')); + } + /** * @return string[] */ diff --git a/test/fixtures/unknownSymbols/vendor/composer/installed.json b/test/fixtures/unknownSymbols/vendor/composer/installed.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/test/fixtures/unknownSymbols/vendor/composer/installed.json @@ -0,0 +1 @@ +[]