keradus/cli-executor
Execute CLI commands from PHP with a simple, lightweight wrapper. Run processes, capture output, handle exit codes, and manage arguments for reliable command execution in scripts and Laravel apps.
Start by installing the package via Composer:
composer require --dev keradus/cli-executor
This lightweight wrapper around symfony/process is designed for executing CLI commands reliably in development tooling—especially testing scenarios. Its sole public class, ScriptExecutor, provides minimal boilerplate around command execution, result capture, and error handling.
Begin by reviewing the ScriptExecutor class directly in the source (it’s under 100 lines). Look for usage examples in the PHP-CS-Fixer test suite (since this package originates from that project). Its niche is not general-purpose CLI orchestration, but rather consistent, repeatable execution of external binaries within controlled environments (e.g., tests, automated scripts).
ScriptExecutor to drive real CLI binaries (e.g., php-cs-fixer, phpunit, phpstan) in integration tests—avoiding mocks of external tool behavior:
use Keradus\CliExecutor\ScriptExecutor;
$executor = new ScriptExecutor();
$result = $executor->execute('php-cs-fixer', ['check', 'src', '--dry-run']);
$this->assertSame(0, $result->getExitCode());
$this->assertStringNotContainsString('found', $result->getOutput());
$executor->execute('php', ['-l', 'src']) || throw new RuntimeException('Lint failed');
$executor->execute('phpunit') || throw new RuntimeException('Tests failed');
$executor->execute('php-cs-fixer', ['fix', 'src']) || throw new RuntimeException('Format failed');
$executor = new ScriptExecutor([
'cwd' => sys_get_temp_dir() . '/my-test-project',
'env' => ['SYMFONY_DOTENV_VARS' => 'APP_SECRET,DB_URL'],
]);
The package shines where you need deterministic CLI invocation without extra abstraction layers—ideal for tool authors or test suites needing binary fidelity.
Process has no timeout unless set via constructor option. Add it for safety:
$executor = new ScriptExecutor(['timeout' => 30]); // seconds
Otherwise, long-running commands (e.g., Composer installs) may hang.exitCode === 0 ≠ success: Many tools exit cleanly while logging warnings to stderr. Always inspect both outputs:
$exitCode = $result->getExitCode();
$stderr = $result->getErrorOutput();
if ($exitCode !== 0 || !empty($stderr)) {
// Handle partial failures, e.g., lint warnings with exit 0
}
PATH, use absolute paths (e.g., /usr/bin/php-cs-fixer), or manually resolve via which first.ScriptExecutor in your own CliRunner for testability.ScriptResult to understand payload structure:
dd([
'exitCode' => $result->getExitCode(),
'signalCode' => $result->getSignalCode(),
'stdout' => $result->getOutput(),
'stderr' => $result->getErrorOutput(),
]);
Output format is stable but undocumented—source inspection is your best reference.How can I help you explore Laravel packages today?