Skip to content

Commit 1bbbcdc

Browse files
committed
feature #11346 [Console] ProgressBar developer experience (stefanosala, gido)
This PR was merged into the 2.6-dev branch. Discussion ---------- [Console] ProgressBar developer experience | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #11184 | License | MIT | Doc PR | WIP ## TODO - [x] Create `getProgress/setProgress` methods to replace `getStep/setCurrent` - [x] `ProgressBar::setCurrent` should auto-start the ProgressBar. - [x] You should be able to pass `max` to `start` - [x] `barCharOriginal` not needed. Logic can simply be part of `getBarChar` - [x] `getStepWidth` is internal information that should not be public - [x] when verbosity set to quiet, the progress bar does not even need to execute all the logic to generate output that is then thrown away - [x] Allow to advance past max. - [x] negative max needs to be validated - [x] `getProgressPercent` should return float instead of int. Commits ------- 42b95df [Console][ProgressBar] Developer experience: - Removed barCharOriginal - getProgressPercent should return float instead of int. - Minor refactoring 3011685 [Console][ProgressBar] Allow to advance past max. 73ca340 [Console][ProgressBar] Developer experience - Create getProgress/setProgress methods to replace getStep/setCurrent - ProgressBar::setCurrent should auto-start the ProgressBar. - You should be able to pass max to start - getStepWidth is internal information that should not be public - when verbosity set to quiet, the progress bar does not even need to execute all the logic to generate output that is then thrown away
2 parents 919345e + 4fc2c74 commit 1bbbcdc

File tree

2 files changed

+192
-72
lines changed

2 files changed

+192
-72
lines changed

Helper/ProgressBar.php

Lines changed: 77 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class ProgressBar
2424
{
2525
// options
2626
private $barWidth = 28;
27-
private $barChar = '=';
27+
private $barChar;
2828
private $emptyBarChar = '-';
2929
private $progressChar = '>';
3030
private $format = null;
@@ -34,13 +34,12 @@ class ProgressBar
3434
* @var OutputInterface
3535
*/
3636
private $output;
37-
private $step;
37+
private $step = 0;
3838
private $max;
3939
private $startTime;
4040
private $stepWidth;
41-
private $percent;
42-
private $lastMessagesLength;
43-
private $barCharOriginal;
41+
private $percent = 0.0;
42+
private $lastMessagesLength = 0;
4443
private $formatLineCount;
4544
private $messages;
4645

@@ -57,18 +56,11 @@ public function __construct(OutputInterface $output, $max = 0)
5756
{
5857
// Disabling output when it does not support ANSI codes as it would result in a broken display anyway.
5958
$this->output = $output->isDecorated() ? $output : new NullOutput();
60-
$this->max = (int) $max;
61-
$this->stepWidth = $this->max > 0 ? Helper::strlen($this->max) : 4;
62-
63-
if (!self::$formatters) {
64-
self::$formatters = self::initPlaceholderFormatters();
65-
}
66-
67-
if (!self::$formats) {
68-
self::$formats = self::initFormats();
69-
}
59+
$this->setMaxSteps($max);
7060

7161
$this->setFormat($this->determineBestFormat());
62+
63+
$this->startTime = time();
7264
}
7365

7466
/**
@@ -170,16 +162,30 @@ public function getMaxSteps()
170162
/**
171163
* Gets the progress bar step.
172164
*
165+
* @deprecated since 2.6, to be removed in 3.0. Use {@link getProgress()} instead.
166+
*
173167
* @return int The progress bar step
174168
*/
175169
public function getStep()
170+
{
171+
return $this->getProgress();
172+
}
173+
174+
/**
175+
* Gets the current step position.
176+
*
177+
* @return int The progress bar step
178+
*/
179+
public function getProgress()
176180
{
177181
return $this->step;
178182
}
179183

180184
/**
181185
* Gets the progress bar step width.
182186
*
187+
* @internal This method is public for PHP 5.3 compatibility, it should not be used.
188+
*
183189
* @return int The progress bar step width
184190
*/
185191
public function getStepWidth()
@@ -190,7 +196,7 @@ public function getStepWidth()
190196
/**
191197
* Gets the current progress bar percent.
192198
*
193-
* @return int The current progress bar percent
199+
* @return float The current progress bar percent
194200
*/
195201
public function getProgressPercent()
196202
{
@@ -234,6 +240,10 @@ public function setBarCharacter($char)
234240
*/
235241
public function getBarCharacter()
236242
{
243+
if (null === $this->barChar) {
244+
return $this->max ? '=' : $this->emptyBarChar;
245+
}
246+
237247
return $this->barChar;
238248
}
239249

@@ -285,10 +295,10 @@ public function getProgressCharacter()
285295
public function setFormat($format)
286296
{
287297
// try to use the _nomax variant if available
288-
if (!$this->max && isset(self::$formats[$format.'_nomax'])) {
289-
$this->format = self::$formats[$format.'_nomax'];
290-
} elseif (isset(self::$formats[$format])) {
291-
$this->format = self::$formats[$format];
298+
if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
299+
$this->format = self::getFormatDefinition($format.'_nomax');
300+
} elseif (null !== self::getFormatDefinition($format)) {
301+
$this->format = self::getFormatDefinition($format);
292302
} else {
293303
$this->format = $format;
294304
}
@@ -308,18 +318,17 @@ public function setRedrawFrequency($freq)
308318

309319
/**
310320
* Starts the progress output.
321+
*
322+
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
311323
*/
312-
public function start()
324+
public function start($max = null)
313325
{
314326
$this->startTime = time();
315327
$this->step = 0;
316-
$this->percent = 0;
317-
$this->lastMessagesLength = 0;
318-
$this->barCharOriginal = '';
328+
$this->percent = 0.0;
319329

320-
if (!$this->max) {
321-
$this->barCharOriginal = $this->barChar;
322-
$this->barChar = $this->emptyBarChar;
330+
if (null !== $max) {
331+
$this->setMaxSteps($max);
323332
}
324333

325334
$this->display();
@@ -334,35 +343,45 @@ public function start()
334343
*/
335344
public function advance($step = 1)
336345
{
337-
$this->setCurrent($this->step + $step);
346+
$this->setProgress($this->step + $step);
338347
}
339348

340349
/**
341350
* Sets the current progress.
342351
*
352+
* @deprecated since 2.6, to be removed in 3.0. Use {@link setProgress()} instead.
353+
*
343354
* @param int $step The current progress
344355
*
345356
* @throws \LogicException
346357
*/
347358
public function setCurrent($step)
348359
{
349-
if (null === $this->startTime) {
350-
throw new \LogicException('You must start the progress bar before calling setCurrent().');
351-
}
360+
$this->setProgress($step);
361+
}
352362

363+
/**
364+
* Sets the current progress.
365+
*
366+
* @param int $step The current progress
367+
*
368+
* @throws \LogicException
369+
*/
370+
public function setProgress($step)
371+
{
353372
$step = (int) $step;
354373
if ($step < $this->step) {
355374
throw new \LogicException('You can\'t regress the progress bar.');
356375
}
357376

358-
if ($this->max > 0 && $step > $this->max) {
359-
throw new \LogicException('You can\'t advance the progress bar past the max value.');
377+
if ($this->max && $step > $this->max) {
378+
$this->max = $step;
360379
}
361380

362381
$prevPeriod = intval($this->step / $this->redrawFreq);
363382
$currPeriod = intval($step / $this->redrawFreq);
364383
$this->step = $step;
365-
$this->percent = $this->max > 0 ? (float) $this->step / $this->max : 0;
384+
$this->percent = $this->max ? (float) $this->step / $this->max : 0;
366385
if ($prevPeriod !== $currPeriod || $this->max === $step) {
367386
$this->display();
368387
}
@@ -373,32 +392,20 @@ public function setCurrent($step)
373392
*/
374393
public function finish()
375394
{
376-
if (null === $this->startTime) {
377-
throw new \LogicException('You must start the progress bar before calling finish().');
378-
}
379-
380395
if (!$this->max) {
381-
$this->barChar = $this->barCharOriginal;
382396
$this->max = $this->step;
383-
$this->setCurrent($this->max);
384-
$this->max = 0;
385-
$this->barChar = $this->emptyBarChar;
386-
} else {
387-
$this->setCurrent($this->max);
388397
}
389398

390-
$this->startTime = null;
399+
$this->setProgress($this->max);
391400
}
392401

393402
/**
394403
* Outputs the current progress string.
395-
*
396-
* @throws \LogicException
397404
*/
398405
public function display()
399406
{
400-
if (null === $this->startTime) {
401-
throw new \LogicException('You must start the progress bar before calling display().');
407+
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
408+
return;
402409
}
403410

404411
// these 3 variables can be removed in favor of using $this in the closure when support for PHP 5.3 will be dropped.
@@ -434,6 +441,17 @@ public function clear()
434441
$this->overwrite(str_repeat("\n", $this->formatLineCount));
435442
}
436443

444+
/**
445+
* Sets the progress bar maximal steps.
446+
*
447+
* @param int The progress bar max steps
448+
*/
449+
private function setMaxSteps($max)
450+
{
451+
$this->max = max(0, (int) $max);
452+
$this->stepWidth = $this->max ? Helper::strlen($this->max) : 4;
453+
}
454+
437455
/**
438456
* Overwrites a previous message to the output.
439457
*
@@ -473,21 +491,21 @@ private function determineBestFormat()
473491
switch ($this->output->getVerbosity()) {
474492
// OutputInterface::VERBOSITY_QUIET: display is disabled anyway
475493
case OutputInterface::VERBOSITY_VERBOSE:
476-
return $this->max > 0 ? 'verbose' : 'verbose_nomax';
494+
return $this->max ? 'verbose' : 'verbose_nomax';
477495
case OutputInterface::VERBOSITY_VERY_VERBOSE:
478-
return $this->max > 0 ? 'very_verbose' : 'very_verbose_nomax';
496+
return $this->max ? 'very_verbose' : 'very_verbose_nomax';
479497
case OutputInterface::VERBOSITY_DEBUG:
480-
return $this->max > 0 ? 'debug' : 'debug_nomax';
498+
return $this->max ? 'debug' : 'debug_nomax';
481499
default:
482-
return $this->max > 0 ? 'normal' : 'normal_nomax';
500+
return $this->max ? 'normal' : 'normal_nomax';
483501
}
484502
}
485503

486504
private static function initPlaceholderFormatters()
487505
{
488506
return array(
489507
'bar' => function (ProgressBar $bar, OutputInterface $output) {
490-
$completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getStep() % $bar->getBarWidth());
508+
$completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth());
491509
$display = str_repeat($bar->getBarCharacter(), $completeBars);
492510
if ($completeBars < $bar->getBarWidth()) {
493511
$emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter());
@@ -504,10 +522,10 @@ private static function initPlaceholderFormatters()
504522
throw new \LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
505523
}
506524

507-
if (!$bar->getStep()) {
525+
if (!$bar->getProgress()) {
508526
$remaining = 0;
509527
} else {
510-
$remaining = round((time() - $bar->getStartTime()) / $bar->getStep() * ($bar->getMaxSteps() - $bar->getStep()));
528+
$remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress()));
511529
}
512530

513531
return Helper::formatTime($remaining);
@@ -517,10 +535,10 @@ private static function initPlaceholderFormatters()
517535
throw new \LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
518536
}
519537

520-
if (!$bar->getStep()) {
538+
if (!$bar->getProgress()) {
521539
$estimated = 0;
522540
} else {
523-
$estimated = round((time() - $bar->getStartTime()) / $bar->getStep() * $bar->getMaxSteps());
541+
$estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps());
524542
}
525543

526544
return Helper::formatTime($estimated);
@@ -529,7 +547,7 @@ private static function initPlaceholderFormatters()
529547
return Helper::formatMemory(memory_get_usage(true));
530548
},
531549
'current' => function (ProgressBar $bar) {
532-
return str_pad($bar->getStep(), $bar->getStepWidth(), ' ', STR_PAD_LEFT);
550+
return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT);
533551
},
534552
'max' => function (ProgressBar $bar) {
535553
return $bar->getMaxSteps();

0 commit comments

Comments
 (0)