Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fix the autoloading of the excluded files #1323

Merged
merged 4 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Box.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public function startBuffering(): void
}

/**
* @param callable(SymbolsRegistry, string): void $dumpAutoload
* @param callable(SymbolsRegistry, string, string[]): void $dumpAutoload
*/
public function endBuffering(?callable $dumpAutoload): void
{
Expand Down
34 changes: 33 additions & 1 deletion src/Composer/AutoloadDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

namespace KevinGH\Box\Composer;

use Humbug\PhpScoper\Autoload\ComposerFileHasher;
use Humbug\PhpScoper\Autoload\ScoperAutoloadGenerator;
use Humbug\PhpScoper\Symbol\SymbolsRegistry;
use KevinGH\Box\NotInstantiable;
Expand All @@ -23,22 +24,34 @@
use function implode;
use function preg_match;
use function preg_replace;
use function sprintf;
use function str_replace;
use const PHP_EOL;

final class AutoloadDumper
{
use NotInstantiable;

private const PACKAGE_PATH_REGEX = '~^%s/(?<vendor>[^/]+?/[^/]+?)/(?<path>.+?)$~';

/**
* @param string[] $excludedComposerAutoloadFiles
*/
public static function generateAutoloadStatements(
SymbolsRegistry $symbolsRegistry,
array $excludedComposerAutoloadFileHashes,
string $vendorDir,
array $excludedComposerAutoloadFiles,
string $autoloadContents,
): string {
if (0 === $symbolsRegistry->count()) {
return $autoloadContents;
}

$excludedComposerAutoloadFileHashes = self::getExcludedComposerAutoloadFileHashes(
$vendorDir,
$excludedComposerAutoloadFiles,
);

$autoloadContents = self::extractInlinedAutoloadContents($autoloadContents);
$scoperStatements = self::getOriginalScoperAutoloaderContents(
$symbolsRegistry,
Expand All @@ -59,6 +72,25 @@ public static function generateAutoloadStatements(
return self::cleanupDuplicateLineReturns($mergedAutoloadContents);
}

/**
* @param string[] $excludedComposerAutoloadFiles
*/
private static function getExcludedComposerAutoloadFileHashes(
string $vendorDir,
array $excludedComposerAutoloadFiles,
): array {
$fileHashGenerator = new ComposerFileHasher(
'',
$excludedComposerAutoloadFiles,
sprintf(
self::PACKAGE_PATH_REGEX,
$vendorDir,
),
);

return $fileHashGenerator->generateHashes();
}

private static function extractInlinedAutoloadContents(string $autoloadContents): string
{
$autoloadContents = str_replace('<?php', '', $autoloadContents);
Expand Down
16 changes: 13 additions & 3 deletions src/Composer/ComposerOrchestrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,29 +110,39 @@ public function checkVersion(): void
}
}

/**
* @param string[] $excludedComposerAutoloadFiles Relative paths of the files that were not scoped hence which need
* to be configured as loaded to Composer as otherwise they would be
* autoloaded twice.
*/
public function dumpAutoload(
SymbolsRegistry $symbolsRegistry,
string $prefix,
bool $excludeDevFiles,
array $excludedComposerAutoloadFileHashes,
array $excludedComposerAutoloadFiles,
): void {
$this->dumpAutoloader(true === $excludeDevFiles);

if ('' === $prefix) {
return;
}

$autoloadFile = $this->getVendorDir().'/autoload.php';
$vendorDir = $this->getVendorDir();
$autoloadFile = $vendorDir.'/autoload.php';

$autoloadContents = AutoloadDumper::generateAutoloadStatements(
$symbolsRegistry,
$excludedComposerAutoloadFileHashes,
$vendorDir,
$excludedComposerAutoloadFiles,
$this->fileSystem->getFileContents($autoloadFile),
);

$this->fileSystem->dumpFile($autoloadFile, $autoloadContents);
}

/**
* @return string The vendor-dir directory path relative to its composer.json.
*/
public function getVendorDir(): string
{
$vendorDirProcess = $this->processFactory->getVendorDirProcess();
Expand Down
50 changes: 32 additions & 18 deletions tests/Composer/AutoloadDumperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use function md5;

/**
* @internal
Expand All @@ -29,12 +30,14 @@ final class AutoloadDumperTest extends TestCase
#[DataProvider('autoloadProvider')]
public function test_it_can_generate_the_autoload(
SymbolsRegistry $symbolsRegistry,
string $vendorDir,
array $excludedComposerAutoloadFileHashes,
string $autoloadContents,
string $expected,
): void {
$actual = AutoloadDumper::generateAutoloadStatements(
$symbolsRegistry,
$vendorDir,
$excludedComposerAutoloadFileHashes,
$autoloadContents,
);
Expand All @@ -44,8 +47,16 @@ public function test_it_can_generate_the_autoload(

public static function autoloadProvider(): iterable
{
$defaultVendorDir = 'vendor';

$excludedFile1 = 'vendor/phpstorm/stubs/stub1.php';
$excludedFile1Hash = md5('phpstorm/stubs:stub1.php');
$excludedFile2 = 'vendor/phpstorm/stubs/stub2.php';
$excludedFile2Hash = md5('phpstorm/stubs:stub2.php');

yield 'no symbols' => [
new SymbolsRegistry(),
$defaultVendorDir,
[],
<<<'PHP'
<?php
Expand Down Expand Up @@ -113,6 +124,7 @@ public static function autoloadProvider(): iterable
],
[],
),
$defaultVendorDir,
[],
<<<'PHP'
<?php
Expand Down Expand Up @@ -210,7 +222,8 @@ public static function autoloadProvider(): iterable
],
[],
),
['a610a8e036135f992c6edfb10ca9f4e9', 'e252736c6babb7c097ab6692dbcb2a5a'],
$defaultVendorDir,
[$excludedFile1, $excludedFile2],
<<<'PHP'
<?php

Expand Down Expand Up @@ -241,14 +254,14 @@ public static function autoloadProvider(): iterable
return ComposerAutoloaderInit2b848b33b146391fe6393b67efd7cd6f::getLoader();

PHP,
<<<'PHP'
<<<PHP
<?php

// @generated by Humbug Box

$loader = (static function () {
\$loader = (static function () {
// Backup the autoloaded Composer files
$existingComposerAutoloadFiles = $GLOBALS['__composer_autoload_files'] ?? [];
\$existingComposerAutoloadFiles = \$GLOBALS['__composer_autoload_files'] ?? [];

// @generated by Humbug Box

Expand All @@ -258,43 +271,43 @@ public static function autoloadProvider(): iterable
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
\$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
fwrite(STDERR, \$err);
} elseif (!headers_sent()) {
echo $err;
echo \$err;
}
}
trigger_error(
$err,
\$err,
E_USER_ERROR
);
}

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

$loader = ComposerAutoloaderInit2b848b33b146391fe6393b67efd7cd6f::getLoader();
\$loader = ComposerAutoloaderInit2b848b33b146391fe6393b67efd7cd6f::getLoader();

// Ensure InstalledVersions is available
$installedVersionsPath = __DIR__.'/composer/InstalledVersions.php';
if (file_exists($installedVersionsPath)) require_once $installedVersionsPath;
\$installedVersionsPath = __DIR__.'/composer/InstalledVersions.php';
if (file_exists(\$installedVersionsPath)) require_once \$installedVersionsPath;

// Restore the backup and ensure the excluded files are properly marked as loaded
$GLOBALS['__composer_autoload_files'] = \array_merge(
$existingComposerAutoloadFiles,
\array_fill_keys(['a610a8e036135f992c6edfb10ca9f4e9', 'e252736c6babb7c097ab6692dbcb2a5a'], true)
\$GLOBALS['__composer_autoload_files'] = \\array_merge(
\$existingComposerAutoloadFiles,
\\array_fill_keys(['{$excludedFile1Hash}', '{$excludedFile2Hash}'], true)
);

return $loader;
return \$loader;
})();

// Function aliases. For more information see:
// https://github.com/humbug/php-scoper/blob/master/docs/further-reading.md#function-aliases
if (!function_exists('bar')) { function bar() { return \Humbug\bar(...func_get_args()); } }
if (!function_exists('foo')) { function foo() { return \Humbug\foo(...func_get_args()); } }
if (!function_exists('bar')) { function bar() { return \\Humbug\\bar(...func_get_args()); } }
if (!function_exists('foo')) { function foo() { return \\Humbug\\foo(...func_get_args()); } }

return $loader;
return \$loader;

PHP,
];
Expand All @@ -308,6 +321,7 @@ public static function autoloadProvider(): iterable
],
[],
),
$defaultVendorDir,
[],
<<<'PHP'
<?php
Expand Down
Loading