浏览代码

AutoloadGenerator to support PSR-4. Tests included.

Andreas Hennings 11 年之前
父节点
当前提交
bbf6278905

+ 69 - 5
src/Composer/Autoload/AutoloadGenerator.php

@@ -69,9 +69,23 @@ return array(
 
 
 EOF;
 EOF;
 
 
+        $psr4File = <<<EOF
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+\$vendorDir = $vendorPathCode52;
+\$baseDir = $appBaseDirCode;
+
+return array(
+
+EOF;
+
+        // Collect information from all packages.
         $packageMap = $this->buildPackageMap($installationManager, $mainPackage, $localRepo->getCanonicalPackages());
         $packageMap = $this->buildPackageMap($installationManager, $mainPackage, $localRepo->getCanonicalPackages());
         $autoloads = $this->parseAutoloads($packageMap, $mainPackage);
         $autoloads = $this->parseAutoloads($packageMap, $mainPackage);
 
 
+        // Process the 'psr-0' base directories.
         foreach ($autoloads['psr-0'] as $namespace => $paths) {
         foreach ($autoloads['psr-0'] as $namespace => $paths) {
             $exportedPaths = array();
             $exportedPaths = array();
             foreach ($paths as $path) {
             foreach ($paths as $path) {
@@ -83,6 +97,21 @@ EOF;
         }
         }
         $namespacesFile .= ");\n";
         $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 = <<<EOF
         $classmapFile = <<<EOF
 <?php
 <?php
 
 
@@ -131,6 +160,8 @@ EOF;
         // flatten array
         // flatten array
         $classMap = array();
         $classMap = array();
         if ($scanPsr0Packages) {
         if ($scanPsr0Packages) {
+            // Scan the PSR-0 directories for class files, and add them to the
+            // class map.
             foreach ($autoloads['psr-0'] as $namespace => $paths) {
             foreach ($autoloads['psr-0'] as $namespace => $paths) {
                 foreach ($paths as $dir) {
                 foreach ($paths as $dir) {
                     $dir = $filesystem->normalizePath($filesystem->isAbsolutePath($dir) ? $dir : $basePath.'/'.$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.+(?<!(?<!/)Test\.php)$}',
+                        preg_quote($dir),
+                        strpos($namespace, '_') === false ? preg_quote(strtr($namespace, '\\', '/')) : ''
+                    );
+                    foreach (ClassMapGenerator::createMap($dir, $whitelist) as $class => $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']));
         $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_namespaces.php', $namespacesFile);
+        file_put_contents($targetDir.'/autoload_psr4.php', $psr4File);
         file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile);
         file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile);
         if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
         if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
             file_put_contents($targetDir.'/include_paths.php', $includePathFile);
             file_put_contents($targetDir.'/include_paths.php', $includePathFile);
@@ -181,7 +236,7 @@ EOF;
             file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile);
             file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile);
         }
         }
         file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
         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
         // use stream_copy_to_stream instead of copy
         // to work around https://bugs.php.net/bug.php?id=64634
         // to work around https://bugs.php.net/bug.php?id=64634
@@ -229,12 +284,14 @@ EOF;
         array_unshift($packageMap, $mainPackageMap);
         array_unshift($packageMap, $mainPackageMap);
 
 
         $psr0 = $this->parseAutoloadsType($packageMap, 'psr-0', $mainPackage);
         $psr0 = $this->parseAutoloadsType($packageMap, 'psr-0', $mainPackage);
+        $psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage);
         $classmap = $this->parseAutoloadsType($sortedPackageMap, 'classmap', $mainPackage);
         $classmap = $this->parseAutoloadsType($sortedPackageMap, 'classmap', $mainPackage);
         $files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage);
         $files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage);
 
 
         krsort($psr0);
         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;
 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
         // TODO the class ComposerAutoloaderInit should be revert to a closure
         // when APC has been fixed:
         // when APC has been fixed:
@@ -417,8 +474,7 @@ HEADER;
 INCLUDE_PATH;
 INCLUDE_PATH;
         }
         }
 
 
-        if ($usePSR0) {
-            $file .= <<<'PSR0'
+        $file .= <<<'PSR0'
         $map = require __DIR__ . '/autoload_namespaces.php';
         $map = require __DIR__ . '/autoload_namespaces.php';
         foreach ($map as $namespace => $path) {
         foreach ($map as $namespace => $path) {
             $loader->set($namespace, $path);
             $loader->set($namespace, $path);
@@ -426,8 +482,16 @@ INCLUDE_PATH;
 
 
 
 
 PSR0;
 PSR0;
+
+        $file .= <<<'PSR4'
+        $map = require __DIR__ . '/autoload_psr4.php';
+        foreach ($map as $namespace => $path) {
+            $loader->setPsr4($namespace, $path);
         }
         }
 
 
+
+PSR4;
+
         if ($useClassMap) {
         if ($useClassMap) {
             $file .= <<<'CLASSMAP'
             $file .= <<<'CLASSMAP'
         $classMap = require __DIR__ . '/autoload_classmap.php';
         $classMap = require __DIR__ . '/autoload_classmap.php';

+ 33 - 1
tests/Composer/Test/Autoload/AutoloadGeneratorTest.php

@@ -95,7 +95,14 @@ class AutoloadGeneratorTest extends TestCase
     {
     {
         $package = new Package('a', '1.0', '1.0');
         $package = new Package('a', '1.0', '1.0');
         $package->setAutoload(array(
         $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/'),
             'classmap' => array('composersrc/'),
         ));
         ));
 
 
@@ -107,11 +114,22 @@ class AutoloadGeneratorTest extends TestCase
         $this->fs->ensureDirectoryExists($this->workingDir.'/src');
         $this->fs->ensureDirectoryExists($this->workingDir.'/src');
         $this->fs->ensureDirectoryExists($this->workingDir.'/lib');
         $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');
         $this->fs->ensureDirectoryExists($this->workingDir.'/composersrc');
         file_put_contents($this->workingDir.'/composersrc/foo.php', '<?php class ClassMapFoo {}');
         file_put_contents($this->workingDir.'/composersrc/foo.php', '<?php class ClassMapFoo {}');
 
 
         $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_1');
         $this->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');
         $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');
         $this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap');
     }
     }
 
 
@@ -122,6 +140,10 @@ class AutoloadGeneratorTest extends TestCase
         $package = new Package('a', '1.0', '1.0');
         $package = new Package('a', '1.0', '1.0');
         $package->setAutoload(array(
         $package->setAutoload(array(
             'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'),
             'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'),
+            'psr-4' => array(
+                'Acme\Fruit\\' => 'src-fruit/',
+                'Acme\Cake\\' => array('src-cake/', 'lib-cake/'),
+            ),
             'classmap' => array('composersrc/'),
             '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->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_2');
         $this->assertAutoloadFiles('main3', $this->vendorDir.'/composer');
         $this->assertAutoloadFiles('main3', $this->vendorDir.'/composer');
+        $this->assertAutoloadFiles('psr4_3', $this->vendorDir.'/composer', 'psr4');
         $this->assertAutoloadFiles('classmap3', $this->vendorDir.'/composer', 'classmap');
         $this->assertAutoloadFiles('classmap3', $this->vendorDir.'/composer', 'classmap');
     }
     }
 
 
@@ -146,6 +169,10 @@ class AutoloadGeneratorTest extends TestCase
         $package = new Package('a', '1.0', '1.0');
         $package = new Package('a', '1.0', '1.0');
         $package->setAutoload(array(
         $package->setAutoload(array(
             'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'),
             'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'),
+            'psr-4' => array(
+                'Acme\Fruit\\' => 'src-fruit/',
+                'Acme\Cake\\' => array('src-cake/', 'lib-cake/'),
+            ),
             'classmap' => array('composersrc/'),
             'classmap' => array('composersrc/'),
         ));
         ));
 
 
@@ -162,6 +189,7 @@ class AutoloadGeneratorTest extends TestCase
         file_put_contents($this->workingDir.'/composersrc/foo.php', '<?php class ClassMapFoo {}');
         file_put_contents($this->workingDir.'/composersrc/foo.php', '<?php class ClassMapFoo {}');
         $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_3');
         $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_3');
         $this->assertAutoloadFiles('main2', $this->vendorDir.'/composer');
         $this->assertAutoloadFiles('main2', $this->vendorDir.'/composer');
+        $this->assertAutoloadFiles('psr4_2', $this->vendorDir.'/composer', 'psr4');
         $this->assertAutoloadFiles('classmap2', $this->vendorDir.'/composer', 'classmap');
         $this->assertAutoloadFiles('classmap2', $this->vendorDir.'/composer', 'classmap');
     }
     }
 
 
@@ -170,6 +198,10 @@ class AutoloadGeneratorTest extends TestCase
         $package = new Package('a', '1.0', '1.0');
         $package = new Package('a', '1.0', '1.0');
         $package->setAutoload(array(
         $package->setAutoload(array(
             'psr-0' => array('Main\\Foo' => '', 'Main\\Bar' => ''),
             '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'),
             'classmap' => array('Main/Foo/src', 'lib'),
             'files' => array('foo.php', 'Main/Foo/bar.php'),
             'files' => array('foo.php', 'Main/Foo/bar.php'),
         ));
         ));

+ 11 - 0
tests/Composer/Test/Autoload/Fixtures/autoload_psr4.php

@@ -0,0 +1,11 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Acme\\Fruit\\' => array($baseDir . '/src-fruit'),
+    'Acme\\Cake\\' => array($baseDir . '/src-cake', $baseDir . '/lib-cake'),
+);

+ 11 - 0
tests/Composer/Test/Autoload/Fixtures/autoload_psr4_2.php

@@ -0,0 +1,11 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname(dirname($vendorDir));
+
+return array(
+    'Acme\\Fruit\\' => array($baseDir . '/src-fruit'),
+    'Acme\\Cake\\' => array($baseDir . '/src-cake', $baseDir . '/lib-cake'),
+);

+ 11 - 0
tests/Composer/Test/Autoload/Fixtures/autoload_psr4_3.php

@@ -0,0 +1,11 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = $vendorDir;
+
+return array(
+    'Acme\\Fruit\\' => array($vendorDir . '/src-fruit'),
+    'Acme\\Cake\\' => array($vendorDir . '/src-cake', $vendorDir . '/lib-cake'),
+);

+ 5 - 0
tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php

@@ -31,6 +31,11 @@ class ComposerAutoloaderInitFilesAutoloadOrder
             $loader->set($namespace, $path);
             $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';
         $classMap = require __DIR__ . '/autoload_classmap.php';
         if ($classMap) {
         if ($classMap) {
             $loader->addClassMap($classMap);
             $loader->addClassMap($classMap);

+ 5 - 0
tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php

@@ -31,6 +31,11 @@ class ComposerAutoloaderInitFilesAutoload
             $loader->set($namespace, $path);
             $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';
         $classMap = require __DIR__ . '/autoload_classmap.php';
         if ($classMap) {
         if ($classMap) {
             $loader->addClassMap($classMap);
             $loader->addClassMap($classMap);

+ 5 - 0
tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php

@@ -31,6 +31,11 @@ class ComposerAutoloaderInitIncludePath
             $loader->set($namespace, $path);
             $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';
         $classMap = require __DIR__ . '/autoload_classmap.php';
         if ($classMap) {
         if ($classMap) {
             $loader->addClassMap($classMap);
             $loader->addClassMap($classMap);

+ 5 - 0
tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php

@@ -31,6 +31,11 @@ class ComposerAutoloaderInitTargetDir
             $loader->set($namespace, $path);
             $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';
         $classMap = require __DIR__ . '/autoload_classmap.php';
         if ($classMap) {
         if ($classMap) {
             $loader->addClassMap($classMap);
             $loader->addClassMap($classMap);