php-standard-library/shell
Execute shell commands safely in PHP with robust argument escaping, clear capture of stdout/stderr, and helpful error handling. Part of PHP Standard Library, designed for predictable command execution and output management across environments.
## Technical Evaluation
### **Architecture Fit**
- **Use Case Alignment**: The package continues to excel in **structured shell command execution** within Laravel, with **6.1.2 introducing critical improvements** for **real-time streaming** and **resource management**. The new `timeout()` method and `onError()` callback enhance its suitability for **long-running CLI workflows** (e.g., CI/CD pipelines, Docker builds) and **interactive user feedback** (e.g., Laravel Livewire deployment dashboards). The streaming API now supports **chunked processing**, reducing memory overhead for large outputs.
- **API Design**: The addition of **`timeout()`** and **`onError()`** aligns with Laravel’s **resilience patterns** (e.g., `throw_if`, `retry()`). The structured API (e.g., `stdout`, `stderr`, `exitCode`) remains intuitive, but the new **error granularity** requires updates to existing error-handling logic. The **chunked streaming** feature is particularly valuable for **high-volume logs** (e.g., `docker logs --follow`), as it avoids buffering entire outputs in memory.
- **Security Focus**: The package retains **argument escaping** and **environment isolation**, but the new `onError()` callback introduces a **new attack surface** if not sanitized. For Laravel apps, **sensitive data in stderr** (e.g., API keys) must be **explicitly filtered** before streaming. The release **does not weaken security**, but **custom validation** is now required for error outputs.
### **Integration Feasibility**
- **Laravel Compatibility**: The package integrates seamlessly with **Artisan commands, queued jobs, and service containers**, with **6.1.2’s improvements** making it ideal for **async streaming workflows**. For example:
- **Artisan Commands**: Real-time progress bars for `php artisan deploy:stream`.
- **Queued Jobs**: Chunked streaming stored in **database/Redis** for later processing.
- **Livewire/Inertia**: Streamed CLI output displayed dynamically to users.
- **Cross-Platform Support**: The **`timeout()`** and **chunked streaming** features may introduce **Windows-specific quirks** (e.g., `cmd.exe` line buffering vs. `bash` chunking). Laravel’s **cross-platform tooling** (e.g., `php artisan serve`) will need **additional testing** to ensure consistent behavior, especially for **interactive commands**.
- **Async/Blocking Considerations**: While commands still execute **synchronously by default**, the new **`timeout()`** and **chunked streaming** enable **non-blocking workflows** when combined with Laravel’s **queues** or **process managers** (e.g., Symfony Process + Horizon). For **true async**, a **hybrid approach** (buffering chunks in a queue) is recommended.
### **Technical Risk**
- **Dependency Isolation**: The package’s **low activity** remains a risk, but **6.1.2’s backward-compatible improvements** (e.g., `timeout()`, `onError()`) reduce urgency for a fork. However, **long-term maintenance** is still uncertain, and a **custom wrapper** may be preferable for **mission-critical systems**.
- **Performance Overhead**: Chunked streaming **reduces memory usage** but introduces **CPU overhead** for callback processing. Benchmarking against **raw `proc_open()`** is critical, especially for **high-frequency commands** (e.g., real-time monitoring). The **`timeout()`** feature may also **increase resource usage** if not managed (e.g., orphaned processes).
- **Error Handling Granularity**: The new **`onError()`** callback **complicates error handling** if not integrated carefully. Laravel’s `throw_if` may need adaptation to **handle streaming errors** (e.g., partial stderr before a crash). **Custom exceptions** should be introduced for streaming-specific failures.
- **Environment Variable Leakage**: Streaming **stderr** (now supported via `onError()`) could **expose sensitive data** if not filtered. Laravel’s `env()` helper must be used **selectively**, and **sensitive patterns** (e.g., `PASSWORD_*`) should be **automatically redacted** in error outputs.
### **Key Questions**
1. **Adoption Risk**: Does the **new `timeout()` and `onError()`** justify using this package over alternatives (e.g., Symfony Process)? Should a **custom wrapper** be built to address **streaming quirks** (e.g., Windows chunking)?
2. **Async Strategy**: How will **chunked streaming** be handled in **async contexts** (e.g., queued jobs)? Will **buffering** (e.g., Redis) or **database storage** be required for large outputs?
3. **Cross-Platform Testing**: Are there **Windows-specific issues** with **chunked streaming** or **timeouts** (e.g., `cmd.exe` buffering)? Should a **fallback mechanism** (e.g., non-chunked mode) be implemented?
4. **Security Review**: Does the **`onError()` callback** preserve **argument escaping** for dynamic commands? Are there risks of **sensitive data leakage** in **stderr streams**?
5. **Monitoring**: How will **streaming timeouts** and **chunk failures** be logged/alerted in Laravel’s **Monolog** or **Sentry** setup? Should **custom metrics** (e.g., `stream_chunks_processed`) be introduced?
6. **Backward Compatibility**: Does **6.1.2 introduce breaking changes** for existing Laravel integrations? (e.g., new method signatures, exception types, or callback requirements)
---
## Integration Approach
### **Stack Fit**
- **Laravel Console**: The **`timeout()`** and **chunked streaming** features are **ideal for Artisan commands** requiring **real-time feedback with safeguards** (e.g., `php artisan deploy:stream --timeout=300`).
- **Queued Jobs**: For async use, **chunked streaming** should be **buffered and stored** (e.g., database, Redis) for later retrieval, rather than processed in real-time. Example:
```php
$shell->run('docker build .')->stream(fn($chunk) => Queue::push(new StoreChunk($chunk)));
SystemCommandService) with streaming-aware methods (e.g., streamWithTimeout()). Example:
class SystemCommand {
public function streamWithTimeout(string $command, int $timeout): void {
$shell->run($command)->timeout($timeout)->stream(fn($chunk) => Log::info($chunk));
}
}
Mockery or PHPUnit.exec()/shell_exec() with the package’s API, adding timeouts to critical commands.// Before (no timeout)
$shell->run('git pull')->getStdout();
// After (with timeout)
$shell->run('git pull')->timeout(120)->getStdout();
$shell->run('docker logs container')->stream(fn($chunk) => $this->handleChunk($chunk));
onError() callbacks, ensuring sensitive data is redacted.$shell->run('command')->onError(fn($error) => Log::error($this->redactSensitiveData($error)));
ProcessStreamedOutput::dispatch()).class StreamToQueue implements ShouldQueue {
public function handle(Shell $shell) {
$shell->run('docker build .')
->timeout(1800)
->stream(fn($chunk) => Queue::push(new StoreChunk($chunk)));
}
}
pcntl may improve process control for timeouts (optional).onError()) could conflict with Laravel’s .env. Use whitelisting/blacklisting to filter sensitive vars:
$shell->run('command')->withEnvironment(
How can I help you explore Laravel packages today?