Command Executor Library
A robust PHP library for executing system commands with timeout support, output handling, and process management.
Features
- Simple API - Easy to use interface for command execution
- Error Handling - Comprehensive exception handling with detailed error information
- Process Management - Robust process handling with non-blocking I/O and timeouts
- Output Capture - Captures both stdout and stderr streams and exposes them separately and combined
- Environment Variables - Support for custom environment variables
- Working Directory - Ability to set custom working directory
- PHP 8.3+ - Modern PHP with strict types and full type safety
Installation
Install via Composer:
composer require adachsoft/command-executor-lib
Requirements
- PHP 8.3 or higher
- No external system dependencies
Quick Start
Basic Usage
<?php
use AdachSoft\CommandExecutorLib\CommandExecutorException;
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
try {
$output = $executor->execute('ls -la');
echo $output;
} catch (CommandExecutorException $e) {
echo 'Error: ' . $e->getMessage();
}
Advanced Usage
Execute with Environment Variables
<?php
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
$env = [
'PATH' => '/usr/local/bin:/usr/bin:/bin',
'DEBUG' => 'true',
];
$output = $executor->execute('echo $DEBUG', $env);
Execute in Specific Directory
<?php
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
$output = $executor->execute('ls -la', [], '/var/www');
Handle Command Failures
<?php
use AdachSoft\CommandExecutorLib\CommandExecutorException;
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
try {
$output = $executor->execute('nonexistent-command');
} catch (CommandExecutorException $e) {
echo 'Command failed: ' . $e->getMessage() . "\n";
echo 'Exit code: ' . $e->getExitCode() . "\n";
echo 'Output: ' . $e->getCommandOutput() . "\n";
}
Get Exit Code Without Exception (array result)
<?php
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
$result = $executor->executeWithExitCode('ls /nonexistent');
if ($result['exitCode'] === 0) {
echo 'Success (stdout): ' . $result['stdout'] . "\n";
} else {
echo 'Failed with code ' . $result['exitCode'] . ': ' . $result['output'] . "\n";
}
Get Detailed Result as DTO
<?php
use AdachSoft\CommandExecutorLib\CommandExecutionResult;
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
$result = $executor->executeWithResult('ls /nonexistent');
if ($result->isSuccessful()) {
echo 'Success (stdout): ' . $result->stdout . "\n";
} else {
echo 'Failed with code ' . $result->exitCode . ': ' . $result->output . "\n";
}
Using Timeouts
<?php
use AdachSoft\CommandExecutorLib\CommandExecutorException;
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
try {
$output = $executor->execute('sleep 5', [], null, 1.0);
} catch (CommandExecutorException $e) {
echo 'Command timed out: ' . $e->getMessage();
}
Real-World Examples
Git Operations
<?php
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
$branch = trim($executor->execute('git rev-parse --abbrev-ref HEAD'));
echo "Current branch: $branch";
$status = $executor->execute('git status --porcelain', [], '/path/to/project');
if (empty($status)) {
echo 'Working directory is clean';
} else {
echo 'Uncommitted changes detected';
}
File Operations
<?php
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
$backupDir = '/backups/' . date('Y-m-d');
$executor->execute("mkdir -p $backupDir");
$executor->execute("cp -r /var/www $backupDir/");
$usage = $executor->execute('df -h / | tail -1');
echo "Disk usage: $usage";
Docker Operations
<?php
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
$executor->execute('docker build -t myapp:latest .', [], '/path/to/dockerfile');
$result = $executor->executeWithExitCode('docker run --rm myapp:latest');
if ($result['exitCode'] === 0) {
echo 'Container executed successfully';
}
API Reference
CommandExecutorInterface
The main interface that all command executors must implement.
Methods
execute(string $command, array $env = [], ?string $cwd = null, ?float $timeout = null): string
executeWithExitCode(string $command, array $env = [], ?string $cwd = null, ?float $timeout = null): array{stdout: string, stderr: string, output: string, exitCode: int}
executeWithResult(string $command, array $env = [], ?string $cwd = null, ?float $timeout = null): CommandExecutionResult
SimpleCommandExecutor
Default implementation using PHP's proc_open().
Constructor
$executor = new SimpleCommandExecutor();
CommandExecutionResult
Immutable data transfer object representing command execution result.
Properties
string $stdout - Standard output (STDOUT)
string $stderr - Standard error output (STDERR)
string $output - Combined stdout + stderr
int $exitCode - Process exit code
Methods
isSuccessful(): bool - Returns true when exitCode === 0
CommandExecutorException
Exception thrown when command execution fails.
Methods
getCommandOutput(): ?string - Returns the command output
getExitCode(): ?int - Returns the exit code
getStdout(): ?string - Returns captured standard output (STDOUT), if available
getStderr(): ?string - Returns captured standard error output (STDERR), if available
fromCommandFailure() - Static factory method for creating exceptions
Error Handling
The library provides comprehensive error handling through CommandExecutorException:
<?php
use AdachSoft\CommandExecutorLib\CommandExecutorException;
use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
$executor = new SimpleCommandExecutor();
try {
$output = $executor->execute('invalid-command');
} catch (CommandExecutorException $e) {
switch ($e->getExitCode()) {
case 127:
echo 'Command not found';
break;
case 126:
echo 'Permission denied';
break;
default:
echo 'Command failed: ' . $e->getMessage();
}
}
CommandExecutorException exposes both the full combined output via getCommandOutput() and separate streams via getStdout()/getStderr(). When a command times out, the exception message may include a short stderr summary if stderr is not empty.
Testing
Run the test suite:
composer test
Run with coverage:
composer test-coverage
Run code quality checks:
composer quality
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature)
- Run quality checks (
composer quality)
- Commit your changes (
git commit -m 'Add amazing feature')
- Push to the branch (
git push origin feature/amazing-feature)
- Open a Merge Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
See CHANGELOG.md for a detailed list of changes.