Merge remote-tracking branch 'donquixote/feature/psr4-complete'

main
Jordi Boggiano 11 years ago
commit b23742e30c

@ -15,6 +15,7 @@ before_script:
- sudo apt-get install parallel - sudo apt-get install parallel
- rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini - rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
- composer install --dev --prefer-source - composer install --dev --prefer-source
- bin/composer install --dev --prefer-source
- git config --global user.name travis-ci - git config --global user.name travis-ci
- git config --global user.email travis@example.com - git config --global user.email travis@example.com

@ -438,6 +438,10 @@ use an empty prefix like:
} }
} }
#### PSR-4
Stub: Similar to PSR-0.
#### Classmap #### Classmap
The `classmap` references are all combined, during install/update, into a single The `classmap` references are all combined, during install/update, into a single

@ -207,6 +207,11 @@
"description": "This is a hash of namespaces (keys) and the directories they can be found into (values, can be arrays of paths) by the autoloader.", "description": "This is a hash of namespaces (keys) and the directories they can be found into (values, can be arrays of paths) by the autoloader.",
"additionalProperties": true "additionalProperties": true
}, },
"psr-4": {
"type": "object",
"description": "This is a hash of namespaces (keys) and the PSR-4 directories they can be found into (values, can be arrays of paths) by the autoloader.",
"additionalProperties": true
},
"classmap": { "classmap": {
"type": "array", "type": "array",
"description": "This is an array of directories that contain classes to be included in the class-map generation process." "description": "This is an array of directories that contain classes to be included in the class-map generation process."

@ -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
@ -204,6 +259,7 @@ EOF;
if ($package instanceof AliasPackage) { if ($package instanceof AliasPackage) {
continue; continue;
} }
$this->validatePackage($package);
$packageMap[] = array( $packageMap[] = array(
$package, $package,
@ -214,6 +270,21 @@ EOF;
return $packageMap; return $packageMap;
} }
/**
* @param PackageInterface $package
*
* @throws \Exception
* Throws an exception, if the package has illegal settings.
*/
protected function validatePackage(PackageInterface $package) {
$autoload = $package->getAutoload();
if (!empty($autoload['psr-4']) && null !== $package->getTargetDir()) {
$name = $package->getName();
$package->getTargetDir();
throw new \Exception("The ['autoload']['psr-4'] setting is incompatible with the ['target-dir'] setting, in package '$name'.");
}
}
/** /**
* Compiles an ordered list of namespace => path mappings * Compiles an ordered list of namespace => path mappings
* *
@ -229,12 +300,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);
} }
/** /**
@ -253,6 +326,12 @@ EOF;
} }
} }
if (isset($autoloads['psr-4'])) {
foreach ($autoloads['psr-4'] as $namespace => $path) {
$loader->addPsr4($namespace, $path);
}
}
return $loader; return $loader;
} }
@ -366,7 +445,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 +496,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 +504,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';

@ -42,19 +42,36 @@ namespace Composer\Autoload;
*/ */
class ClassLoader class ClassLoader
{ {
private $prefixes = array(); // PSR-4
private $fallbackDirs = array(); private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false; private $useIncludePath = false;
private $classMap = array(); private $classMap = array();
public function getPrefixes() public function getPrefixes()
{ {
return call_user_func_array('array_merge', $this->prefixes); return call_user_func_array('array_merge', $this->prefixesPsr0);
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
} }
public function getFallbackDirs() public function getFallbackDirs()
{ {
return $this->fallbackDirs; return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
} }
public function getClassMap() public function getClassMap()
@ -75,23 +92,24 @@ class ClassLoader
} }
/** /**
* Registers a set of classes, merging with any others previously set. * Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
* *
* @param string $prefix The classes prefix * @param string $prefix The prefix
* @param array|string $paths The location(s) of the classes * @param array|string $paths The PSR-0 root directories
* @param bool $prepend Prepend the location(s) * @param bool $prepend Whether to prepend the directories
*/ */
public function add($prefix, $paths, $prepend = false) public function add($prefix, $paths, $prepend = false)
{ {
if (!$prefix) { if (!$prefix) {
if ($prepend) { if ($prepend) {
$this->fallbackDirs = array_merge( $this->fallbackDirsPsr0 = array_merge(
(array) $paths, (array) $paths,
$this->fallbackDirs $this->fallbackDirsPsr0
); );
} else { } else {
$this->fallbackDirs = array_merge( $this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirs, $this->fallbackDirsPsr0,
(array) $paths (array) $paths
); );
} }
@ -100,38 +118,104 @@ class ClassLoader
} }
$first = $prefix[0]; $first = $prefix[0];
if (!isset($this->prefixes[$first][$prefix])) { if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixes[$first][$prefix] = (array) $paths; $this->prefixesPsr0[$first][$prefix] = (array) $paths;
return; return;
} }
if ($prepend) { if ($prepend) {
$this->prefixes[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths, (array) $paths,
$this->prefixes[$first][$prefix] $this->prefixesPsr0[$first][$prefix]
); );
} else { } else {
$this->prefixes[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixes[$first][$prefix], $this->prefixesPsr0[$first][$prefix],
(array) $paths (array) $paths
); );
} }
} }
/** /**
* Registers a set of classes, replacing any others previously set. * Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
* *
* @param string $prefix The classes prefix * @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The location(s) of the classes * @param array|string $paths The PSR-0 base directories
* @param bool $prepend Whether to prepend the directories
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \Exception("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/ */
public function set($prefix, $paths) public function set($prefix, $paths)
{ {
if (!$prefix) { if (!$prefix) {
$this->fallbackDirs = (array) $paths; $this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
return; /**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*/
public function setPsr4($prefix, $paths) {
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \Exception("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} }
$this->prefixes[substr($prefix, 0, 1)][$prefix] = (array) $paths;
} }
/** /**
@ -202,45 +286,71 @@ class ClassLoader
$class = substr($class, 1); $class = substr($class, 1);
} }
// class map lookup
if (isset($this->classMap[$class])) { if (isset($this->classMap[$class])) {
return $this->classMap[$class]; return $this->classMap[$class];
} }
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) { if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name // namespaced class name
$classPath = strtr(substr($class, 0, $pos), '\\', DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; $logicalPathPsr0
$className = substr($class, $pos + 1); = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR)
;
} else { } else {
// PEAR-like class name // PEAR-like class name
$classPath = null; $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
$className = $class;
} }
$classPath .= strtr($className, '_', DIRECTORY_SEPARATOR) . '.php'; if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
$first = $class[0];
if (isset($this->prefixes[$first])) {
foreach ($this->prefixes[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) { if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) { foreach ($dirs as $dir) {
if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $dir . DIRECTORY_SEPARATOR . $classPath; return $file;
} }
} }
} }
} }
} }
foreach ($this->fallbackDirs as $dir) { // PSR-0 fallback dirs
if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { foreach ($this->fallbackDirsPsr0 as $dir) {
return $dir . DIRECTORY_SEPARATOR . $classPath; if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
} }
} }
if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { // PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file; return $file;
} }
// Remember that this class does not exist.
return $this->classMap[$class] = false; return $this->classMap[$class] = false;
} }
} }

@ -303,6 +303,10 @@ EOT
foreach ($autoloads as $name => $path) { foreach ($autoloads as $name => $path) {
$output->writeln(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.'))); $output->writeln(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
} }
} elseif ($type === 'psr-4') {
foreach ($autoloads as $name => $path) {
$output->writeln(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
}
} elseif ($type === 'classmap') { } elseif ($type === 'classmap') {
$output->writeln(implode(', ', $autoloads)); $output->writeln(implode(', ', $autoloads));
} }

@ -103,6 +103,7 @@ class Compiler
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_namespaces.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_namespaces.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_psr4.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_classmap.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_classmap.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php'));
if (file_exists(__DIR__.'/../../vendor/composer/include_paths.php')) { if (file_exists(__DIR__.'/../../vendor/composer/include_paths.php')) {

@ -180,7 +180,7 @@ class ValidatingArrayLoader implements LoaderInterface
} }
if ($this->validateArray('autoload') && !empty($this->config['autoload'])) { if ($this->validateArray('autoload') && !empty($this->config['autoload'])) {
$types = array('psr-0', 'classmap', 'files'); $types = array('psr-0', 'psr-4', 'classmap', 'files');
foreach ($this->config['autoload'] as $type => $typeConfig) { foreach ($this->config['autoload'] as $type => $typeConfig) {
if (!in_array($type, $types)) { if (!in_array($type, $types)) {
$this->errors[] = 'autoload : invalid value ('.$type.'), must be one of '.implode(', ', $types); $this->errors[] = 'autoload : invalid value ('.$type.'), must be one of '.implode(', ', $types);
@ -189,6 +189,13 @@ class ValidatingArrayLoader implements LoaderInterface
} }
} }
if (!empty($this->config['autoload']['psr-4']) && !empty($this->config['target-dir'])) {
$this->errors[] = "The ['autoload']['psr-4'] setting is incompatible with the ['target-dir'] setting.";
// Unset the psr-4 setting, since unsetting target-dir might
// interfere with other settings.
unset($this->config['autoload']['psr-4']);
}
// TODO validate dist // TODO validate dist
// TODO validate source // TODO validate source

@ -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'),
)); ));
@ -486,6 +518,20 @@ return array(
'A' => array(\$vendorDir . '/a/a/src'), 'A' => array(\$vendorDir . '/a/a/src'),
); );
EOF;
// autoload_psr4.php is expected to be empty in this example.
$expectedPsr4 = <<<EOF
<?php
// autoload_psr4.php @generated by Composer
\$vendorDir = dirname(dirname(__FILE__));
\$baseDir = dirname(\$vendorDir);
return array(
);
EOF; EOF;
$expectedClassmap = <<<EOF $expectedClassmap = <<<EOF
@ -505,6 +551,7 @@ EOF;
$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_9'); $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_9');
$this->assertEquals($expectedNamespace, file_get_contents($this->vendorDir.'/composer/autoload_namespaces.php')); $this->assertEquals($expectedNamespace, file_get_contents($this->vendorDir.'/composer/autoload_namespaces.php'));
$this->assertEquals($expectedPsr4, file_get_contents($this->vendorDir.'/composer/autoload_psr4.php'));
$this->assertEquals($expectedClassmap, file_get_contents($this->vendorDir.'/composer/autoload_classmap.php')); $this->assertEquals($expectedClassmap, file_get_contents($this->vendorDir.'/composer/autoload_classmap.php'));
} }
@ -678,6 +725,7 @@ EOF;
$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('Foo' => 'src'), 'psr-0' => array('Foo' => 'src'),
'psr-4' => array('Acme\Foo\\' => 'src-psr4'),
'classmap' => array('classmap'), 'classmap' => array('classmap'),
'files' => array('test.php'), 'files' => array('test.php'),
)); ));
@ -685,6 +733,7 @@ EOF;
$vendorPackage = new Package('b/b', '1.0', '1.0'); $vendorPackage = new Package('b/b', '1.0', '1.0');
$vendorPackage->setAutoload(array( $vendorPackage->setAutoload(array(
'psr-0' => array('Bar' => 'lib'), 'psr-0' => array('Bar' => 'lib'),
'psr-4' => array('Acme\Bar\\' => 'lib-psr4'),
'classmap' => array('classmaps'), 'classmap' => array('classmaps'),
'files' => array('bootstrap.php'), 'files' => array('bootstrap.php'),
)); ));
@ -734,6 +783,21 @@ return array(
'Bar' => array($vendorDir . '/b/b/lib'), 'Bar' => array($vendorDir . '/b/b/lib'),
); );
EOF;
$expectedPsr4 = <<<'EOF'
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir).'/working-dir';
return array(
'Acme\\Foo\\' => array($baseDir . '/src-psr4'),
'Acme\\Bar\\' => array($vendorDir . '/b/b/lib-psr4'),
);
EOF; EOF;
$expectedClassmap = <<<'EOF' $expectedClassmap = <<<'EOF'
@ -754,6 +818,7 @@ return array(
EOF; EOF;
$this->assertEquals($expectedNamespace, file_get_contents($vendorDir.'/composer/autoload_namespaces.php')); $this->assertEquals($expectedNamespace, file_get_contents($vendorDir.'/composer/autoload_namespaces.php'));
$this->assertEquals($expectedPsr4, file_get_contents($vendorDir.'/composer/autoload_psr4.php'));
$this->assertEquals($expectedClassmap, file_get_contents($vendorDir.'/composer/autoload_classmap.php')); $this->assertEquals($expectedClassmap, file_get_contents($vendorDir.'/composer/autoload_classmap.php'));
$this->assertContains("\n \$vendorDir . '/b/b/bootstrap.php',\n", file_get_contents($vendorDir.'/composer/autoload_files.php')); $this->assertContains("\n \$vendorDir . '/b/b/bootstrap.php',\n", file_get_contents($vendorDir.'/composer/autoload_files.php'));
$this->assertContains("\n \$baseDir . '/test.php',\n", file_get_contents($vendorDir.'/composer/autoload_files.php')); $this->assertContains("\n \$baseDir . '/test.php',\n", file_get_contents($vendorDir.'/composer/autoload_files.php'));
@ -768,6 +833,7 @@ EOF;
$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('Foo' => '../path/../src'), 'psr-0' => array('Foo' => '../path/../src'),
'psr-4' => array('Acme\Foo\\' => '../path/../src-psr4'),
'classmap' => array('../classmap'), 'classmap' => array('../classmap'),
'files' => array('../test.php'), 'files' => array('../test.php'),
)); ));
@ -798,7 +864,21 @@ return array(
EOF; EOF;
$expectedClassmap = <<<'EOF' $expectedPsr4 = <<<'EOF'
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir).'/working-dir';
return array(
'Acme\\Foo\\' => array($baseDir . '/../src-psr4'),
);
EOF;
$expectedClassmap = <<<'EOF'
<?php <?php
// autoload_classmap.php @generated by Composer // autoload_classmap.php @generated by Composer
@ -814,6 +894,7 @@ return array(
EOF; EOF;
$this->assertEquals($expectedNamespace, file_get_contents($this->vendorDir.'/composer/autoload_namespaces.php')); $this->assertEquals($expectedNamespace, file_get_contents($this->vendorDir.'/composer/autoload_namespaces.php'));
$this->assertEquals($expectedPsr4, file_get_contents($this->vendorDir.'/composer/autoload_psr4.php'));
$this->assertEquals($expectedClassmap, file_get_contents($this->vendorDir.'/composer/autoload_classmap.php')); $this->assertEquals($expectedClassmap, file_get_contents($this->vendorDir.'/composer/autoload_classmap.php'));
$this->assertContains("\n \$baseDir . '/../test.php',\n", file_get_contents($this->vendorDir.'/composer/autoload_files.php')); $this->assertContains("\n \$baseDir . '/../test.php',\n", file_get_contents($this->vendorDir.'/composer/autoload_files.php'));
} }
@ -823,6 +904,7 @@ EOF;
$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('Foo' => ''), 'psr-0' => array('Foo' => ''),
'psr-4' => array('Acme\Foo\\' => ''),
'classmap' => array(''), 'classmap' => array(''),
)); ));
@ -850,7 +932,21 @@ return array(
EOF; EOF;
$expectedClassmap = <<<'EOF' $expectedPsr4 = <<<'EOF'
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Acme\\Foo\\' => array($baseDir . '/'),
);
EOF;
$expectedClassmap = <<<'EOF'
<?php <?php
// autoload_classmap.php @generated by Composer // autoload_classmap.php @generated by Composer
@ -866,6 +962,7 @@ return array(
EOF; EOF;
$this->assertEquals($expectedNamespace, file_get_contents($this->vendorDir.'/composer/autoload_namespaces.php')); $this->assertEquals($expectedNamespace, file_get_contents($this->vendorDir.'/composer/autoload_namespaces.php'));
$this->assertEquals($expectedPsr4, file_get_contents($this->vendorDir.'/composer/autoload_psr4.php'));
$this->assertEquals($expectedClassmap, file_get_contents($this->vendorDir.'/composer/autoload_classmap.php')); $this->assertEquals($expectedClassmap, file_get_contents($this->vendorDir.'/composer/autoload_classmap.php'));
} }
@ -874,6 +971,7 @@ EOF;
$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('Foo' => 'composer-test-autoload-src/src'), 'psr-0' => array('Foo' => 'composer-test-autoload-src/src'),
'psr-4' => array('Acme\Foo\\' => 'composer-test-autoload-src/src-psr4'),
)); ));
$this->repository->expects($this->once()) $this->repository->expects($this->once())
@ -894,10 +992,25 @@ return array(
'Foo' => array($baseDir . '/composer-test-autoload-src/src'), 'Foo' => array($baseDir . '/composer-test-autoload-src/src'),
); );
EOF;
$expectedPsr4 = <<<'EOF'
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Acme\\Foo\\' => array($baseDir . '/composer-test-autoload-src/src-psr4'),
);
EOF; EOF;
$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, 'VendorSubstring'); $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, 'VendorSubstring');
$this->assertEquals($expectedNamespace, file_get_contents($this->vendorDir.'/composer/autoload_namespaces.php')); $this->assertEquals($expectedNamespace, file_get_contents($this->vendorDir.'/composer/autoload_namespaces.php'));
$this->assertEquals($expectedPsr4, file_get_contents($this->vendorDir.'/composer/autoload_psr4.php'));
} }
private function assertAutoloadFiles($name, $dir, $type = 'namespaces') private function assertAutoloadFiles($name, $dir, $type = 'namespaces')

@ -0,0 +1,65 @@
<?php
namespace Composer\Test\Autoload;
use Composer\Autoload\ClassLoader;
/**
* Tests the Composer\Autoload\ClassLoader class.
*/
class ClassLoaderTest extends \PHPUnit_Framework_TestCase
{
/**
* Tests regular PSR-0 and PSR-4 class loading.
*
* @dataProvider getLoadClassTests
*
* @param string $class
* The fully-qualified class name to test,
* without preceding namespace separator.
* @param bool $prependSeparator
* Whether to call ->loadClass() with a class name with preceding
* namespace separator, as it happens in PHP 5.3.0 - 5.3.2.
* See https://bugs.php.net/50731
*/
public function testLoadClass($class, $prependSeparator = FALSE)
{
$loader = new ClassLoader();
$loader->add('Namespaced\\', __DIR__ . '/Fixtures');
$loader->add('Pearlike_', __DIR__ . '/Fixtures');
$loader->addPsr4('ShinyVendor\\ShinyPackage\\', __DIR__ . '/Fixtures');
if ($prependSeparator) {
$prepend = '\\';
$message = "->loadClass() loads '$class'.";
}
else {
$prepend = '';
$message = "->loadClass() loads '\\$class', as required in PHP 5.3.0 - 5.3.2.";
}
$loader->loadClass($prepend . $class);
$this->assertTrue(class_exists($class, false), $message);
}
/**
* Provides arguments for ->testLoadClass().
*
* @return array
* Array of parameter sets to test with.
*/
public function getLoadClassTests()
{
return array(
array('Namespaced\\Foo'),
array('Pearlike_Foo'),
array('ShinyVendor\\ShinyPackage\\SubNamespace\\Foo'),
// "Bar" would not work here, since it is defined in a ".inc" file,
// instead of a ".php" file. So, use "Baz" instead.
array('Namespaced\\Baz', '\\'),
array('Pearlike_Bar', '\\'),
array('ShinyVendor\\ShinyPackage\\SubNamespace\\Bar', '\\'),
);
}
}

@ -0,0 +1,5 @@
<?php
namespace ShinyVendor\ShinyPackage\SubNamespace;
class Bar {}

@ -0,0 +1,5 @@
<?php
namespace ShinyVendor\ShinyPackage\SubNamespace;
class Foo {}

@ -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'),
);

@ -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'),
);

@ -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'),
);

@ -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);

@ -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);

@ -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);

@ -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);

@ -253,7 +253,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase
), ),
), ),
array( array(
'autoload : invalid value (psr0), must be one of psr-0, classmap, files' 'autoload : invalid value (psr0), must be one of psr-0, psr-4, classmap, files'
) )
), ),
); );

Loading…
Cancel
Save