Skip to content
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
47 changes: 47 additions & 0 deletions core/tests/Unit/Install/CliInstallExecAutoloadTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

/*
|--------------------------------------------------------------------------
| The CLI installer's composer step runs before the core bootstraps
|--------------------------------------------------------------------------
|
| Nothing has registered the Composer autoloader at that point, so the installer
| has to make the ExecWithFallback package loadable itself. The CI install job
| passes --skipComposer=y and never reaches the call, which is how a fresh
| `php cli-install.php` came to fatal with "Class ExecWithFallback not found".
|
*/

$root = dirname(__DIR__, 4);

it('loads ExecWithFallback in a process without the Composer autoloader', function () use ($root) {
$probe = tempnam(sys_get_temp_dir(), 'evo_exec_probe_');
file_put_contents($probe, '<?php'
. ' define("EVO_CORE_PATH", ' . var_export($root . '/core/', true) . ');'
. ' require ' . var_export($root . '/install/src/functions.php', true) . ';'
. ' echo json_encode([loadExecWithFallback(), class_exists("ExecWithFallback\\ExecWithFallback"), class_exists("ExecWithFallback\\Availability")]);');
$out = trim((string) shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($probe) . ' 2>&1'));
unlink($probe);

expect($out)->toBe('[true,true,true]');
});

it('reports a missing package instead of fataling', function () {
$core = sys_get_temp_dir() . '/evo_exec_missing_' . uniqid() . '/';
$probe = tempnam(sys_get_temp_dir(), 'evo_exec_probe_');
file_put_contents($probe, '<?php'
. ' define("EVO_CORE_PATH", ' . var_export($core, true) . ');'
. ' require ' . var_export(dirname(__DIR__, 4) . '/install/src/functions.php', true) . ';'
. ' var_export(loadExecWithFallback());');
$out = trim((string) shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($probe) . ' 2>&1'));
unlink($probe);

expect($out)->toBe('false');
});

it('makes the package loadable before the installer calls it', function () use ($root) {
$installer = (string) file_get_contents($root . '/install/cli-install.php');

expect(strpos($installer, 'loadExecWithFallback()'))
->toBeLessThan(strpos($installer, 'ExecWithFallback::exec($cmd'));
});
6 changes: 6 additions & 0 deletions install/cli-install.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ class InstallEvo
*/
protected function runComposerUpdate(string $cmd): void
{
// This runs before the core bootstraps, so nothing has registered the Composer autoloader yet.
if (!loadExecWithFallback()) {
warning('⚠ The exec-with-fallback package is missing. Run "composer update" manually.');
return;
}

$out = [];
$exitCode = 0;
try {
Expand Down
34 changes: 34 additions & 0 deletions install/src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,40 @@ function seed($folder = 'install')
}
}

/**
* Make the ExecWithFallback classes loadable without the Composer autoloader.
*
* The CLI installer runs "composer update" before the core bootstraps, so the vendor autoloader
* is not registered yet when the installer wants the package. Only its own PSR-4 directory is
* mapped: requiring vendor/autoload.php that early would pull Laravel's helper files in over the
* installer's own.
*
* @since 3.5.9
* @return bool whether the package is available
*/
function loadExecWithFallback(): bool
{
if (class_exists('ExecWithFallback\ExecWithFallback')) {
return true;
}

$directory = EVO_CORE_PATH . 'vendor/rosell-dk/exec-with-fallback/src/';
if (!is_file($directory . 'ExecWithFallback.php')) {
return false;
}

spl_autoload_register(static function (string $class) use ($directory): void {
if (strncmp($class, 'ExecWithFallback\\', 17) === 0) {
$file = $directory . substr($class, 17) . '.php';
if (is_file($file)) {
require $file;
}
}
});

return class_exists('ExecWithFallback\ExecWithFallback');
}

/**
* Print a neutral info message (default color).
*
Expand Down
Loading