diff --git a/src/Symfony/Component/Console/Helper/ProgressBar.php b/src/Symfony/Component/Console/Helper/ProgressBar.php index 4c883ac04bc9d..f6b3323d59f4d 100644 --- a/src/Symfony/Component/Console/Helper/ProgressBar.php +++ b/src/Symfony/Component/Console/Helper/ProgressBar.php @@ -50,7 +50,8 @@ final class ProgressBar private OutputInterface $output; private int $step = 0; private int $startingStep = 0; - private ?int $max = null; + private int $max = 0; + private bool $isMaxKnown = false; private int $startTime; private int $stepWidth; private float $percent = 0.0; @@ -195,7 +196,7 @@ public function getProgressPercent(): float public function getBarOffset(): float { - return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth); + return floor($this->isMaxKnown || $this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth); } public function getEstimated(): float @@ -209,7 +210,7 @@ public function getEstimated(): float public function getRemaining(): float { - if (!$this->step) { + if (!$this->step || $this->step === $this->startingStep) { return 0; } @@ -233,7 +234,7 @@ public function setBarCharacter(string $char) public function getBarCharacter(): string { - return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar); + return $this->barChar ?? ($this->isMaxKnown || $this->max ? '=' : $this->emptyBarChar); } public function setEmptyBarCharacter(string $char) @@ -289,6 +290,8 @@ public function maxSecondsBetweenRedraws(float $seconds): void */ public function iterate(iterable $iterable, int $max = null): iterable { + $this->isMaxKnown = is_countable($iterable); + $this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0)); foreach ($iterable as $key => $value) { @@ -308,6 +311,8 @@ public function iterate(iterable $iterable, int $max = null): iterable */ public function start(int $max = null, int $startAt = 0): void { + $this->isMaxKnown = !is_null($max); + $this->startTime = time(); $this->step = $startAt; $this->startingStep = $startAt; @@ -352,6 +357,8 @@ public function setProgress(int $step) $currPeriod = (int) ($step / $redrawFreq); $this->step = $step; $this->percent = $this->max ? (float) $this->step / $this->max : 0; + if ($this->isMaxKnown && 0 == $this->max) + $this->percent = 1.0; $timeInterval = microtime(true) - $this->lastWriteTime; // Draw regardless of other limits @@ -377,6 +384,7 @@ public function setMaxSteps(int $max) $this->format = null; $this->max = max(0, $max); $this->stepWidth = $this->max ? Helper::width((string) $this->max) : 4; + $this->isMaxKnown = $this->isMaxKnown || 0 < $this->max; } /** @@ -519,14 +527,14 @@ private static function initPlaceholderFormatters(): array return Helper::formatTime(time() - $bar->getStartTime()); }, 'remaining' => function (self $bar) { - if (!$bar->getMaxSteps()) { + if (!$bar->isMaxKnown && !$bar->getMaxSteps()) { throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); } return Helper::formatTime($bar->getRemaining()); }, 'estimated' => function (self $bar) { - if (!$bar->getMaxSteps()) { + if (!$bar->isMaxKnown && !$bar->getMaxSteps()) { throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); } diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php index f9a74ae761879..dea640ffd22a8 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Console\Tests\Helper; use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Exception\Exception; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Helper\Helper; use Symfony\Component\Console\Helper\ProgressBar; @@ -1037,6 +1038,29 @@ public function testIterateUncountable() ); } + public function testEmptyInputWithDebugFormat() + { + try { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('%current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%'); + + $input = []; + foreach ($bar->iterate($input) as $item) { + var_dump($item); + } + + // succeed the test if we reach this code (no exception happened) + rewind($output->getStream()); + $this->assertEquals( + ' 0/0 [>---------------------------] 0% < 1 sec/< 1 sec' . + $this->generateOutput(' 0/0 [============================] 100% < 1 sec/< 1 sec'), + stream_get_contents($output->getStream()) + ); + } catch (Exception $e) { + $this->fail(); + } + } + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) { return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);