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

Terminal Laravel Package

titasgailius/terminal

titasgailius/terminal is a Laravel-friendly wrapper for running shell commands via Symfony Process. Build, execute, and chain commands fluently, capture output, handle errors/timeouts, and mock processes in tests for reliable command-line automation.

Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require titasgailius/terminal
    

    For Laravel, no service provider is needed—use the facade or resolve the class directly.

  2. First Command Execution

    use Terminal\Command;
    
    $output = Command::run('ls -la')->output();
    echo $output;
    
  3. Basic Error Handling

    try {
        $result = Command::run('ls /nonexistent')->output();
    } catch (\Terminal\Exception\CommandFailedException $e) {
        // Handle failure (exit code != 0)
        echo "Command failed: " . $e->getMessage();
    }
    

Where to Look First

  • Facade: Terminal\Facades\Command (Laravel) or Terminal\Command (plain PHP).
  • Core Methods:
    • run() → Execute and return a Result object.
    • output() → Capture stdout.
    • errorOutput() → Capture stderr.
    • exitCode() → Get exit code.
  • Documentation: Focus on the API reference for method signatures and options.

Implementation Patterns

Fluent Command Composition

Pattern: Chain methods for readability and reusability.

$output = Command::run('git pull')
    ->workingDirectory(storage_path('app'))
    ->env(['GIT_TERMINAL_PROMPT', '0'])
    ->timeout(30)
    ->output();

Use Case: Wrap repetitive commands in a helper:

function deploy() {
    return Command::run('php artisan deploy')
        ->env(['DEPLOY_KEY', config('deploy.key')])
        ->timeout(120);
}

Handling Output Streams

Pattern: Stream real-time output for CLI feedback.

Command::run('docker-compose up')
    ->stream(function ($type, $line) {
        if ($type === 'stdout') {
            echo "[LOG] $line\n";
        } elseif ($type === 'stderr') {
            echo "[ERR] $line\n";
        }
    });

Use Case: Log command output to a file:

$logFile = fopen(storage_path('logs/command.log'), 'a');
Command::run('php artisan queue:work')
    ->stream(function ($type, $line) use ($logFile) {
        fwrite($logFile, "$type: $line\n");
    });

Integration with Laravel Artisan

Pattern: Extend Artisan commands with shell logic.

use Terminal\Command;
use Illuminate\Console\Command as ArtisanCommand;

class MyArtisanCommand extends ArtisanCommand {
    protected function handle() {
        $result = Command::run('php artisan migrate:fresh')
            ->output();

        $this->info("Migration output:\n$result");
    }
}

Use Case: Pre/post-command hooks:

Command::run('npm install')
    ->before(function () {
        $this->info('Running npm install...');
    })
    ->after(function ($result) {
        $this->line("Exit code: {$result->exitCode()}");
    });

Testing Strategies

Pattern: Mock commands in tests using Terminal\Mock\Command.

public function test_deploy_command() {
    Command::shouldReceive('run')
        ->with('php artisan deploy')
        ->once()
        ->andReturn(new \Terminal\Result(0, 'Success', ''));

    $this->assertTrue($this->deploy());
}

Use Case: Test error scenarios:

Command::shouldReceive('run')
    ->with('ls /invalid')
    ->andThrow(new \Terminal\Exception\CommandFailedException(1, 'Not found'));

$this->expectException(\RuntimeException::class);
$this->deploy();

Gotchas and Tips

Pitfalls

  1. Argument Escaping

    • Gotcha: Forgetting to escape arguments can lead to command injection.
      // UNSAFE: Vulnerable to injection
      Command::run('rm ' . $userInput);
      
    • Fix: Use the fluent API:
      Command::run('rm')->arg($userInput)->safe();
      
  2. Timeouts

    • Gotcha: Default timeout is null (no timeout). Always set a timeout for long-running commands.
      Command::run('php artisan queue:work')->timeout(60); // 60 seconds
      
  3. Working Directory

    • Gotcha: Relative paths in workingDirectory() are resolved relative to the current PHP process, not the command’s perspective.
      // May not work as expected
      Command::run('pwd')->workingDirectory('../');
      
    • Fix: Use absolute paths:
      Command::run('pwd')->workingDirectory(realpath('../'));
      
  4. Environment Variables

    • Gotcha: Overwriting system environment variables can break dependencies.
      // Avoid unless necessary
      Command::run('npm install')->env(['PATH', '/custom/path']);
      
    • Fix: Prefer passing only required vars:
      Command::run('npm install')->env(['NODE_ENV', 'production']);
      

Debugging Tips

  1. Inspect Raw Output Use ->combinedOutput() to debug mixed stdout/stderr:

    $combined = Command::run('ls /invalid')->combinedOutput();
    $this->info($combined);
    
  2. Exit Code Analysis Check exit codes for non-obvious failures:

    $exitCode = Command::run('git status')->exitCode();
    if ($exitCode === 128) {
        $this->error('Command killed by signal.');
    }
    
  3. Real-Time Streaming Issues

    • Gotcha: Streaming may buffer output if the command writes in large chunks.
    • Fix: Use ->stream() with a buffer or log to a file.

Extension Points

  1. Custom Exceptions Extend \Terminal\Exception\CommandFailedException for domain-specific errors:

    class DeploymentFailedException extends CommandFailedException {}
    
  2. Command Wrappers Create reusable command classes:

    class GitCommand {
        public function pull(string $repo) {
            return Command::run('git pull origin main')
                ->workingDirectory($repo)
                ->safe();
        }
    }
    
  3. Integration with Laravel Events Trigger events on command success/failure:

    Command::run('php artisan optimize')
        ->after(function ($result) {
            if ($result->successful()) {
                event(new CommandSucceeded($result));
            }
        });
    
  4. Parallel Commands Use Terminal\Parallel for concurrent executions (requires PHP 7.4+):

    use Terminal\Parallel;
    
    Parallel::run([
        Command::run('php artisan queue:work'),
        Command::run('tail -f storage/logs/laravel.log'),
    ]);
    
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
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
twbs/bootstrap4
php-http/client-implementation
phpcr/phpcr-implementation
cucumber/gherkin-monorepo
haydenpierce/class-finder
psr/simple-cache-implementation
uri-template/tests