最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

php - Stream Artisan command output to the browser using Livewire - Stack Overflow

programmeradmin1浏览0评论

I have a page where users are allowed to execute Artisan commands using a UI. My problem is getting the output of long-running commands to stream to the browser and not just show it after the command is finished.

Here is my current implementation (code simplified for brevity)

    public string $output = '';

    public function runCommand(): void
    {
        $outputBuffer = new BufferedConsoleOutput();

        Artisan::call('list', outputBuffer: $outputBuffer);

        $this->output = $outputBuffer->fetch();
    }

The problem with this is that the output is only displayed when the command is done and the page/UI is unresponsive until then.

Here is what I tried to do using Livewire's stream functionality


use Symfony\Component\Console\Output\StreamOutput;

public function streamTest()
{
    $stream = fopen('php://output', 'w');

    $outputBuffer = new StreamOutput($stream);

    Artisan::call('list', outputBuffer: $outputBuffer);

    $this->stream(
        to: 'output',
        content: $this->output,
        replace: true,
    );

    while ($content = stream_get_contents($outputBuffer->getStream())) {
        $this->output .= $content;
    }
}

The above doesn't work and I don't know why. I have very limited experience with using streams so there are probably some glaring mistakes in that example, but I couldn't find anything similar online.

FWIW, this is the Blade template for the page

<div>
  <button wire:click="streamTest">Stream</button>
  
  Output: <pre wire:stream="output">{{ $output }}</pre>
</div>

I have a page where users are allowed to execute Artisan commands using a UI. My problem is getting the output of long-running commands to stream to the browser and not just show it after the command is finished.

Here is my current implementation (code simplified for brevity)

    public string $output = '';

    public function runCommand(): void
    {
        $outputBuffer = new BufferedConsoleOutput();

        Artisan::call('list', outputBuffer: $outputBuffer);

        $this->output = $outputBuffer->fetch();
    }

The problem with this is that the output is only displayed when the command is done and the page/UI is unresponsive until then.

Here is what I tried to do using Livewire's stream functionality


use Symfony\Component\Console\Output\StreamOutput;

public function streamTest()
{
    $stream = fopen('php://output', 'w');

    $outputBuffer = new StreamOutput($stream);

    Artisan::call('list', outputBuffer: $outputBuffer);

    $this->stream(
        to: 'output',
        content: $this->output,
        replace: true,
    );

    while ($content = stream_get_contents($outputBuffer->getStream())) {
        $this->output .= $content;
    }
}

The above doesn't work and I don't know why. I have very limited experience with using streams so there are probably some glaring mistakes in that example, but I couldn't find anything similar online.

FWIW, this is the Blade template for the page

<div>
  <button wire:click="streamTest">Stream</button>
  
  Output: <pre wire:stream="output">{{ $output }}</pre>
</div>
Share Improve this question asked Jan 30 at 23:01 SPRTKSPRTK 475 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

I think you can't stream the output in real-time from a single Livewire call because Artisan::call is blocking and only returns its output when it's fully done. A simple workaround is to run the Artisan command in the background(for example, via a queued job) and write the output to storage (like a file or the database). Then, have your Livewire component poll or listen (e.g. using Laravel Echo or SSE) for new output and display it as it arrives. I don't know what the overall implementation idea is in your project but if you just want a rough idea of how this might look, you could have a job like this:

public function handle()
{
    $process = new Process(['php', 'artisan', 'list']);
    $process->setTimeout(0);
    $process->start();

    foreach ($process as $type => $data) {
        // Append $data to a file or a database record
    }
}

Then in your Livewire component, poll or subscribe to updates, read new output, and append it to the UI. This way you're not blocking the main request and you'll see the output as it's generated.

发布评论

评论列表(0)

  1. 暂无评论