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

Process Laravel Package

symfony/process

Symfony Process component runs external commands in separate processes with robust control over arguments, environment, timeouts, and working directory. Capture stdout/stderr, stream live output, manage input, and handle exit codes reliably across platforms.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require symfony/process
    

    For Laravel, no additional setup is required—it integrates seamlessly with the framework.

  2. First Use Case: Execute a simple command and capture output:

    use Symfony\Component\Process\Process;
    use Symfony\Component\Process\Exception\ProcessFailedException;
    
    $process = new Process(['ls', '-la']);
    $process->run();
    
    // Check if the process ran successfully
    if (!$process->isSuccessful()) {
        throw new ProcessFailedException($process);
    }
    
    echo $process->getOutput();
    
  3. Where to Look First:

    • Official Documentation for API reference and examples.
    • Laravel’s Artisan commands (e.g., php artisan make:command) for integrating subprocesses into CLI workflows.
    • The Process facade for dependency injection in Laravel services.

Implementation Patterns

Core Workflows

1. Basic Command Execution

$process = new Process(['git', 'pull']);
$process->run();

if ($process->isSuccessful()) {
    echo "Git pull succeeded!";
} else {
    echo "Error: " . $process->getErrorOutput();
}

2. Streaming Output in Real-Time

Useful for long-running commands (e.g., docker-compose up or npm run build):

$process = new Process(['docker-compose', 'up']);
$process->start();

// Stream output line by line
foreach ($process as $type => $output) {
    if (Process::ERR === $type) {
        echo 'ERR > ' . $output;
    } else {
        echo 'OUT > ' . $output;
    }
}

3. Environment Variables and Working Directory

$process = new Process(['php', 'script.php']);
$process->setEnvironment([
    'DB_HOST' => 'localhost',
    'DEBUG' => 'true',
]);
$process->setWorkingDirectory(storage_path('app'));
$process->run();

4. Timeouts and Signal Handling

Prevent hanging processes:

$process = new Process(['sleep', '10']);
$process->setTimeout(5); // Fail after 5 seconds
$process->run();

if (!$process->isSuccessful()) {
    $process->stop(); // Terminate the process
}

5. Integration with Laravel Artisan Commands

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Symfony\Component\Process\Process;

class RunBackupCommand extends Command
{
    protected $signature = 'backup:run';
    protected $description = 'Run database backup';

    public function handle()
    {
        $process = new Process(['mysqldump', '-u', 'root', '-p', 'database']);
        $process->run();

        if (!$process->isSuccessful()) {
            $this->error($process->getErrorOutput());
            return 1;
        }

        $this->info('Backup completed!');
    }
}

6. Asynchronous Execution with Queues

Offload subprocess execution to a queue worker (e.g., RunProcessMessage in Symfony Messenger):

use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Process\RunProcessMessage;

$bus->dispatch(new RunProcessMessage([
    'command' => 'php',
    'arguments' => ['artisan', 'queue:work'],
    'timeout' => 3600,
]));

Integration Tips

  1. Dependency Injection: Laravel’s service container automatically resolves Process instances. Bind custom configurations in AppServiceProvider:

    $this->app->bind(Process::class, function ($app) {
        return new Process(['default-command']);
    });
    
  2. Logging Process Output: Use Laravel’s logging facade to capture subprocess output:

    $process = new Process(['some-command']);
    $process->run();
    
    \Log::info('Command output:', [
        'stdout' => $process->getOutput(),
        'stderr' => $process->getErrorOutput(),
        'exit_code' => $process->getExitCode(),
    ]);
    
  3. Cross-Platform Path Handling: Use Laravel’s str() helper or Path facade to ensure paths are OS-agnostic:

    use Illuminate\Support\Str;
    
    $process = new Process(['node', Str::of(storage_path('scripts/build.js'))]);
    
  4. Retry Logic for Transient Failures: Combine with Laravel’s retry helper or a custom retry mechanism:

    use Illuminate\Support\Facades\Retry;
    
    Retry::times(3)->attempt(function () {
        $process = new Process(['git', 'pull']);
        $process->run();
    
        if (!$process->isSuccessful()) {
            throw new \RuntimeException('Git pull failed');
        }
    });
    

Gotchas and Tips

Pitfalls

  1. Windows Environment Variable Limits:

    • Issue: Windows has a ~32KB limit for environment variables. Exceeding this throws InvalidArgumentException.
    • Fix: Validate environment size or split variables into multiple setEnvironment() calls.
    • Example:
      $env = [];
      foreach ($largeEnvVars as $key => $value) {
          if (strlen($key . '=' . $value) > 32000) {
              throw new \RuntimeException("Environment variable too large for Windows");
          }
          $env[$key] = $value;
      }
      $process->setEnvironment($env);
      
  2. Broken Pipes on stdin:

    • Issue: Writing to a broken stdin pipe (e.g., when the subprocess closes unexpectedly) can cause PHP to hang.
    • Fix: Use ignore_stdin() or handle Process::ERR output gracefully.
    • Example:
      $process = new Process(['cat']);
      $process->ignoreStdin(); // Ignore stdin if not needed
      
  3. PTY Mode and Mixed Output:

    • Issue: In PTY mode, stdout and stderr can be mixed, making parsing difficult.
    • Fix: Avoid PTY mode unless necessary, or use separate pipes for stderr:
      $process = new Process(['some-command'], null, [
          'pipe_stderr' => true, // Separate stderr pipe
      ]);
      
  4. Signal Handling Across Platforms:

    • Issue: Signal handling (e.g., SIGKILL) behaves differently on Windows vs. Unix.
    • Fix: Use setTimeout() for cross-platform timeouts instead of signals:
      $process->setTimeout(10); // Fails after 10 seconds
      
  5. Command Injection Risks:

    • Issue: Even with Symfony Process, improperly constructed commands can still be vulnerable.
    • Fix: Always use the Process constructor with an array of arguments, not a shell string:
      // UNSAFE: Vulnerable to shell injection
      $process = new Process('ls ' . $userInput);
      
      // SAFE: Array arguments
      $process = new Process(['ls', $userInput]);
      
  6. Resource Leaks:

    • Issue: Forgetting to call run(), start(), or stop() can leave processes running.
    • Fix: Use __destruct() to ensure cleanup or wrap in a try-finally block:
      $process = new Process(['sleep', '10']);
      try {
          $process->start();
          // ... stream output ...
      } finally {
          $process->stop();
      }
      

Debugging Tips

  1. Enable Verbose Output: Use setVerbose() to debug process execution:

    $process = new Process(['some-command']);
    $process->setVerbose();
    $process->run();
    
  2. Check Exit Codes:

    • 0: Success.
    • >0: Failure (check getErrorOutput()).
    • 127: Command not found.
    • 137: Process killed (e.g., by SIGKILL).
  3. Log Process Metadata: Log the full command, environment, and working directory for debugging:

    \Log::debug('Process executed', [
        'command' => $process->getCommandLine(),
        'env' => $process->getEnvironment(),
        'cwd' => $process->getWorkingDirectory(),
    ]);
    
  4. Use getOutputFromFunction() for Testing: Simulate subprocess output in tests:

    $output = Process::getOutputFromFunction(function () {
        return "test output\n";
    });
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport