Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Shell Laravel Package

php-standard-library/shell

Execute shell commands safely in PHP with built-in argument escaping and controlled handling of stdout/stderr. Part of PHP Standard Library, providing a simple API for running processes, capturing error output, and managing failures predictably.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the package via Composer:
    composer require php-standard-library/shell
    
  2. Basic command execution (replace exec()):
    use PHPStandardLibrary\Shell\Shell;
    
    $shell = new Shell();
    $result = $shell->run('ls -la');
    echo $result->getOutput();
    
  3. Check exit codes for error handling:
    if ($result->isFailed()) {
        throw new \RuntimeException('Command failed: ' . $result->getErrorOutput());
    }
    

First Use Case: Artisan Command

Replace unsafe exec() in a Laravel Artisan command:

use Illuminate\Console\Command;
use PHPStandardLibrary\Shell\Shell;

class DeployCommand extends Command
{
    public function __construct(private Shell $shell) {
        parent::__construct();
    }

    public function handle()
    {
        $result = $this->shell->run('git pull');
        $this->info($result->getOutput());
    }
}

Register in AppServiceProvider:

public function register()
{
    $this->app->singleton(Shell::class);
}

Implementation Patterns

Core Workflows

1. Blocking Execution (Most Common)

// Run a command and capture output
$result = $shell->run('docker ps');
$output = $result->getOutput(); // stdout
$error = $result->getErrorOutput(); // stderr
$exitCode = $result->getExitCode();

// Validate exit code
$result->throwIfFailed(); // throws on non-zero exit

2. Streaming Output (Real-Time)

// Stream output line-by-line (PHP 8.1+)
$shell->run('tail -f logfile.log')
    ->stream(fn(string $chunk) => $this->output->writeln($chunk));

// For async jobs, buffer to database/Redis
$shell->run('docker build .')
    ->stream(fn($chunk) => DB::table('build_logs')->insert(['chunk' => $chunk]));

3. Argument Escaping (Security)

// Safe argument handling (prevents injection)
$shell->run('cp', ['source.txt', 'destination.txt']);

// Dynamic arguments
$userInput = 'file.txt';
$shell->run('mv', [$userInput])->escapeArguments(); // auto-escapes

4. Environment Management

// Set environment variables
$shell->run('php artisan queue:work')
    ->withEnvironment(['QUEUE_CONNECTION' => 'database']);

// Merge with existing env
$shell->run('command')->withEnvironment($_ENV);

5. Pipes and Redirection

// Pipe output between commands
$shell->run('cat file.log | grep error');

// Redirect stderr to stdout
$shell->run('command 2>&1');

Laravel-Specific Patterns

Artisan Command Integration

use Illuminate\Console\Command;
use PHPStandardLibrary\Shell\Shell;

class DockerCommand extends Command
{
    public function __construct(private Shell $shell) {}

    public function handle()
    {
        $this->info('Starting containers...');
        $result = $this->shell->run('docker-compose up -d');
        $this->line($result->getOutput());
    }
}

Queued Jobs with Streaming

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use PHPStandardLibrary\Shell\Shell;

class BuildJob implements ShouldQueue
{
    use Queueable, SerializesModels;

    public function handle(Shell $shell)
    {
        $shell->run('docker build .')
            ->stream(fn($chunk) => Log::debug('Build: ' . $chunk));
    }
}

Service Layer Abstraction

// app/Services/SystemCommand.php
namespace App\Services;

use PHPStandardLibrary\Shell\Shell;

class SystemCommand
{
    public function __construct(private Shell $shell) {}

    public function runSafe(string $command): string
    {
        return $this->shell->run($command)
            ->throwIfFailed()
            ->getOutput();
    }

    public function streamLogs(string $command, callable $callback): void
    {
        $this->shell->run($command)->stream($callback);
    }
}

Gotchas and Tips

Pitfalls

  1. Streaming on Windows (cmd.exe)

    • Issue: cmd.exe buffers output, causing delayed or partial chunks.
    • Fix: Use PowerShell or add a --line-buffering flag if possible:
      $shell->run('powershell -Command "Get-Content logfile.log -Wait"')
          ->stream(...);
      
  2. Argument Escaping Overhead

    • Issue: Over-escaping can break commands with special characters (e.g., * in filenames).
    • Fix: Use escapeArguments(false) for trusted inputs:
      $shell->run('mv', [$file])->escapeArguments(false);
      
  3. Memory Leaks in Streaming

    • Issue: Unbounded streaming (e.g., tail -f) can exhaust memory if not buffered.
    • Fix: Limit chunk size or use a queue:
      $shell->run('tail -f logfile.log')
          ->stream(fn($chunk) => $this->buffer->add($chunk));
      
  4. Exit Code Misinterpretation

    • Issue: Some commands (e.g., grep) return non-zero on "no match" but are successful.
    • Fix: Custom validation:
      if ($result->isFailed() && $result->getExitCode() !== 1) {
          throw new \RuntimeException('Command failed');
      }
      
  5. Environment Variable Leakage

    • Issue: Streaming output may expose sensitive data from $_ENV.
    • Fix: Filter environment variables:
      $safeEnv = array_filter($_ENV, fn($key) => !str_starts_with($key, 'SECRET_'));
      $shell->run('command')->withEnvironment($safeEnv);
      

Debugging Tips

  1. Inspect Raw Output Use getOutput() and getErrorOutput() to debug command behavior:

    $result = $shell->run('ls /nonexistent');
    $this->error("Output: " . $result->getOutput());
    $this->error("Errors: " . $result->getErrorOutput());
    
  2. Enable Verbose Mode Add -v flags to commands for debugging:

    $shell->run('docker-compose up -d -v');
    
  3. Log Streaming Chunks For async jobs, log chunks to track progress:

    $shell->run('docker build .')
        ->stream(fn($chunk) => Log::debug('Build chunk: ' . substr($chunk, 0, 100)));
    
  4. Test Cross-Platform Verify commands work on both Linux/macOS and Windows:

    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        $shell->run('powershell -Command "Get-Process"');
    } else {
        $shell->run('ps aux');
    }
    

Extension Points

  1. Custom CommandResult Extend CommandResult for Laravel-specific logic:

    use PHPStandardLibrary\Shell\CommandResult;
    
    class LaravelCommandResult extends CommandResult
    {
        public function toNotification(): Notification
        {
            return new CommandFailedNotification($this);
        }
    }
    
  2. Stream Filtering Add middleware to filter streaming output:

    $shell->run('command')
        ->stream(fn($chunk) => $this->filterSensitiveData($chunk));
    
  3. Retry Logic Integrate with Laravel’s retry system:

    use Illuminate\Support\Facades\Retry;
    
    Retry::retry(3, fn() => $shell->run('command')->throwIfFailed());
    
  4. Windows-Specific Wrappers Override shell behavior for Windows:

    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        $shell = new WindowsShellWrapper();
    }
    

Configuration Quirks

  1. Default Timeout The package has no default timeout, which can cause hangs. Set explicitly:

    $shell->run('slow-command')->timeout(30); // 30 seconds
    
  2. Environment Inheritance By default, the shell does not inherit $_ENV. Explicitly merge if needed:

    $shell->run('command')->withEnvironment($_ENV);
    
  3. Working Directory Change the working directory for commands:

    $shell->run('ls')->workingDirectory('/path/to/dir');
    
  4. Shell Integration Force a specific shell (e.g., bash vs

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky