diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 1252371d6..e3dae3ea8 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -69,9 +69,23 @@ return array( EOF; + $psr4File = <<buildPackageMap($installationManager, $mainPackage, $localRepo->getCanonicalPackages()); $autoloads = $this->parseAutoloads($packageMap, $mainPackage); + // Process the 'psr-0' base directories. foreach ($autoloads['psr-0'] as $namespace => $paths) { $exportedPaths = array(); foreach ($paths as $path) { @@ -83,6 +97,21 @@ EOF; } $namespacesFile .= ");\n"; + // Process the 'psr-4' base directories. + foreach ($autoloads['psr-4'] as $namespace => $paths) { + if ('\\' !== $namespace[strlen($namespace) - 1]) { + throw new \Exception("PSR-4 namespaces must end with a namespace separator. '$namespace' does not."); + } + $exportedPaths = array(); + foreach ($paths as $path) { + $exportedPaths[] = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); + } + $exportedPrefix = var_export($namespace, true); + $psr4File .= " $exportedPrefix => "; + $psr4File .= "array(".implode(', ', $exportedPaths)."),\n"; + } + $psr4File .= ");\n"; + $classmapFile = << $paths) { foreach ($paths as $dir) { $dir = $filesystem->normalizePath($filesystem->isAbsolutePath($dir) ? $dir : $basePath.'/'.$dir); @@ -152,6 +183,29 @@ EOF; } } } + // Scan the PSR-4 directories for class files, and add them to the + // class map. + foreach ($autoloads['psr-4'] as $namespace => $paths) { + foreach ($paths as $dir) { + $dir = $filesystem->normalizePath($filesystem->isAbsolutePath($dir) ? $dir : $basePath.'/'.$dir); + if (!is_dir($dir)) { + continue; + } + $whitelist = sprintf( + '{%s/%s.+(? $path) { + if ('' === $namespace || 0 === strpos($class, $namespace)) { + if (!isset($classMap[$class])) { + $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); + $classMap[$class] = $path.",\n"; + } + } + } + } + } } $autoloads['classmap'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); @@ -173,6 +227,7 @@ EOF; } file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile); + file_put_contents($targetDir.'/autoload_psr4.php', $psr4File); file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile); if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) { file_put_contents($targetDir.'/include_paths.php', $includePathFile); @@ -181,7 +236,7 @@ EOF; file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile); } file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); - file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader)); + file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader)); // use stream_copy_to_stream instead of copy // to work around https://bugs.php.net/bug.php?id=64634 @@ -229,12 +284,14 @@ EOF; array_unshift($packageMap, $mainPackageMap); $psr0 = $this->parseAutoloadsType($packageMap, 'psr-0', $mainPackage); + $psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage); $classmap = $this->parseAutoloadsType($sortedPackageMap, 'classmap', $mainPackage); $files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage); krsort($psr0); + krsort($psr4); - return array('psr-0' => $psr0, 'classmap' => $classmap, 'files' => $files); + return array('psr-0' => $psr0, 'psr-4' => $psr4, 'classmap' => $classmap, 'files' => $files); } /** @@ -366,7 +423,7 @@ return ComposerAutoloaderInit$suffix::getLoader(); AUTOLOAD; } - protected function getAutoloadRealFile($usePSR0, $useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader) + protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader) { // TODO the class ComposerAutoloaderInit should be revert to a closure // when APC has been fixed: @@ -417,8 +474,7 @@ HEADER; INCLUDE_PATH; } - if ($usePSR0) { - $file .= <<<'PSR0' + $file .= <<<'PSR0' $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); @@ -426,8 +482,16 @@ INCLUDE_PATH; PSR0; + + $file .= <<<'PSR4' + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); } + +PSR4; + if ($useClassMap) { $file .= <<<'CLASSMAP' $classMap = require __DIR__ . '/autoload_classmap.php'; diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 7a1609926..38fb4886e 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -95,7 +95,14 @@ class AutoloadGeneratorTest extends TestCase { $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array( - 'psr-0' => array('Main' => 'src/', 'Lala' => array('src/', 'lib/')), + 'psr-0' => array( + 'Main' => 'src/', + 'Lala' => array('src/', 'lib/'), + ), + 'psr-4' => array( + 'Acme\Fruit\\' => 'src-fruit/', + 'Acme\Cake\\' => array('src-cake/', 'lib-cake/'), + ), 'classmap' => array('composersrc/'), )); @@ -107,11 +114,22 @@ class AutoloadGeneratorTest extends TestCase $this->fs->ensureDirectoryExists($this->workingDir.'/src'); $this->fs->ensureDirectoryExists($this->workingDir.'/lib'); + $this->fs->ensureDirectoryExists($this->workingDir.'/src-fruit'); + $this->fs->ensureDirectoryExists($this->workingDir.'/src-cake'); + $this->fs->ensureDirectoryExists($this->workingDir.'/lib-cake'); + $this->fs->ensureDirectoryExists($this->workingDir.'/composersrc'); file_put_contents($this->workingDir.'/composersrc/foo.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_1'); + + // Assert that autoload_namespaces.php was correctly generated. $this->assertAutoloadFiles('main', $this->vendorDir.'/composer'); + + // Assert that autoload_psr4.php was correctly generated. + $this->assertAutoloadFiles('psr4', $this->vendorDir.'/composer', 'psr4'); + + // Assert that autoload_classmap.php was correctly generated. $this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap'); } @@ -122,6 +140,10 @@ class AutoloadGeneratorTest extends TestCase $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'), + 'psr-4' => array( + 'Acme\Fruit\\' => 'src-fruit/', + 'Acme\Cake\\' => array('src-cake/', 'lib-cake/'), + ), 'classmap' => array('composersrc/'), )); @@ -138,6 +160,7 @@ class AutoloadGeneratorTest extends TestCase $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_2'); $this->assertAutoloadFiles('main3', $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('psr4_3', $this->vendorDir.'/composer', 'psr4'); $this->assertAutoloadFiles('classmap3', $this->vendorDir.'/composer', 'classmap'); } @@ -146,6 +169,10 @@ class AutoloadGeneratorTest extends TestCase $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'), + 'psr-4' => array( + 'Acme\Fruit\\' => 'src-fruit/', + 'Acme\Cake\\' => array('src-cake/', 'lib-cake/'), + ), 'classmap' => array('composersrc/'), )); @@ -162,6 +189,7 @@ class AutoloadGeneratorTest extends TestCase file_put_contents($this->workingDir.'/composersrc/foo.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_3'); $this->assertAutoloadFiles('main2', $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('psr4_2', $this->vendorDir.'/composer', 'psr4'); $this->assertAutoloadFiles('classmap2', $this->vendorDir.'/composer', 'classmap'); } @@ -170,6 +198,10 @@ class AutoloadGeneratorTest extends TestCase $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main\\Foo' => '', 'Main\\Bar' => ''), + 'psr-4' => array( + 'Acme\Fruit\\' => 'src-fruit/', + 'Acme\Cake\\' => array('src-cake/', 'lib-cake/'), + ), 'classmap' => array('Main/Foo/src', 'lib'), 'files' => array('foo.php', 'Main/Foo/bar.php'), )); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_psr4.php b/tests/Composer/Test/Autoload/Fixtures/autoload_psr4.php new file mode 100644 index 000000000..78b609869 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_psr4.php @@ -0,0 +1,11 @@ + array($baseDir . '/src-fruit'), + 'Acme\\Cake\\' => array($baseDir . '/src-cake', $baseDir . '/lib-cake'), +); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_psr4_2.php b/tests/Composer/Test/Autoload/Fixtures/autoload_psr4_2.php new file mode 100644 index 000000000..ab9ca2f54 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_psr4_2.php @@ -0,0 +1,11 @@ + array($baseDir . '/src-fruit'), + 'Acme\\Cake\\' => array($baseDir . '/src-cake', $baseDir . '/lib-cake'), +); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_psr4_3.php b/tests/Composer/Test/Autoload/Fixtures/autoload_psr4_3.php new file mode 100644 index 000000000..a903b17b8 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_psr4_3.php @@ -0,0 +1,11 @@ + array($vendorDir . '/src-fruit'), + 'Acme\\Cake\\' => array($vendorDir . '/src-cake', $vendorDir . '/lib-cake'), +); 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 d1f9afd30..e58e8d2fa 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,6 +31,11 @@ class ComposerAutoloaderInitFilesAutoloadOrder $loader->set($namespace, $path); } + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php index dc09bc0ee..a92e664cd 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php @@ -31,6 +31,11 @@ class ComposerAutoloaderInitFilesAutoload $loader->set($namespace, $path); } + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); 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 3c92a5a2c..e72ea108a 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php @@ -31,6 +31,11 @@ class ComposerAutoloaderInitIncludePath $loader->set($namespace, $path); } + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); 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 cae8d5a31..4a6259da2 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php @@ -31,6 +31,11 @@ class ComposerAutoloaderInitTargetDir $loader->set($namespace, $path); } + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap);