Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Console] Issue in multiple ProgressIndicator #62306

Open
@LucianMihalache

Description

@LucianMihalache

Symfony version(s) affected

At least all versions of in the past 3 years based on git blame

Description

This is going to be a bit of a long one, as it contains the explanation and a possible fix.

I've been playing around with a console script.
I am triggering subprocesses to run them in parallel. I wanted to make the console look nice, for no particular reason, to show a loading like a throbber as the subprocesses take some time.

So I stumbled uponSymfony\Component\Console\Helper\ProgressIndicator.
Great! Or so I thought... All nice and good until, for some reason that I could not manage to debug, after the exact 5th redraw of the indicator, it starts eating up the previous text (the lines above the section).
Each iteration, the indicator gets pushed up in the terminal.

After hours of debugging to understand if it's a me-issue, I managed to track it down to this function insideProgressIndicator class:

privatefunctionoverwrite(string$message):void    {if ($this->output->isDecorated()) {$this->output->write("\x0D\x1B[2K");$this->output->write($message);        }else {$this->output->writeln($message);        }    }

$this->output is an instance of theConsoleSectionOutput class.
write function is all the way inSymfony\Component\Console\Output\Output.
Internally, it callsdoWrite.
doWrite is overwritten inConsoleSectionOutput.

ThedoWrite are doing some newline stuff. Which is fine I guess.
But at the same time, theProgressIndicator's internaloverwrite does->write("\x0D\x1B[2K").
That thing over there tells the terminal to move the cursor at the end of the line, and then to delete everything on that line, or so I understand.

There seems to be a weird conflict between this and thedoWrite's newLines, which results in this weird behavior of the sections moving up in terminal if you have multiple of them.

How to reproduce

Here is a small script that takes an array of Processes (Symfony\Component\Process\Process), starts them, and shows a throbber while waiting for the processes to finish.

protectedfunctionrunAndWatchProcesses(array$processes):void{/** @var ConsoleOutputInterface $console */$console =$this->output->getOutput();$sections = [];$indicators  = [];foreach ($processesas$pid =>$p) {if (!$p->isRunning()) {$p->start();        }$section =$console->section();$sections[$pid] =$section;$indicator =newProgressIndicator(            output:$section,            indicatorValues: ['','','','','','','','','','']        );$indicator->start('⚙️ Starting async process...');$indicators[$pid] =$indicator;    }while (!empty($processes)) {foreach ($processesas$pid =>$p) {$lastLineOfText =$this->lastNonEmptyLine($p->getIncrementalErrorOutput() ?:$p->getIncrementalOutput());if ($lastLineOfText) {$indicators[$pid]->setMessage($lastLineOfText);            }$indicators[$pid]->advance();if (!$p->isRunning()) {$indicators[$pid]->finish($lastLineOfText,$p->isSuccessful() ?'' :'');// Because finish does this weird and it always shows Ok even if the process failed                unset($processes[$pid],$indicators[$pid],$sections[$pid]);            }        }usleep(100_000);    }}

Example of the processes array:
It will require adaptations, of course...

$projects = [1,2,3,4,5];$processes = [];foreach ($projectsas$projectId) {$process =newProcess([PHP_BINARY,'artisan','time-consuming: command',"--project=$projectId",    ]);$process->setTimeout(900);$processes[$projectId] =$process;}

Possible Solution

The fix is to just use the "native" overwrite of the output (ConsoleSectionOutput) without using the\x0D\x1B[2K trick. For some reason that fixed it.

privatefunctionoverwrite(string$message):void    {if ($this->output->isDecorated()) {//$this->output->write("\x0D\x1B[2K");$this->output->overwrite($message);        }else {$this->output->writeln($message);        }    }

Additional Context

Gifs to explain:

Image

Image

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp