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;
}
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('<warning>'.$e->getMessage().'</warning>');
}

View File

@ -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);
}
/**

View File

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