1
0
Fork 0

Add classmap-authoritative config setting

Add a "classmap-authoritative" configuration setting that can be used to
disable searching the various prefix and fallback directories for
classes that have not been registered with the
Composer\Autoload\ClassLoader class map. This setting can be used to
optimize performance by avoiding a potentially large number of
`file_exists` calls when Composer is being used in a program with
additional autoloader facilities. Use of the setting implies
"optimize-autoloader" to ensure that the most complete class map
possible is generated.

Closes #3603
pull/3610/head
Bryan Davis 2015-01-03 17:35:25 -07:00
parent e172cd81a1
commit ad1f8e6c5a
10 changed files with 110 additions and 18 deletions

View File

@ -789,6 +789,9 @@ The following options are supported:
the generated Composer autoloader. When null a random one will be generated.
* **optimize-autoloader** Defaults to `false`. Always optimize when dumping
the autoloader.
* **classmap-authoritative:** Defaults to `false`. If true, the composer
autoloader will not scan the filesystem for classes that are not found in
the class map. Implies 'optimize-autoloader'.
* **github-domains:** Defaults to `["github.com"]`. A list of domains to use in
github mode. This is used for GitHub Enterprise setups.
* **github-expose-hostname:** Defaults to `true`. If set to false, the OAuth

View File

@ -197,6 +197,10 @@
"type": "boolean",
"description": "If false, the composer autoloader will not be prepended to existing autoloaders, defaults to true."
},
"classmap-authoritative": {
"type": "boolean",
"description": "If true, the composer autoloader will not scan the filesystem for classes that are not found in the class map, defaults to false."
},
"github-domains": {
"type": "array",
"description": "A list of domains to use in github mode. This is used for GitHub Enterprise setups, defaults to [\"github.com\"].",

View File

@ -63,6 +63,7 @@ class AutoloadGenerator
$vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
$useGlobalIncludePath = (bool) $config->get('use-include-path');
$prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true';
$classMapAuthoritative = $config->get('classmap-authoritative');
$targetDir = $vendorPath.'/'.$targetDir;
$filesystem->ensureDirectoryExists($targetDir);
@ -226,7 +227,7 @@ EOF;
file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile);
}
file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(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, $classMapAuthoritative));
// use stream_copy_to_stream instead of copy
// to work around https://bugs.php.net/bug.php?id=64634
@ -443,7 +444,7 @@ return ComposerAutoloaderInit$suffix::getLoader();
AUTOLOAD;
}
protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader)
protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative)
{
// TODO the class ComposerAutoloaderInit should be revert to a closure
// when APC has been fixed:
@ -520,6 +521,13 @@ PSR4;
CLASSMAP;
}
if ($classMapAuthoritative) {
$file .= <<<'CLASSMAPAUTHORITATIVE'
$loader->setClassMapAuthoritative(true);
CLASSMAPAUTHORITATIVE;
}
if ($useGlobalIncludePath) {
$file .= <<<'INCLUDEPATH'
$loader->setUseIncludePath(true);

View File

@ -54,6 +54,8 @@ class ClassLoader
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoratative = false;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
@ -248,6 +250,27 @@ class ClassLoader
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoratative
*/
public function setClassMapAuthoritative($classMapAuthoratative)
{
$this->classMapAuthoratative = $classMapAuthoratative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function getClassMapAuthoratative()
{
return $this->classMapAuthoratative;
}
/**
* Registers this instance as an autoloader.
*
@ -298,6 +321,8 @@ class ClassLoader
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
} elseif ($this->classMapAuthoratative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php');

View File

@ -323,6 +323,7 @@ EOT
),
'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }),
'optimize-autoloader' => array($booleanValidator, $booleanNormalizer),
'classmap-authoritative' => array($booleanValidator, $booleanNormalizer),
'prepend-autoloader' => array($booleanValidator, $booleanNormalizer),
'github-expose-hostname' => array($booleanValidator, $booleanNormalizer),
);

View File

@ -52,7 +52,7 @@ EOT
$package = $composer->getPackage();
$config = $composer->getConfig();
$optimize = $input->getOption('optimize') || $config->get('optimize-autoloader');
$optimize = $input->getOption('optimize') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
if ($optimize) {
$output->writeln('<info>Generating optimized autoload files</info>');

View File

@ -106,7 +106,7 @@ EOT
$preferDist = $input->getOption('prefer-dist');
}
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
$install
->setDryRun($input->getOption('dry-run'))

View File

@ -110,7 +110,7 @@ EOT
$preferDist = $input->getOption('prefer-dist');
}
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
$install
->setDryRun($input->getOption('dry-run'))

View File

@ -39,6 +39,7 @@ class Config
'discard-changes' => false,
'autoloader-suffix' => null,
'optimize-autoloader' => false,
'classmap-authoritative' => false,
'prepend-autoloader' => true,
'github-domains' => array('github.com'),
'github-expose-hostname' => true,

View File

@ -67,6 +67,11 @@ class AutoloadGeneratorTest extends TestCase
*/
private $eventDispatcher;
/**
* @var array
*/
private $configValueMap;
protected function setUp()
{
$this->fs = new Filesystem;
@ -79,18 +84,23 @@ class AutoloadGeneratorTest extends TestCase
$this->config = $this->getMock('Composer\Config');
$this->config->expects($this->at(0))
->method('get')
->with($this->equalTo('vendor-dir'))
->will($this->returnCallback(function () use ($that) {
$this->configValueMap = array(
'vendor-dir' => function () use ($that) {
return $that->vendorDir;
}));
},
);
$this->config->expects($this->at(1))
$this->config->expects($this->atLeastOnce())
->method('get')
->with($this->equalTo('vendor-dir'))
->will($this->returnCallback(function () use ($that) {
return $that->vendorDir;
->will($this->returnCallback(function ($arg) use ($that) {
$ret = null;
if (isset($that->configValueMap[$arg])) {
$ret = $that->configValueMap[$arg];
if (is_callable($ret)) {
$ret = $ret();
}
}
return $ret;
}));
$this->origDir = getcwd();
@ -483,6 +493,49 @@ class AutoloadGeneratorTest extends TestCase
include $this->vendorDir.'/composer/autoload_classmap.php'
);
$this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap');
$this->assertNotRegExp('/\$loader->setClassMapAuthoritative\(true\);/', file_get_contents($this->vendorDir.'/composer/autoload_real.php'));
}
public function testClassMapAutoloadingAuthoritative()
{
$package = new Package('a', '1.0', '1.0');
$packages = array();
$packages[] = $a = new Package('a/a', '1.0', '1.0');
$packages[] = $b = new Package('b/b', '1.0', '1.0');
$packages[] = $c = new Package('c/c', '1.0', '1.0');
$a->setAutoload(array('classmap' => array('')));
$b->setAutoload(array('classmap' => array('test.php')));
$c->setAutoload(array('classmap' => array('./')));
$this->repository->expects($this->once())
->method('getCanonicalPackages')
->will($this->returnValue($packages));
$this->configValueMap['classmap-authoritative'] = true;
$this->fs->ensureDirectoryExists($this->vendorDir.'/composer');
$this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/src');
$this->fs->ensureDirectoryExists($this->vendorDir.'/b/b');
$this->fs->ensureDirectoryExists($this->vendorDir.'/c/c/foo');
file_put_contents($this->vendorDir.'/a/a/src/a.php', '<?php class ClassMapFoo {}');
file_put_contents($this->vendorDir.'/b/b/test.php', '<?php class ClassMapBar {}');
file_put_contents($this->vendorDir.'/c/c/foo/test.php', '<?php class ClassMapBaz {}');
$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_7');
$this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated.");
$this->assertEquals(
array(
'ClassMapBar' => $this->vendorDir.'/b/b/test.php',
'ClassMapBaz' => $this->vendorDir.'/c/c/foo/test.php',
'ClassMapFoo' => $this->vendorDir.'/a/a/src/a.php',
),
include $this->vendorDir.'/composer/autoload_classmap.php'
);
$this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap');
$this->assertRegExp('/\$loader->setClassMapAuthoritative\(true\);/', file_get_contents($this->vendorDir.'/composer/autoload_real.php'));
// FIXME: how can we actually test the ClassLoader implementation?
}
public function testFilesAutoloadGeneration()
@ -829,10 +882,7 @@ EOF;
->method('getCanonicalPackages')
->will($this->returnValue(array()));
$this->config->expects($this->at(2))
->method('get')
->with($this->equalTo('use-include-path'))
->will($this->returnValue(true));
$this->configValueMap['use-include-path'] = true;
$this->fs->ensureDirectoryExists($this->vendorDir.'/a');