php-standard-library/process
Typed, non-blocking PHP API for spawning, monitoring, and controlling child processes. Manage stdin/stdout/stderr streams, retrieve exit codes, and handle timeouts and signals with a clean, reliable interface for long-running and parallel tasks.
## Technical Evaluation
### Architecture Fit
- **Laravel Synergy**: Aligns perfectly with Laravel’s CLI-centric workflows (Artisan commands, queues, console kernels) by providing a structured alternative to raw `exec()` or `shell_exec()`. The typed API reduces boilerplate and improves maintainability in Laravel’s event-driven architecture.
- **Non-Blocking Design**: Critical for Laravel’s async patterns (e.g., queues, background jobs) where blocking calls would stall workers. The `start()`/`isRunning()` methods enable reactive process management.
- **Observability-Ready**: Output streams (`stdout`/`stderr`) and exit codes integrate seamlessly with Laravel’s logging (Monolog) and monitoring (Telescope/Laravel Debugbar) systems.
- **Framework Agnostic**: Avoids Laravel-specific dependencies, making it reusable across PHP projects while still fitting into Laravel’s service container and dependency injection patterns.
### Integration Feasibility
- **Laravel Facade Potential**: Can be wrapped in a `Process` facade (e.g., `Process::run('command')`) to mimic Laravel’s fluent API style, reducing cognitive load for developers.
- **Queue Integration**: Processes can be dispatched as Laravel jobs (e.g., `ProcessJob`) with retries, timeouts, and failure callbacks, leveraging Laravel’s queue system for scalability.
- **Artisan Command Hooks**: Ideal for pre/post-command hooks (e.g., `Artisan::starting()`, `Artisan::finished()`) to manage process lifecycle events.
- **Testing Compatibility**: Mockable design allows for unit testing process interactions (e.g., using `Mockery` to stub `Process::run()`).
### Technical Risk
- **Cross-Platform Fragmentation**: Process behavior differs across OSes (e.g., Windows vs. Unix signal handling, path separators). Requires rigorous cross-platform testing, especially for CI/CD pipelines or multi-environment deployments.
- **Resource Management**: Improperly managed processes (e.g., unbounded spawning, lack of timeouts) can lead to server instability (e.g., zombie processes, memory leaks). Mitigation requires:
- Hard timeouts (`setTimeout()`).
- Process limits (e.g., `ulimit` or Laravel’s `max_execution_time`).
- Cleanup hooks (e.g., `register_shutdown_function()`).
- **Security Gaps**: Command injection remains a risk if user input is directly interpolated into commands. Mitigation strategies:
- Whitelist allowed commands.
- Use `escapeshellarg()` or parameterized commands (e.g., `['git', 'checkout', $branch]`).
- Validate working directories and environment variables.
- **Dependency Stability**: As a niche package (0 stars, limited dependents), long-term maintenance relies on the PHP Standard Library’s roadmap. Risk mitigation:
- Fork the package if critical features are missing.
- Monitor for breaking changes in minor releases (e.g., 6.x → 7.x).
### Key Questions
1. **Use Case Granularity**:
- Will this replace *all* `exec()`/`shell_exec()` calls, or only specific workflows (e.g., CLI tools vs. API endpoints)?
- *Example*: Should database migrations use this instead of raw `Artisan::call()`?
2. **Error Recovery Strategy**:
- How will failed processes trigger Laravel events (e.g., `ProcessFailed`) or integrate with Laravel’s exception handler?
- *Example*: Should `ProcessException` extend Laravel’s `Handler` for centralized logging?
3. **Performance Benchmarks**:
- How does this compare to alternatives like `symfony/process` or `proc_open()` in terms of:
- Memory usage (e.g., for long-running processes).
- Startup latency (critical for CI/CD pipelines).
- *Tool*: Use `sysgettemp()` or `hrtime()` to measure overhead.
4. **Security Hardening**:
- What sanitization rules will apply to:
- Command arguments (e.g., prevent `rm -rf`).
- Working directories (e.g., restrict to `storage/` or `app/`).
- Environment variables (e.g., block `PATH` modifications).
- *Example*: Implement a `ProcessValidator` service.
5. **Observability Pipeline**:
- How will process metrics (runtime, exit codes, memory) be logged for debugging?
- *Example*: Integrate with Laravel Telescope’s `Process` table or Prometheus.
6. **Async Scaling**:
- How will concurrent processes be managed in a Laravel queue worker?
- *Example*: Limit to 5 parallel processes per worker to avoid resource exhaustion.
7. **Migration Strategy**:
- Which `exec()` calls are highest-priority to replace (e.g., those with error handling or logging)?
- *Tool*: Use `git grep` to audit `exec()` usage in the codebase.
---
## Integration Approach
### Stack Fit
- **Laravel CLI Tools**:
- Replace `Artisan::call()` or raw `exec()` in custom commands (e.g., `php artisan deploy:db`).
- *Example*: Use `Process` to run `git pull`, `composer install`, and `php artisan migrate` in a single deploy command.
- **Background Jobs**:
- Dispatch process-heavy tasks to queues (e.g., `ProcessJob` for image resizing with `ffmpeg`).
- *Example*: Offload `docker build` to a queue to avoid blocking API requests.
- **API Integrations**:
- Call external services (e.g., Docker API, GitHub CLI) with structured output parsing.
- *Example*: Use `Process` to trigger `docker-compose up` in a Laravel API endpoint.
- **Testing**:
- Mock processes in PHPUnit for unit tests (e.g., stub `Process::run()` to return canned output).
- *Example*: Test a `DeployCommand` without spawning actual processes.
### Migration Path
1. **Phase 1: CLI Replacement (Low Risk)**
- Replace ad-hoc `exec()` calls in Artisan commands with `Process`.
- *Example*: Convert `exec('git pull')` to:
```php
$process = new Process(['git', 'pull']);
$process->run();
if (!$process->isSuccessful()) { throw new RuntimeException($process->getErrorOutput()); }
```
- **Tools**: Use `git blame` to identify `exec()` usage and prioritize high-impact commands (e.g., deploy scripts).
2. **Phase 2: Queue Integration (Medium Risk)**
- Create a `ProcessJob` class to wrap processes in Laravel’s queue system.
- *Example*:
```php
class ProcessJob implements ShouldQueue {
public function handle() {
$process = new Process($this->command);
$process->run();
// Log output to a database or file
}
}
```
- **Tools**: Use Laravel’s `queue:work` to test concurrency limits.
3. **Phase 3: Facade/Service Layer (High Reward)**
- Build a `Process` facade or service to standardize API usage.
- *Example*:
```php
// config/process.php
'commands' => [
'deploy' => ['git pull', 'composer install', 'php artisan migrate'],
],
// Facade usage:
Process::run('deploy');
```
- **Tools**: Use Laravel’s `bind()` to inject `Process` into controllers.
4. **Phase 4: Observability (Long-Term)**
- Integrate with Laravel’s logging/monitoring (e.g., Telescope, Sentry).
- *Example*: Log process metadata to a `processes` table:
```php
Process::run('command')->then(function ($process) {
ProcessLog::create([
'command' => $process->getCommand(),
'exit_code' => $process->getExitCode(),
'duration_ms' => $process->getDuration(),
]);
});
```
### Compatibility
- **PHP Version**:
- Ensure compatibility with Laravel’s PHP version (e.g., 8.2+). Test with PHP 8.3 if using newer features (e.g., typed properties).
- *Tool*: Use `php -v` and `composer validate` to check constraints.
- **OS Dependencies**:
- Validate on target environments:
- Linux (Ubuntu/CentOS) for shared hosting.
- Windows (WSL or native) for internal tools.
- Docker containers (multi-stage builds).
- *Tool*: Use GitHub Actions or Laravel Forge to test across environments.
- **Laravel Facades**:
- If extending, create a `Process` facade to unify API calls:
```php
// app/Providers/AppServiceProvider.php
use PhpStandardLibrary\Process\Process;
public function boot() {
$this->app->bind('process', function () {
return new Process(config('app.default_command'));
});
}
```
- *Example*: Use `app('process')->run('command')` for dependency-injected processes.
### Sequencing
1. **Core Integration**:
- Start with a single `ProcessService` class in `App\Services` to encapsulate usage.
- *Example*:
```php
class ProcessService {
public function run(string $command, array $options = []): Process {
$process = new Process($command);
foreach ($options as $key => $value)
How can I help you explore Laravel packages today?