diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index 869e4757f..09efc0b9f 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -74,6 +74,48 @@ class ArtifactRepository extends ArrayRepository } } + /** + * Find a file by name, returning the one that has the shortest path. + * + * @param \ZipArchive $zip + * @param $filename + * @return bool|int + */ + private function locateFile(\ZipArchive $zip, $filename) { + $indexOfShortestMatch = false; + $lengthOfShortestMatch = -1; + + for ($i = 0; $i < $zip->numFiles; $i++ ){ + $stat = $zip->statIndex($i); + if (strcmp(basename($stat['name']), $filename) === 0){ + $directoryName = dirname($stat['name']); + if ($directoryName == '.') { + //if composer.json is in root directory + //it has to be the one to use. + return $i; + } + + if(strpos($directoryName, '\\') !== false || + strpos($directoryName, '/') !== false) { + //composer.json files below first directory are rejected + continue; + } + + $length = strlen($stat['name']); + if ($indexOfShortestMatch == false || $length < $lengthOfShortestMatch) { + //Check it's not a directory. + $contents = $zip->getFromIndex($i); + if ($contents !== false) { + $indexOfShortestMatch = $i; + $lengthOfShortestMatch = $length; + } + } + } + } + + return $indexOfShortestMatch; + } + private function getComposerInformation(\SplFileInfo $file) { $zip = new \ZipArchive(); @@ -83,7 +125,7 @@ class ArtifactRepository extends ArrayRepository return false; } - $foundFileIndex = $zip->locateName('composer.json', \ZipArchive::FL_NODIR); + $foundFileIndex = $this->locateFile($zip, 'composer.json'); if (false === $foundFileIndex) { return false; } diff --git a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php index 109b53bfb..a5889dd28 100644 --- a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php @@ -26,6 +26,10 @@ class ArtifactRepositoryTest extends TestCase 'composer/composer-1.0.0-alpha6', 'vendor1/package2-4.3.2', 'vendor3/package1-5.4.3', + 'test/jsonInRoot-1.0.0', + 'test/jsonInFirstLevel-1.0.0', + //The files not-an-artifact.zip and jsonSecondLevel are not valid + //artifacts and do not get detected. ); $coordinates = array('type' => 'artifact', 'url' => __DIR__ . '/Fixtures/artifacts'); @@ -41,3 +45,42 @@ class ArtifactRepositoryTest extends TestCase $this->assertSame($expectedPackages, $foundPackages); } } + +//Files jsonInFirstLevel.zip, jsonInRoot.zip and jsonInSecondLevel.zip were generated with: +// +//$archivesToCreate = array( +// 'jsonInRoot' => array( +// "extra.txt" => "Testing testing testing", +// "composer.json" => '{ "name": "test/jsonInRoot", "version": "1.0.0" }', +// "subdir/extra.txt" => "Testing testing testing", +// "subdir/extra2.txt" => "Testing testing testing", +// ), +// +// 'jsonInFirstLevel' => array( +// "extra.txt" => "Testing testing testing", +// "subdir/composer.json" => '{ "name": "test/jsonInFirstLevel", "version": "1.0.0" }', +// "subdir/extra.txt" => "Testing testing testing", +// "subdir/extra2.txt" => "Testing testing testing", +// ), +// +// 'jsonInSecondLevel' => array( +// "extra.txt" => "Testing testing testing", +// "subdir/extra1.txt" => "Testing testing testing", +// "subdir/foo/composer.json" => '{ "name": "test/jsonInSecondLevel", "version": "1.0.0" }', +// "subdir/foo/extra1.txt" => "Testing testing testing", +// "subdir/extra2.txt" => "Testing testing testing", +// "subdir/extra3.txt" => "Testing testing testing", +// ), +//); +// +//foreach($archivesToCreate as $archiveName => $fileDetails) { +// $zipFile = new ZipArchive(); +// $zipFile->open("$archiveName.zip", ZIPARCHIVE::CREATE); +// +// foreach ($fileDetails as $filename => $fileContents) { +// $zipFile->addFromString($filename, $fileContents); +// } +// +// $zipFile->close(); +//} + diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInFirstLevel.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInFirstLevel.zip new file mode 100644 index 000000000..498037464 Binary files /dev/null and b/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInFirstLevel.zip differ diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInRoot.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInRoot.zip new file mode 100644 index 000000000..7b2a87eb9 Binary files /dev/null and b/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInRoot.zip differ diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInSecondLevel.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInSecondLevel.zip new file mode 100644 index 000000000..0e5abc61b Binary files /dev/null and b/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInSecondLevel.zip differ