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

Stdio React Laravel Package

clue/stdio-react

ReactPHP-based STDIO stream wrapper for non-blocking access to STDIN/STDOUT/STDERR. Enables event-driven CLI apps with readable and writable streams, integrating terminal input/output into the ReactPHP loop for async command-line tools.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require clue/stdio-react
    

    Add to composer.json if using Laravel's autoloader:

    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Clue\\React\\": "vendor/clue/reactphp-stdio/src/"
        }
    }
    
  2. First Use Case: Create a simple interactive CLI command in Laravel:

    // app/Console/Commands/InteractiveCommand.php
    namespace App\Console\Commands;
    
    use Illuminate\Console\Command;
    use Clue\React\Stdio\Stdio;
    
    class InteractiveCommand extends Command
    {
        protected $signature = 'interactive:demo';
        protected $description = 'Demonstrate async CLI interaction';
    
        public function handle()
        {
            $loop = \React\EventLoop\Factory::create();
            $stdio = new Stdio($loop);
    
            $stdio->setPrompt('> ');
            $stdio->on('data', function ($line) use ($stdio, $loop) {
                $line = rtrim($line, "\r\n");
                $this->info("You entered: $line");
    
                if ($line === 'exit') {
                    $stdio->end();
                    $loop->stop();
                }
            });
    
            $loop->run();
        }
    }
    
  3. Where to Look First:


Implementation Patterns

Core Workflows

1. Event-Driven Input Handling

$stdio->on('data', function ($line) {
    // Process input line-by-line
    $this->handleInput(rtrim($line, "\r\n"));
});

2. Interleaved I/O (Output While Waiting for Input)

$stdio->write("Processing... ");
$stdio->on('data', function ($line) use ($stdio) {
    $stdio->write("Received: " . rtrim($line, "\r\n") . PHP_EOL);
});

3. Password Input (Hidden Echo)

$stdio->setPrompt('Password: ');
$stdio->setEcho('*'); // Mask input with asterisks

4. Command History Integration

$stdio->on('data', function ($line) use ($stdio) {
    $line = rtrim($line);
    $history = $stdio->listHistory();
    if (!empty($line) && end($history) !== $line) {
        $stdio->addHistory($line);
    }
});

5. Autocomplete Integration

$stdio->setAutocomplete(function ($input) {
    return $this->suggestCommands($input);
});

Laravel-Specific Patterns

1. Service Provider Integration

// app/Providers/StdioServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Clue\React\Stdio\Stdio;
use React\EventLoop\Factory;

class StdioServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(Stdio::class, function ($app) {
            $loop = Factory::create();
            return new Stdio($loop);
        });
    }
}

2. Command Bus Integration

// app/Console/Kernel.php
protected $commands = [
    \App\Console\Commands\InteractiveCommand::class,
    // Other commands...
];

public function handle()
{
    $this->call('interactive:demo');
}

3. Artisan Event Listeners

// app/Providers/EventServiceProvider.php
public function boot()
{
    Artisan::starting(function () {
        $loop = \React\EventLoop\Factory::create();
        $stdio = new Stdio($loop);
        // Attach to Artisan's output
    });
}

Advanced Patterns

1. Custom Key Bindings

$stdio->on('key', function ($key) {
    if ($key === 'ctrl+c') {
        $this->info("Interrupt received");
    }
});

2. Multi-Line Input

$stdio->setPrompt('> ');
$stdio->on('data', function ($line) use ($stdio) {
    if (substr($line, -2) === '\\n') {
        $stdio->write("... ");
        return;
    }
    // Process complete input
});

3. TTY Detection

if ($stdio->isTTY()) {
    $stdio->setPrompt('TTY Mode: ');
} else {
    $stdio->setPrompt('Non-TTY Mode: ');
}

4. Progress Bars

$stdio->write("Processing [");
$stdio->on('progress', function ($percent) use ($stdio) {
    $stdio->write(str_repeat('#', $percent));
});
$stdio->write("] Done!" . PHP_EOL);

Gotchas and Tips

Common Pitfalls

  1. Blocking Calls:

    • Avoid synchronous I/O in event handlers. Use async alternatives (e.g., ReactPHP HTTP clients).
    • ❌ Bad: file_get_contents() in data handler.
    • ✅ Good: Use React\Dns\Resolver or React\Http\Client.
  2. Cursor Position Quirks:

    • getCursorPosition() returns character count, not screen columns.
    • For UTF-8, use getCursorCell() for visual alignment.
    • Example: A Chinese character may occupy 2 cells but 1 position.
  3. History Management:

    • History is not auto-populated. Manually call addHistory().
    • Empty history disables UP/DOWN keys. Initialize with addHistory('').
  4. Echo Mode Edge Cases:

    • setEcho(false) hides all input, including cursor movement.
    • Use setEcho('*') for passwords to show feedback.
  5. TTY Assumptions:

    • Methods like isTTY() may return false in pipes or non-interactive shells.
    • Test with php artisan command | head to catch issues.

Debugging Tips

  1. Log Raw Input:

    $stdio->on('data', function ($line) {
        $this->debug("Raw input: " . json_encode($line));
    });
    
  2. Visualize Cursor:

    $stdio->on('key', function ($key) {
        $this->info("Key: $key, Cursor: {$stdio->getCursorPosition()}");
    });
    
  3. Check TTY State:

    $this->info("Is TTY: " . ($stdio->isTTY() ? 'Yes' : 'No'));
    
  4. Handle Interrupts Gracefully:

    $stdio->on('key', function ($key) {
        if ($key === 'ctrl+c') {
            $this->error("Aborted!");
            $stdio->end();
            $loop->stop();
        }
    });
    

Configuration Quirks

  1. Environment Variables:

    • Respect HISTSIZE for history limits:
      $limit = (int)getenv('HISTSIZE') ?: 500;
      $stdio->limitHistory($limit);
      
  2. Prompt Formatting:

    • Use \r for carriage returns (overwrites prompt):
      $stdio->write("\rProcessing... ");
      
  3. Autocomplete Delays:

    • Add a small delay to avoid flickering:
      $stdio->setAutocomplete(function ($input) {
          $loop->futureTick(function () use ($input) {
              return $this->suggestCommands($input);
          });
      });
      

Extension Points

  1. Custom Prompt Rendering:

    $stdio->setPromptRenderer(function ($prompt) {
        return "\033[32m" . $prompt . "\033[0m"; // Green prompt
    });
    
  2. Input Validation:

    $stdio->on('data', function ($line) use ($stdio) {
        if (!$this->validateInput(rtrim($line, "\r\n"))) {
            $stdio->write("Invalid input. Try again.\n");
            $stdio->setInput(''); // Clear buffer
        }
    });
    
  3. Plugin System:

    // Extend Stdio with traits or decorators
    trait StdioLogger {
        public function logInput($line) {
            $
    
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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata