diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index e1581b5d409a2..a7b3c0450f38d 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * Add `verbosity` argument to `mustRun` process helper method * [BC BREAK] Add silent verbosity (`--silent`/`SHELL_VERBOSITY=-2`) to suppress all output, including errors * Add `OutputInterface::isSilent()`, `Output::isSilent()`, `OutputStyle::isSilent()` methods + * Add a configurable finished indicator to the progress indicator to show that the progress is finished 7.1 --- diff --git a/src/Symfony/Component/Console/Helper/ProgressIndicator.php b/src/Symfony/Component/Console/Helper/ProgressIndicator.php index d06897d8543a2..b6bbd0cfa0986 100644 --- a/src/Symfony/Component/Console/Helper/ProgressIndicator.php +++ b/src/Symfony/Component/Console/Helper/ProgressIndicator.php @@ -36,8 +36,10 @@ class ProgressIndicator private ?string $message = null; private array $indicatorValues; private int $indicatorCurrent; + private string $finishedIndicatorValue; private float $indicatorUpdateTime; private bool $started = false; + private bool $finished = false; /** * @var array @@ -53,10 +55,12 @@ public function __construct( ?string $format = null, private int $indicatorChangeInterval = 100, ?array $indicatorValues = null, + ?string $finishedIndicatorValue = null, ) { $format ??= $this->determineBestFormat(); $indicatorValues ??= ['-', '\\', '|', '/']; $indicatorValues = array_values($indicatorValues); + $finishedIndicatorValue ??= '✔'; if (2 > \count($indicatorValues)) { throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); @@ -64,6 +68,7 @@ public function __construct( $this->format = self::getFormatDefinition($format); $this->indicatorValues = $indicatorValues; + $this->finishedIndicatorValue = $finishedIndicatorValue; $this->startTime = time(); } @@ -88,6 +93,7 @@ public function start(string $message): void $this->message = $message; $this->started = true; + $this->finished = false; $this->startTime = time(); $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; $this->indicatorCurrent = 0; @@ -122,13 +128,25 @@ public function advance(): void /** * Finish the indicator with message. + * + * @param ?string $finishedIndicator */ - public function finish(string $message): void + public function finish(string $message/* , ?string $finishedIndicator = null */): void { + $finishedIndicator = 1 < \func_num_args() ? func_get_arg(1) : null; + if (null !== $finishedIndicator && !\is_string($finishedIndicator)) { + throw new \TypeError(\sprintf('Argument 2 passed to "%s()" must be of the type string or null, "%s" given.', __METHOD__, get_debug_type($finishedIndicator))); + } + if (!$this->started) { throw new LogicException('Progress indicator has not yet been started.'); } + if (null !== $finishedIndicator) { + $this->finishedIndicatorValue = $finishedIndicator; + } + + $this->finished = true; $this->message = $message; $this->display(); $this->output->writeln(''); @@ -215,7 +233,7 @@ private function getCurrentTimeInMilliseconds(): float private static function initPlaceholderFormatters(): array { return [ - 'indicator' => fn (self $indicator) => $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)], + 'indicator' => fn (self $indicator) => $indicator->finished ? $indicator->finishedIndicatorValue : $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)], 'message' => fn (self $indicator) => $indicator->message, 'elapsed' => fn (self $indicator) => Helper::formatTime(time() - $indicator->startTime, 2), 'memory' => fn () => Helper::formatMemory(memory_get_usage(true)), diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php index 8e2e10e378ee7..2a4441d571cfc 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php @@ -54,11 +54,11 @@ public function testDefaultIndicator() $this->generateOutput(' \\ Starting...'). $this->generateOutput(' \\ Advancing...'). $this->generateOutput(' | Advancing...'). - $this->generateOutput(' | Done...'). + $this->generateOutput(' ✔ Done...'). \PHP_EOL. $this->generateOutput(' - Starting Again...'). $this->generateOutput(' \\ Starting Again...'). - $this->generateOutput(' \\ Done Again...'). + $this->generateOutput(' ✔ Done Again...'). \PHP_EOL, stream_get_contents($output->getStream()) ); @@ -109,6 +109,39 @@ public function testCustomIndicatorValues() ); } + public function testCustomFinishedIndicatorValue() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, ['a', 'b'], '✅'); + + $bar->start('Starting...'); + usleep(101000); + $bar->finish('Done'); + + rewind($output->getStream()); + + $this->assertSame( + $this->generateOutput(' a Starting...'). + $this->generateOutput(' ✅ Done').\PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testCustomFinishedIndicatorWhenFinishingProcess() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, ['a', 'b']); + + $bar->start('Starting...'); + $bar->finish('Process failed', '❌'); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' a Starting...'). + $this->generateOutput(' ❌ Process failed').\PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + public function testCannotSetInvalidIndicatorCharacters() { $this->expectException(\InvalidArgumentException::class);