diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php
index cf90535bf..371f3ed76 100644
--- a/src/Composer/Autoload/AutoloadGenerator.php
+++ b/src/Composer/Autoload/AutoloadGenerator.php
@@ -101,15 +101,15 @@ class AutoloadGenerator
$this->runScripts = (bool) $runScripts;
}
- public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '')
+ public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsrPackages = false, $suffix = '')
{
if ($this->classMapAuthoritative) {
- // Force scanPsr0Packages when classmap is authoritative
- $scanPsr0Packages = true;
+ // Force scanPsrPackages when classmap is authoritative
+ $scanPsrPackages = true;
}
if ($this->runScripts) {
$this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array(), array(
- 'optimize' => (bool) $scanPsr0Packages,
+ 'optimize' => (bool) $scanPsrPackages,
));
}
@@ -234,14 +234,18 @@ EOF;
$blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
}
- // flatten array
$classMap = array();
$ambiguousClasses = array();
- if ($scanPsr0Packages) {
+ $scannedFiles = array();
+ foreach ($autoloads['classmap'] as $dir) {
+ $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, null, $classMap, $ambiguousClasses, $scannedFiles);
+ }
+
+ if ($scanPsrPackages) {
$namespacesToScan = array();
// Scan the PSR-0/4 directories for class files, and add them to the class map
- foreach (array('psr-0', 'psr-4') as $psrType) {
+ foreach (array('psr-4', 'psr-0') as $psrType) {
foreach ($autoloads[$psrType] as $namespace => $paths) {
$namespacesToScan[$namespace][] = array('paths' => $paths, 'type' => $psrType);
}
@@ -257,16 +261,12 @@ EOF;
continue;
}
- $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespace, $group['type'], $classMap, $ambiguousClasses);
+ $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespace, $group['type'], $classMap, $ambiguousClasses, $scannedFiles);
}
}
}
}
- foreach ($autoloads['classmap'] as $dir) {
- $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, null, $classMap, $ambiguousClasses);
- }
-
foreach ($ambiguousClasses as $className => $ambigiousPaths) {
$cleanPath = str_replace(array('$vendorDir . \'', '$baseDir . \'', "',\n"), array($vendorPath, $basePath, ''), $classMap[$className]);
@@ -319,7 +319,7 @@ EOF;
if ($this->runScripts) {
$this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array(
- 'optimize' => (bool) $scanPsr0Packages,
+ 'optimize' => (bool) $scanPsrPackages,
));
}
@@ -336,9 +336,9 @@ EOF;
return 0;
}
- private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespaceFilter, $autoloadType, array $classMap, array &$ambiguousClasses)
+ private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespaceFilter, $autoloadType, array $classMap, array &$ambiguousClasses, array &$scannedFiles)
{
- foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType) as $class => $path) {
+ foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType, true, $scannedFiles) as $class => $path) {
$pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n";
if (!isset($classMap[$class])) {
$classMap[$class] = $pathCode;
@@ -350,9 +350,9 @@ EOF;
return $classMap;
}
- private function generateClassMap($dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, $showAmbiguousWarning = true)
+ private function generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType, $showAmbiguousWarning, array &$scannedFiles)
{
- return ClassMapGenerator::createMap($dir, $blacklist, $showAmbiguousWarning ? $this->io : null, $namespaceFilter, $autoloadType);
+ return ClassMapGenerator::createMap($dir, $blacklist, $showAmbiguousWarning ? $this->io : null, $namespaceFilter, $autoloadType, $scannedFiles);
}
public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
@@ -461,9 +461,10 @@ EOF;
$blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
}
+ $scannedFiles = array();
foreach ($autoloads['classmap'] as $dir) {
try {
- $loader->addClassMap($this->generateClassMap($dir, $blacklist, null, null, false));
+ $loader->addClassMap($this->generateClassMap($dir, $blacklist, null, null, false, $scannedFiles));
} catch (\RuntimeException $e) {
$this->io->writeError(''.$e->getMessage().'');
}
diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php
index 09bbd447f..c0b011f3f 100644
--- a/src/Composer/Autoload/ClassMapGenerator.php
+++ b/src/Composer/Autoload/ClassMapGenerator.php
@@ -59,7 +59,7 @@ class ClassMapGenerator
* @throws \RuntimeException When the path is neither an existing file nor directory
* @return array A class map array
*/
- public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null, $autoloadType = null)
+ public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null, $autoloadType = null, &$scannedFiles = array())
{
if (is_string($path)) {
$basePath = $path;
@@ -94,8 +94,16 @@ class ClassMapGenerator
$filePath = preg_replace('{[\\\\/]{2,}}', '/', $filePath);
}
+ $realPath = realpath($filePath);
+
+ // if a list of scanned files is given, avoid scanning twice the same file to save cycles and avoid generating warnings
+ // in case a PSR-0/4 declaration follows another more specific one, or a classmap declaration, which covered this file already
+ if (isset($scannedFiles[$realPath])) {
+ continue;
+ }
+
// check the realpath of the file against the blacklist as the path might be a symlink and the blacklist is realpath'd so symlink are resolved
- if ($blacklist && preg_match($blacklist, strtr(realpath($filePath), '\\', '/'))) {
+ if ($blacklist && preg_match($blacklist, strtr($realPath, '\\', '/'))) {
continue;
}
// check non-realpath of file for directories symlink in project dir
@@ -105,7 +113,15 @@ class ClassMapGenerator
$classes = self::findClasses($filePath);
if (null !== $autoloadType) {
- $classes = self::filterByNamespace($classes, $filePath, $namespace, $autoloadType, $basePath, $io);
+ list($classes, $validClasses) = self::filterByNamespace($classes, $filePath, $namespace, $autoloadType, $basePath, $io);
+
+ // if no valid class was found in the file then we do not mark it as scanned as it might still be matched by another rule later
+ if ($validClasses) {
+ $scannedFiles[$realPath] = true;
+ }
+ } else {
+ // classmap autoload rules always collect all classes so for these we definitely do not want to scan again
+ $scannedFiles[$realPath] = true;
}
foreach ($classes as $class) {
@@ -192,7 +208,7 @@ class ClassMapGenerator
// TODO enable in Composer 2.0 & unskip test in AutoloadGeneratorTest::testPSRToClassMapIgnoresNonPSRClasses
//return $validClasses;
- return $classes;
+ return array($classes, $validClasses);
}
/**
diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php
index f1e91f15c..9627b2a88 100644
--- a/src/Composer/Command/DumpAutoloadCommand.php
+++ b/src/Composer/Command/DumpAutoloadCommand.php
@@ -82,7 +82,7 @@ EOT
} elseif ($optimize) {
$this->getIO()->write('Generated optimized autoload files containing '. $numberOfClasses .' classes');
} else {
- $this->getIO()->write('Generated autoload files containing '. $numberOfClasses .' classes');
+ $this->getIO()->write('Generated autoload files');
}
return 0;