1
0
Fork 0

Avoid scanning files twice when generating optimized autoloaders, fixes #8683

pull/8697/head
Jordi Boggiano 2020-03-13 18:28:27 +01:00
parent 12b32707fa
commit 86677ad172
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
3 changed files with 40 additions and 23 deletions

View File

@ -101,15 +101,15 @@ class AutoloadGenerator
$this->runScripts = (bool) $runScripts; $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) { if ($this->classMapAuthoritative) {
// Force scanPsr0Packages when classmap is authoritative // Force scanPsrPackages when classmap is authoritative
$scanPsr0Packages = true; $scanPsrPackages = true;
} }
if ($this->runScripts) { if ($this->runScripts) {
$this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array(), array( $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']) . ')}'; $blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
} }
// flatten array
$classMap = array(); $classMap = array();
$ambiguousClasses = 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(); $namespacesToScan = array();
// Scan the PSR-0/4 directories for class files, and add them to the class map // 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) { foreach ($autoloads[$psrType] as $namespace => $paths) {
$namespacesToScan[$namespace][] = array('paths' => $paths, 'type' => $psrType); $namespacesToScan[$namespace][] = array('paths' => $paths, 'type' => $psrType);
} }
@ -257,16 +261,12 @@ EOF;
continue; 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) { foreach ($ambiguousClasses as $className => $ambigiousPaths) {
$cleanPath = str_replace(array('$vendorDir . \'', '$baseDir . \'', "',\n"), array($vendorPath, $basePath, ''), $classMap[$className]); $cleanPath = str_replace(array('$vendorDir . \'', '$baseDir . \'', "',\n"), array($vendorPath, $basePath, ''), $classMap[$className]);
@ -319,7 +319,7 @@ EOF;
if ($this->runScripts) { if ($this->runScripts) {
$this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array( $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array(
'optimize' => (bool) $scanPsr0Packages, 'optimize' => (bool) $scanPsrPackages,
)); ));
} }
@ -336,9 +336,9 @@ EOF;
return 0; 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"; $pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n";
if (!isset($classMap[$class])) { if (!isset($classMap[$class])) {
$classMap[$class] = $pathCode; $classMap[$class] = $pathCode;
@ -350,9 +350,9 @@ EOF;
return $classMap; 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) public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
@ -461,9 +461,10 @@ EOF;
$blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}'; $blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
} }
$scannedFiles = array();
foreach ($autoloads['classmap'] as $dir) { foreach ($autoloads['classmap'] as $dir) {
try { try {
$loader->addClassMap($this->generateClassMap($dir, $blacklist, null, null, false)); $loader->addClassMap($this->generateClassMap($dir, $blacklist, null, null, false, $scannedFiles));
} catch (\RuntimeException $e) { } catch (\RuntimeException $e) {
$this->io->writeError('<warning>'.$e->getMessage().'</warning>'); $this->io->writeError('<warning>'.$e->getMessage().'</warning>');
} }

View File

@ -59,7 +59,7 @@ class ClassMapGenerator
* @throws \RuntimeException When the path is neither an existing file nor directory * @throws \RuntimeException When the path is neither an existing file nor directory
* @return array A class map array * @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)) { if (is_string($path)) {
$basePath = $path; $basePath = $path;
@ -94,8 +94,16 @@ class ClassMapGenerator
$filePath = preg_replace('{[\\\\/]{2,}}', '/', $filePath); $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 // 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; continue;
} }
// check non-realpath of file for directories symlink in project dir // check non-realpath of file for directories symlink in project dir
@ -105,7 +113,15 @@ class ClassMapGenerator
$classes = self::findClasses($filePath); $classes = self::findClasses($filePath);
if (null !== $autoloadType) { 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) { foreach ($classes as $class) {
@ -192,7 +208,7 @@ class ClassMapGenerator
// TODO enable in Composer 2.0 & unskip test in AutoloadGeneratorTest::testPSRToClassMapIgnoresNonPSRClasses // TODO enable in Composer 2.0 & unskip test in AutoloadGeneratorTest::testPSRToClassMapIgnoresNonPSRClasses
//return $validClasses; //return $validClasses;
return $classes; return array($classes, $validClasses);
} }
/** /**

View File

@ -82,7 +82,7 @@ EOT
} elseif ($optimize) { } elseif ($optimize) {
$this->getIO()->write('<info>Generated optimized autoload files containing '. $numberOfClasses .' classes</info>'); $this->getIO()->write('<info>Generated optimized autoload files containing '. $numberOfClasses .' classes</info>');
} else { } else {
$this->getIO()->write('<info>Generated autoload files containing '. $numberOfClasses .' classes</info>'); $this->getIO()->write('<info>Generated autoload files</info>');
} }
return 0; return 0;