Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Allow loading of multiple composer autoloaders concurrently, fixes #1248
  • Loading branch information
Seldaek committed Nov 10, 2012
1 parent c0e75e5 commit 487e66d
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 52 deletions.
22 changes: 17 additions & 5 deletions src/Composer/Autoload/AutoloadGenerator.php
Expand Up @@ -158,13 +158,17 @@ public static function autoload(\$class)
$filesCode .= ' require '.$this->getPathCode($filesystem, $relVendorPath, $vendorPath, $functionFile).";\n";
}

if (!$suffix) {
$suffix = md5(uniqid('', true));
}

file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile);
if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $relVendorPath, $vendorPath, $vendorPathCode, $appBaseDirCode)) {
file_put_contents($targetDir.'/include_paths.php', $includePathFile);
}
file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
file_put_contents($targetDir.'/autoload_real'.$suffix.'.php', $this->getAutoloadRealFile(true, true, (bool) $includePathFile, $targetDirLoader, $filesCode, $vendorPathCode, $appBaseDirCode, $suffix));
file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, true, (bool) $includePathFile, $targetDirLoader, $filesCode, $vendorPathCode, $appBaseDirCode, $suffix));
copy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php');
}

Expand Down Expand Up @@ -313,7 +317,7 @@ protected function getAutoloadFile($vendorPathToTargetDirCode, $suffix)
// autoload.php generated by Composer
require_once $vendorPathToTargetDirCode . '/autoload_real$suffix.php';
require_once $vendorPathToTargetDirCode . '/autoload_real.php';
return ComposerAutoloaderInit$suffix::getLoader();
Expand All @@ -336,21 +340,29 @@ protected function getAutoloadRealFile($usePSR0, $useClassMap, $useIncludePath,
$file = <<<HEADER
<?php
// autoload_real$suffix.php generated by Composer
require __DIR__ . '/ClassLoader.php';
// autoload_real.php generated by Composer
class ComposerAutoloaderInit$suffix
{
private static \$loader;
public static function loadClassLoader(\$class)
{
if ('Composer\\Autoload\\ClassLoader' === \$class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== static::\$loader) {
return static::\$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'));
static::\$loader = \$loader = new \\Composer\\Autoload\\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'));

This comment has been minimized.

Copy link
@donquixote

donquixote Sep 26, 2013

Contributor

I am wondering, is this really necessary?
What about this.

if (!class_exists('Composer\Autoload\ClassLoader', false)) {
  require __DIR__ . '/ClassLoader.php';
}
static::\$loader = \$loader = new \\Composer\\Autoload\\ClassLoader();

It seems to me this would be more transparent and obvious.

This comment has been minimized.

Copy link
@donquixote

donquixote Sep 26, 2013

Contributor

(I think we talked about this before, I just can't remember where. Maybe in IRC)

This comment has been minimized.

Copy link
@stof

stof Sep 27, 2013

Contributor

It was done this way before.
But conditional require are causing issues with some opcode caches (some versions of APC for instance), whereas the autoloading is never triggered twice for a class

This comment has been minimized.

Copy link
@donquixote

donquixote Nov 19, 2013

Contributor

Hm, what about this then:

if (!class_exists('Composer\Autoload\ClassLoader', false)) {
  // Load the class loader, but do it in a separate method, to prevent some opcode caches from loading the file even if the condition fails.
  $this->loadClassLoader();
}
static::\$loader = \$loader = new \\Composer\\Autoload\\ClassLoader();

How could I test this?

This comment has been minimized.

Copy link
@Seldaek

Seldaek Nov 19, 2013

Author Member

Just please don't play with class_exists :) Conditional loading of any kind fucks up with APC in strange ways. I don't care if it's not testable, it's battle tested code, and if we touch it chances are it will mess with someone's day. Just not worth taking the chance.

This comment has been minimized.

Copy link
@donquixote

donquixote via email Nov 19, 2013

Contributor

This comment has been minimized.

Copy link
@Seldaek

Seldaek Nov 19, 2013

Author Member

I'll take the blame if it helps :p

\$vendorDir = $vendorPathCode;
\$baseDir = $appBaseDirCode;
Expand Down
8 changes: 8 additions & 0 deletions tests/Composer/Test/AllFunctionalTest.php
Expand Up @@ -12,6 +12,8 @@
class AllFunctionalTest extends \PHPUnit_Framework_TestCase
{
protected $oldcwd;
protected $testDir;

public function setUp()
{
$this->oldcwd = getcwd();
Expand All @@ -21,6 +23,11 @@ public function setUp()
public function tearDown()
{
chdir($this->oldcwd);
if ($this->testDir) {
$fs = new Filesystem;
$fs->removeDirectory($this->testDir);
$this->testDir = null;
}
}

/**
Expand Down Expand Up @@ -74,6 +81,7 @@ private function parseTestFile(\SplFileInfo $file)
$section = null;

$testDir = sys_get_temp_dir().'/composer_functional_test'.uniqid(mt_rand(), true);
$this->testDir = $testDir;
$varRegex = '#%([a-zA-Z_-]+)%#';
$variableReplacer = function($match) use (&$data, $testDir) {
list(, $var) = $match;
Expand Down
68 changes: 39 additions & 29 deletions tests/Composer/Test/Autoload/AutoloadGeneratorTest.php
Expand Up @@ -36,7 +36,7 @@ protected function setUp()

$this->workingDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'cmptest';
$this->fs->ensureDirectoryExists($this->workingDir);
$this->vendorDir = $this->workingDir.DIRECTORY_SEPARATOR.'composer-test-autoload';
$this->vendorDir = $this->workingDir.DIRECTORY_SEPARATOR.'composer-test-autoload-'.md5(uniqid('', true));
$this->ensureDirectoryExistsAndClear($this->vendorDir);

$this->config = $this->getMock('Composer\Config');
Expand Down Expand Up @@ -162,7 +162,7 @@ public function testMainPackageAutoloadingWithTargetDir()

$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, 'TargetDir');
$this->assertFileEquals(__DIR__.'/Fixtures/autoload_target_dir.php', $this->vendorDir.'/autoload.php');
$this->assertFileEquals(__DIR__.'/Fixtures/autoload_real_target_dir.php', $this->vendorDir.'/composer/autoload_realTargetDir.php');
$this->assertFileEquals(__DIR__.'/Fixtures/autoload_real_target_dir.php', $this->vendorDir.'/composer/autoload_real.php');
}

public function testMainPackageAutoloadingWithTargetDirAndNoPsr()
Expand Down Expand Up @@ -237,12 +237,12 @@ public function testVendorsClassMapAutoloading()
$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_6');
$this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated.");
$this->assertEquals(
array(
'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/src/b.php',
'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/b/b/lib/c.php',
'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php',
),
include ($this->vendorDir.'/composer/autoload_classmap.php')
$this->normalizePaths(array(
'ClassMapBar' => $this->vendorDir.'/b/b/src/b.php',
'ClassMapBaz' => $this->vendorDir.'/b/b/lib/c.php',
'ClassMapFoo' => $this->vendorDir.'/a/a/src/a.php',
)),
$this->normalizePaths(include $this->vendorDir.'/composer/autoload_classmap.php')
);
$this->assertAutoloadFiles('classmap4', $this->vendorDir.'/composer', 'classmap');
}
Expand Down Expand Up @@ -274,12 +274,12 @@ public function testClassMapAutoloadingEmptyDirAndExactFile()
$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->workingDir.'/composer-test-autoload/b/b/test.php',
'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/c/c/foo/test.php',
'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php',
),
include ($this->vendorDir.'/composer/autoload_classmap.php')
$this->normalizePaths(array(
'ClassMapBar' => $this->vendorDir.'/b/b/test.php',
'ClassMapBaz' => $this->vendorDir.'/c/c/foo/test.php',
'ClassMapFoo' => $this->vendorDir.'/a/a/src/a.php',
)),
$this->normalizePaths(include $this->vendorDir.'/composer/autoload_classmap.php')
);
$this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap');
}
Expand Down Expand Up @@ -307,10 +307,7 @@ public function testFilesAutoloadGeneration()

$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, 'FilesAutoload');
$this->assertFileEquals(__DIR__.'/Fixtures/autoload_functions.php', $this->vendorDir.'/autoload.php');
$this->assertFileEquals(__DIR__.'/Fixtures/autoload_real_functions.php', $this->vendorDir.'/composer/autoload_realFilesAutoload.php');

// suppress the class loader to avoid fatals if the class is redefined
file_put_contents($this->vendorDir.'/composer/ClassLoader.php', '');
$this->assertFileEquals(__DIR__.'/Fixtures/autoload_real_functions.php', $this->vendorDir.'/composer/autoload_real.php');

include $this->vendorDir . '/autoload.php';
$this->assertTrue(function_exists('testFilesAutoloadGeneration1'));
Expand Down Expand Up @@ -362,12 +359,9 @@ public function testFilesAutoloadOrderByDependencies()

$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, 'FilesAutoloadOrder');
$this->assertFileEquals(__DIR__ . '/Fixtures/autoload_functions_by_dependency.php', $this->vendorDir . '/autoload.php');
$this->assertFileEquals(__DIR__ . '/Fixtures/autoload_real_files_by_dependency.php', $this->vendorDir . '/composer/autoload_realFilesAutoloadOrder.php');
$this->assertFileEquals(__DIR__ . '/Fixtures/autoload_real_files_by_dependency.php', $this->vendorDir . '/composer/autoload_real.php');

// suppress the class loader to avoid fatals if the class is redefined
file_put_contents($this->vendorDir . '/composer/ClassLoader.php', '');

include $this->vendorDir . '/autoload.php';
require $this->vendorDir . '/autoload.php';

$this->assertTrue(function_exists('testFilesAutoloadOrderByDependency1'));
$this->assertTrue(function_exists('testFilesAutoloadOrderByDependency2'));
Expand Down Expand Up @@ -475,7 +469,7 @@ public function testIncludePathFileGeneration()
$this->vendorDir."/b/b/library",
$this->vendorDir."/c/library",
),
require($this->vendorDir."/composer/include_paths.php")
require $this->vendorDir."/composer/include_paths.php"
);
}

Expand All @@ -499,10 +493,7 @@ public function testIncludePathsArePrependedInAutoloadFile()

$oldIncludePath = get_include_path();

// suppress the class loader to avoid fatals if the class is redefined
file_put_contents($this->vendorDir.'/composer/ClassLoader.php', '');

require($this->vendorDir."/autoload.php");
require $this->vendorDir."/autoload.php";

$this->assertEquals(
$this->vendorDir."/a/a/lib".PATH_SEPARATOR.$oldIncludePath,
Expand Down Expand Up @@ -542,6 +533,25 @@ private function createClassFile($basedir)

private function assertAutoloadFiles($name, $dir, $type = 'namespaces')
{
$this->assertFileEquals(__DIR__.'/Fixtures/autoload_'.$name.'.php', $dir.'/autoload_'.$type.'.php');
$a = __DIR__.'/Fixtures/autoload_'.$name.'.php';
$b = $dir.'/autoload_'.$type.'.php';
$this->assertEquals(
str_replace('%vendorDir%', basename($this->vendorDir), file_get_contents($a)),
file_get_contents($b),
$a .' does not equal '. $b
);
}

private function normalizePaths($paths)
{
if (!is_array($paths)) {
return strtr($paths, '\\', '/');
}

foreach ($paths as $key => $path) {
$paths[$key] = strtr($path, '\\', '/');
}

return $paths;
}
}
6 changes: 3 additions & 3 deletions tests/Composer/Test/Autoload/Fixtures/autoload_classmap4.php
Expand Up @@ -6,7 +6,7 @@
$baseDir = dirname($vendorDir);

return array(
'ClassMapBar' => $baseDir . '/composer-test-autoload/b/b/src/b.php',
'ClassMapBaz' => $baseDir . '/composer-test-autoload/b/b/lib/c.php',
'ClassMapFoo' => $baseDir . '/composer-test-autoload/a/a/src/a.php',
'ClassMapBar' => $baseDir . '/%vendorDir%/b/b/src/b.php',
'ClassMapBaz' => $baseDir . '/%vendorDir%/b/b/lib/c.php',
'ClassMapFoo' => $baseDir . '/%vendorDir%/a/a/src/a.php',
);
6 changes: 3 additions & 3 deletions tests/Composer/Test/Autoload/Fixtures/autoload_classmap5.php
Expand Up @@ -6,7 +6,7 @@
$baseDir = dirname($vendorDir);

return array(
'ClassMapBar' => $baseDir . '/composer-test-autoload/b/b/test.php',
'ClassMapBaz' => $baseDir . '/composer-test-autoload/c/c/foo/test.php',
'ClassMapFoo' => $baseDir . '/composer-test-autoload/a/a/src/a.php',
'ClassMapBar' => $baseDir . '/%vendorDir%/b/b/test.php',
'ClassMapBaz' => $baseDir . '/%vendorDir%/c/c/foo/test.php',
'ClassMapFoo' => $baseDir . '/%vendorDir%/a/a/src/a.php',
);
Expand Up @@ -2,6 +2,6 @@

// autoload.php generated by Composer

require_once __DIR__ . '/composer' . '/autoload_realFilesAutoload.php';
require_once __DIR__ . '/composer' . '/autoload_real.php';

return ComposerAutoloaderInitFilesAutoload::getLoader();
Expand Up @@ -2,6 +2,6 @@

// autoload.php generated by Composer

require_once __DIR__ . '/composer' . '/autoload_realFilesAutoloadOrder.php';
require_once __DIR__ . '/composer' . '/autoload_real.php';

return ComposerAutoloaderInitFilesAutoloadOrder::getLoader();
@@ -1,20 +1,28 @@
<?php

// autoload_realFilesAutoloadOrder.php generated by Composer

require __DIR__ . '/ClassLoader.php';
// autoload_real.php generated by Composer

class ComposerAutoloaderInitFilesAutoloadOrder
{
private static $loader;

public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}

public static function getLoader()
{
if (null !== static::$loader) {
return static::$loader;
}

spl_autoload_register(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader'));
static::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader'));

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

Expand Down
14 changes: 11 additions & 3 deletions tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php
@@ -1,20 +1,28 @@
<?php

// autoload_realFilesAutoload.php generated by Composer

require __DIR__ . '/ClassLoader.php';
// autoload_real.php generated by Composer

class ComposerAutoloaderInitFilesAutoload
{
private static $loader;

public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}

public static function getLoader()
{
if (null !== static::$loader) {
return static::$loader;
}

spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'));
static::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'));

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

Expand Down
14 changes: 11 additions & 3 deletions tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php
@@ -1,20 +1,28 @@
<?php

// autoload_realTargetDir.php generated by Composer

require __DIR__ . '/ClassLoader.php';
// autoload_real.php generated by Composer

class ComposerAutoloaderInitTargetDir
{
private static $loader;

public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}

public static function getLoader()
{
if (null !== static::$loader) {
return static::$loader;
}

spl_autoload_register(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader'));
static::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader'));

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

Expand Down
Expand Up @@ -2,6 +2,6 @@

// autoload.php generated by Composer

require_once __DIR__ . '/composer' . '/autoload_realTargetDir.php';
require_once __DIR__ . '/composer' . '/autoload_real.php';

return ComposerAutoloaderInitTargetDir::getLoader();
6 changes: 6 additions & 0 deletions tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php
Expand Up @@ -31,6 +31,12 @@ public function setUp()
));
}

public function tearDown()
{
$fs = new Filesystem;
$fs->removeDirectory(sys_get_temp_dir() . '/composer-test');
}

public function testPrivateRepository()
{
$repoUrl = 'http://github.com/composer/packagist';
Expand Down

0 comments on commit 487e66d

Please sign in to comment.