- How does this package improve over Laravel’s built-in Artisan::call() for running CLI commands?
- While `Artisan::call()` is Laravel-specific, this package works for *any* shell command (not just Artisan) and provides a unified API for stdout/stderr capture, timeouts, and mocking. It’s ideal for external tools (e.g., Docker, Git) or cross-framework PHP projects while keeping Laravel’s CLI workflows intact.
- Can I use this for Laravel queues (e.g., dispatching shell commands in jobs)?
- Yes. The package supports timeouts and exit code checks, which are critical for queue jobs. Use `Terminal::run()->timeout(60)` to prevent hanging, and handle exceptions in your job’s `handle()` method. It’s also mockable for unit testing job logic without hitting the shell.
- Does this package protect against shell injection attacks when using user input?
- Yes, it escapes arguments by default via Symfony Process’s built-in safety measures. However, *always validate dynamic inputs* (e.g., file paths) before passing them to `Terminal::run()`. For extra security, extend the package to sanitize inputs or use Laravel’s `Str::of()` for path validation.
- How do I mock shell commands in Laravel tests (e.g., for PHPUnit)?
- The package’s design makes mocking straightforward. Use PHPUnit’s `createMock()` to fake `Terminal` or its underlying `Process` interface. For example: `$mock = $this->createMock(Terminal::class); $mock->method('run')->willReturn(new ProcessResult(0, 'Success'));` Then inject the mock into your service.
- Will this work in Laravel Forge/Valet environments, or is it server-only?
- It works in all environments, but Forge/Valet may impose shell restrictions. Test commands locally first, and handle `ProcessFailedException` gracefully. For Valet, ensure the command’s working directory is writable. Shared hosting may block shell access entirely—check with your provider before relying on this.
- Can I chain multiple commands (e.g., `git pull && composer install`) like with shell pipes?
- Yes, use the fluent API to chain commands: `Terminal::run('git pull')->then('composer install')`. For pipes, pass the output of one command to another: `Terminal::run('git pull')->stdout()->pipeTo('composer install')`. The package handles intermediate processes safely.
- How do I handle timeouts for long-running commands in Laravel queues?
- Set a timeout when running the command: `Terminal::run('php artisan queue:work')->timeout(300)`. If the command exceeds the timeout, it throws a `ProcessTimedOutException`. For queues, combine this with Laravel’s `maxAttempts` to retry transient failures.
- Does this package support real-time output streaming (e.g., for Livewire/Inertia dashboards)?
- Yes. Use `Terminal::run()->stream()` to get a `Process` object, then iterate over its output in real-time: `foreach ($process->getOutput() as $type => $buffer) { echo $buffer; }`. This works well with Livewire’s reactivity or Inertia’s server-side rendering.
- What Laravel versions and PHP requirements does this package support?
- The package is compatible with Laravel 10/11 and PHP 8.1+. Check the `composer.json` constraints for exact versions. If you’re on an older Laravel version, ensure Symfony Process (the underlying library) is compatible—Laravel 9+ typically includes a recent enough version.
- Are there alternatives to this package for Laravel shell execution?
- For Laravel-specific CLI needs, `Artisan::call()` is built-in. For broader shell execution, alternatives include `symfony/process` (directly) or `spatie/laravel-shell`. However, this package stands out for its fluent API, built-in mocking support, and Laravel-friendly error handling. Choose it if you need testability and cross-command consistency.