diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d98b34fc1..1e3f99b46 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -103,6 +103,21 @@ jobs: - name: "Run install again using composer binary from source" run: "bin/composer install ${{ env.COMPOSER_FLAGS }}" + - name: "Make source binary the one used by default (Linux / macOS)" + if: "!contains(matrix.os, 'windows')" + run: | + echo -e "$(pwd)/bin\n$(cat $GITHUB_PATH)" > $GITHUB_PATH + echo -e "COMPOSER_BINARY=$(pwd)/bin/composer" >> $GITHUB_ENV + + - name: "Make source binary the one used by default (Windows)" + if: "contains(matrix.os, 'windows')" + run: | + $( + (echo "$(Get-Location)\bin") + (Get-Content $env:GITHUB_PATH -Raw) + ) | Set-Content $env:GITHUB_PATH + echo "COMPOSER_BINARY=$(Get-Location)\bin\composer" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + - name: "Prepare git environment" run: "git config --global user.name composer && git config --global user.email composer@example.com" diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 3a8ed4886..fd56bd7d8 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -43,7 +43,7 @@ namespace Composer\Autoload; class ClassLoader { /** @var \Closure(string):void */ - private $includeFile; + private static $includeFile; /** @var ?string */ private $vendorDir; @@ -109,18 +109,7 @@ class ClassLoader public function __construct($vendorDir = null) { $this->vendorDir = $vendorDir; - - /** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - * - * @param string $file - * @return void - */ - $this->includeFile = static function($file) { - include $file; - }; + self::initializeIncludeClosure(); } /** @@ -440,7 +429,7 @@ class ClassLoader public function loadClass($class) { if ($file = $this->findFile($class)) { - ($this->includeFile)($file); + (self::$includeFile)($file); return true; } @@ -570,4 +559,23 @@ class ClassLoader return false; } + + private static function initializeIncludeClosure(): void + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = static function($file) { + include $file; + }; + } } diff --git a/tests/Composer/Test/Autoload/ClassLoaderTest.php b/tests/Composer/Test/Autoload/ClassLoaderTest.php index 73e3b321d..96ba28c0c 100644 --- a/tests/Composer/Test/Autoload/ClassLoaderTest.php +++ b/tests/Composer/Test/Autoload/ClassLoaderTest.php @@ -59,4 +59,27 @@ class ClassLoaderTest extends TestCase $loader = new ClassLoader(); $this->assertEmpty($loader->getPrefixes()); } + + public function testSerializability(): void + { + $loader = new ClassLoader(); + $loader->add('Pearlike_', __DIR__ . '/Fixtures'); + $loader->add('', __DIR__ . '/FALLBACK'); + $loader->addPsr4('ShinyVendor\\ShinyPackage\\', __DIR__ . '/Fixtures'); + $loader->addPsr4('', __DIR__ . '/FALLBACKPSR4'); + $loader->addClassMap(['A' => '', 'B' => 'path']); + $loader->setApcuPrefix('prefix'); + $loader->setClassMapAuthoritative(true); + $loader->setUseIncludePath(true); + + $loader2 = unserialize(serialize($loader)); + self::assertInstanceOf(ClassLoader::class, $loader2); + self::assertSame($loader->getApcuPrefix(), $loader2->getApcuPrefix()); + self::assertSame($loader->getClassMap(), $loader2->getClassMap()); + self::assertSame($loader->getFallbackDirs(), $loader2->getFallbackDirs()); + self::assertSame($loader->getFallbackDirsPsr4(), $loader2->getFallbackDirsPsr4()); + self::assertSame($loader->getPrefixes(), $loader2->getPrefixes()); + self::assertSame($loader->getPrefixesPsr4(), $loader2->getPrefixesPsr4()); + self::assertSame($loader->getUseIncludePath(), $loader2->getUseIncludePath()); + } }