diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index e46ba1463..489a90d23 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -117,8 +117,6 @@ EOF; // flatten array $classMap = array(); - $autoloads['classmap'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); - if ($scanPsr0Packages) { foreach ($autoloads['psr-0'] as $namespace => $paths) { foreach ($paths as $dir) { @@ -139,12 +137,16 @@ EOF; } } } + + $autoloads['classmap'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); foreach ($autoloads['classmap'] as $dir) { foreach (ClassMapGenerator::createMap($dir) as $class => $path) { $path = '/'.$filesystem->findShortestPath(getcwd(), $path, true); $classMap[$class] = '$baseDir . '.var_export($path, true).",\n"; } } + + ksort($classMap); foreach ($classMap as $class => $code) { $classmapFile .= ' '.var_export($class, true).' => '.$code; } @@ -170,28 +172,13 @@ EOF; { // build package => install path map $packageMap = array(); - $packages[] = $mainPackage; - - // sort packages by dependencies - usort($packages, function (PackageInterface $a, PackageInterface $b) { - foreach (array_merge($a->getRequires(), $a->getDevRequires()) as $link) { - if (in_array($link->getTarget(), $b->getNames())) { - return 1; - } - } - foreach (array_merge($b->getRequires(), $b->getDevRequires()) as $link) { - if (in_array($link->getTarget(), $a->getNames())) { - return -1; - } - } - - return strcmp($a->getName(), $b->getName()); - }); + array_unshift($packages, $mainPackage); foreach ($packages as $package) { if ($package instanceof AliasPackage) { continue; } + if ($package === $mainPackage) { $packageMap[] = array($mainPackage, ''); continue; @@ -213,32 +200,15 @@ EOF; */ public function parseAutoloads(array $packageMap) { - $autoloads = array('classmap' => array(), 'psr-0' => array(), 'files' => array()); - foreach ($packageMap as $item) { - list($package, $installPath) = $item; + $sortedPackageMap = $this->sortPackageMap($packageMap); - if (null !== $package->getTargetDir()) { - $installPath = substr($installPath, 0, -strlen('/'.$package->getTargetDir())); - } + $psr0 = $this->parseAutoloadsType($packageMap, 'psr-0'); + $classmap = $this->parseAutoloadsType($sortedPackageMap, 'classmap'); + $files = $this->parseAutoloadsType($sortedPackageMap, 'files'); - foreach ($package->getAutoload() as $type => $mapping) { - // skip misconfigured packages - if (!is_array($mapping)) { - continue; - } + krsort($psr0); - foreach ($mapping as $namespace => $paths) { - foreach ((array) $paths as $path) { - $autoloads[$type][$namespace][] = empty($installPath) ? $path : $installPath.'/'.$path; - } - } - } - } - - krsort($autoloads['classmap']); - krsort($autoloads['psr-0']); - - return $autoloads; + return array('psr-0' => $psr0, 'classmap' => $classmap, 'files' => $files); } /** @@ -445,4 +415,79 @@ FOOTER; } + protected function parseAutoloadsType(array $packageMap, $type) + { + $autoloads = array(); + foreach ($packageMap as $item) { + list($package, $installPath) = $item; + + $autoload = $package->getAutoload(); + // skip misconfigured packages + if (!isset($autoload[$type]) || !is_array($autoload[$type])) { + continue; + } + + if (null !== $package->getTargetDir()) { + $installPath = substr($installPath, 0, -strlen('/'.$package->getTargetDir())); + } + + foreach ($autoload[$type] as $namespace => $paths) { + foreach ((array) $paths as $path) { + $autoloads[$namespace][] = empty($installPath) ? $path : $installPath.'/'.$path; + } + } + } + + return $autoloads; + } + + protected function sortPackageMap(array $packageMap) + { + $groups = array(); + $names = array(); + foreach ($packageMap as $key => $item) { + $groups[$key] = array($item); + $mainName = $item[0]->getName(); + foreach ($item[0]->getNames() as $name) { + if (!isset($names[$name])) { + $names[$name] = $name == $mainName ? $key : $mainName; + } + } + } + + foreach ($packageMap as $item) { + foreach (array_merge($item[0]->getRequires(), $item[0]->getDevRequires()) as $link) { + $target = $link->getTarget(); + if (!isset($names[$target])) { + continue; + } + + $targetKey = $names[$target]; + if (is_string($targetKey)) { + if (!isset($names[$targetKey])) { + continue; + } + $targetKey = $names[$targetKey]; + } + + $packageKey = $names[$item[0]->getName()]; + if ($targetKey <= $packageKey || !isset($groups[$packageKey])) { + continue; + } + + foreach ($groups[$packageKey] as $originalItem) { + $groups[$targetKey][] = $originalItem; + $names[$originalItem[0]->getName()] = $targetKey; + } + unset($groups[$packageKey]); + } + } + + $sortedPackageMap = array(); + foreach ($groups as $group) { + $sortedPackageMap = array_merge($sortedPackageMap, $group); + } + + return $sortedPackageMap; + } } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index b6cb99a6b..626c2db07 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -238,9 +238,9 @@ class AutoloadGeneratorTest extends TestCase $this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated."); $this->assertEquals( array( - 'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php', 'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/src/b.php', 'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/b/b/lib/c.php', + 'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php', ), include ($this->vendorDir.'/composer/autoload_classmap.php') ); @@ -275,9 +275,9 @@ class AutoloadGeneratorTest extends TestCase $this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated."); $this->assertEquals( array( - 'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php', 'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/test.php', 'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/c/c/foo/test.php', + 'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php', ), include ($this->vendorDir.'/composer/autoload_classmap.php') ); @@ -322,31 +322,42 @@ class AutoloadGeneratorTest extends TestCase { $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array('files' => array('root.php'))); - $package->setRequires(array(new Link('a', 'a/foo'))); + $package->setRequires(array(new Link('a', 'z/foo'))); $packages = array(); - $packages[] = $a = new Package('a/foo', '1.0', '1.0'); + $packages[] = $z = new Package('z/foo', '1.0', '1.0'); $packages[] = $b = new Package('b/bar', '1.0', '1.0'); $packages[] = $c = new Package('c/lorem', '1.0', '1.0'); + $packages[] = $d = new Package('d/d', '1.0', '1.0'); + $packages[] = $e = new Package('e/e', '1.0', '1.0'); - $a->setAutoload(array('files' => array('testA.php'))); - $a->setRequires(array(new Link('a/foo', 'c/lorem'))); + $z->setAutoload(array('files' => array('testA.php'))); + $z->setRequires(array(new Link('z/foo', 'c/lorem'))); $b->setAutoload(array('files' => array('testB.php'))); $b->setRequires(array(new Link('b/bar', 'c/lorem'))); $c->setAutoload(array('files' => array('testC.php'))); + $d->setAutoload(array('files' => array('testD.php'))); + + $e->setAutoload(array('files' => array('testE.php'))); + $e->setRequires(array(new Link('e/e', 'c/lorem'))); + $this->repository->expects($this->once()) ->method('getPackages') ->will($this->returnValue($packages)); - $this->fs->ensureDirectoryExists($this->vendorDir . '/a/foo'); + $this->fs->ensureDirectoryExists($this->vendorDir . '/z/foo'); $this->fs->ensureDirectoryExists($this->vendorDir . '/b/bar'); $this->fs->ensureDirectoryExists($this->vendorDir . '/c/lorem'); - file_put_contents($this->vendorDir . '/a/foo/testA.php', 'fs->ensureDirectoryExists($this->vendorDir . '/d/d'); + $this->fs->ensureDirectoryExists($this->vendorDir . '/e/e'); + file_put_contents($this->vendorDir . '/z/foo/testA.php', 'vendorDir . '/b/bar/testB.php', 'vendorDir . '/c/lorem/testC.php', 'vendorDir . '/d/d/testD.php', 'vendorDir . '/e/e/testE.php', 'workingDir . '/root.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, 'FilesAutoloadOrder'); @@ -361,18 +372,21 @@ class AutoloadGeneratorTest extends TestCase $this->assertTrue(function_exists('testFilesAutoloadOrderByDependency1')); $this->assertTrue(function_exists('testFilesAutoloadOrderByDependency2')); $this->assertTrue(function_exists('testFilesAutoloadOrderByDependency3')); + $this->assertTrue(function_exists('testFilesAutoloadOrderByDependency4')); + $this->assertTrue(function_exists('testFilesAutoloadOrderByDependency5')); $this->assertTrue(function_exists('testFilesAutoloadOrderByDependencyRoot')); } public function testOverrideVendorsAutoloading() { - $package = new Package('a', '1.0', '1.0'); - $package->setAutoload(array('psr-0' => array('A\\B' => $this->workingDir.'/lib'))); + $package = new Package('z', '1.0', '1.0'); + $package->setAutoload(array('psr-0' => array('A\\B' => $this->workingDir.'/lib'), 'classmap' => array($this->workingDir.'/src'))); + $package->setRequires(array(new Link('z', 'a/a'))); $packages = array(); $packages[] = $a = new Package('a/a', '1.0', '1.0'); $packages[] = $b = new Package('b/b', '1.0', '1.0'); - $a->setAutoload(array('psr-0' => array('A' => 'src/', 'A\\B' => 'lib/'))); + $a->setAutoload(array('psr-0' => array('A' => 'src/', 'A\\B' => 'lib/'), 'classmap' => array('classmap'))); $b->setAutoload(array('psr-0' => array('B\\Sub\\Name' => 'src/'))); $this->repository->expects($this->once()) @@ -380,12 +394,16 @@ class AutoloadGeneratorTest extends TestCase ->will($this->returnValue($packages)); $this->fs->ensureDirectoryExists($this->workingDir.'/lib/A/B'); + $this->fs->ensureDirectoryExists($this->workingDir.'/src/'); $this->fs->ensureDirectoryExists($this->vendorDir.'/composer'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/classmap'); $this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/src'); $this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/lib/A/B'); $this->fs->ensureDirectoryExists($this->vendorDir.'/b/b/src'); file_put_contents($this->workingDir.'/lib/A/B/C.php', 'workingDir.'/src/classes.php', 'vendorDir.'/a/a/lib/A/B/C.php', 'vendorDir.'/a/a/classmap/classes.php', 'workingDir, '\\', '/'); $expectedNamespace = << \$baseDir . '/lib/A/B/C.php', + 'Foo\\\\Bar' => \$baseDir . '/src/classes.php', ); EOF; diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php index 75c7cd741..9c5136d7f 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php @@ -6,6 +6,6 @@ $vendorDir = dirname(__DIR__); $baseDir = $vendorDir; return array( - 'Main\\Foo' => $baseDir . '/src/Main/Foo.php', 'ClassMapFoo' => $baseDir . '/composersrc/foo.php', + 'Main\\Foo' => $baseDir . '/src/Main/Foo.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap4.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap4.php index 944a80bed..7ea7c42c7 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap4.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap4.php @@ -6,7 +6,7 @@ $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( + 'ClassMapBar' => $baseDir . '/composer-test-autoload/b/b/src/b.php', 'ClassMapBaz' => $baseDir . '/composer-test-autoload/b/b/lib/c.php', 'ClassMapFoo' => $baseDir . '/composer-test-autoload/a/a/src/a.php', - 'ClassMapBar' => $baseDir . '/composer-test-autoload/b/b/src/b.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap5.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap5.php index e3230f953..41c1738bc 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap5.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap5.php @@ -6,7 +6,7 @@ $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( - 'ClassMapFoo' => $baseDir . '/composer-test-autoload/a/a/src/a.php', 'ClassMapBar' => $baseDir . '/composer-test-autoload/b/b/test.php', 'ClassMapBaz' => $baseDir . '/composer-test-autoload/c/c/foo/test.php', + 'ClassMapFoo' => $baseDir . '/composer-test-autoload/a/a/src/a.php', ); 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 07c8db3b4..ac610bade 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 @@ -31,9 +31,11 @@ class ComposerAutoloaderInitFilesAutoloadOrder $loader->register(); require $vendorDir . '/c/lorem/testC.php'; - require $vendorDir . '/a/foo/testA.php'; + require $vendorDir . '/z/foo/testA.php'; require $baseDir . '/root.php'; require $vendorDir . '/b/bar/testB.php'; + require $vendorDir . '/d/d/testD.php'; + require $vendorDir . '/e/e/testE.php'; return $loader; }