[Console][ProgressBar] Developer experience:

- Removed barCharOriginal
 - getProgressPercent should return float instead of int.
 - Minor refactoring
This commit is contained in:
Stefano Sala 2014-07-07 17:54:11 +02:00
parent 30116859e6
commit 42b95dfd2b
2 changed files with 109 additions and 97 deletions

View File

@ -24,7 +24,7 @@ class ProgressBar
{ {
// options // options
private $barWidth = 28; private $barWidth = 28;
private $barChar = '='; private $barChar;
private $emptyBarChar = '-'; private $emptyBarChar = '-';
private $progressChar = '>'; private $progressChar = '>';
private $format = null; private $format = null;
@ -34,13 +34,12 @@ class ProgressBar
* @var OutputInterface * @var OutputInterface
*/ */
private $output; private $output;
private $step; private $step = 0;
private $max; private $max;
private $startTime; private $startTime;
private $stepWidth; private $stepWidth;
private $percent; private $percent = 0.0;
private $lastMessagesLength; private $lastMessagesLength = 0;
private $barCharOriginal;
private $formatLineCount; private $formatLineCount;
private $messages; private $messages;
@ -52,8 +51,6 @@ class ProgressBar
* *
* @param OutputInterface $output An OutputInterface instance * @param OutputInterface $output An OutputInterface instance
* @param int $max Maximum steps (0 if unknown) * @param int $max Maximum steps (0 if unknown)
*
* @throws \InvalidArgumentException
*/ */
public function __construct(OutputInterface $output, $max = 0) public function __construct(OutputInterface $output, $max = 0)
{ {
@ -61,21 +58,9 @@ class ProgressBar
$this->output = $output->isDecorated() ? $output : new NullOutput(); $this->output = $output->isDecorated() ? $output : new NullOutput();
$this->setMaxSteps($max); $this->setMaxSteps($max);
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
if (!self::$formats) {
self::$formats = self::initFormats();
}
$this->setFormat($this->determineBestFormat()); $this->setFormat($this->determineBestFormat());
$this->startTime = time(); $this->startTime = time();
$this->step = 0;
$this->percent = 0;
$this->lastMessagesLength = 0;
$this->barCharOriginal = '';
} }
/** /**
@ -164,25 +149,6 @@ class ProgressBar
return $this->startTime; return $this->startTime;
} }
/**
* Sets the progress bar maximal steps.
*
* @param int The progress bar max steps
*
* @throws \InvalidArgumentException
*/
public function setMaxSteps($max)
{
$max = (int) $max;
if ($max < 0) {
throw new \InvalidArgumentException('Max steps should be a positive integer, 0 or null. Got "%s".', $max);
}
$this->max = $max;
$this->stepWidth = $this->max > 0 ? Helper::strlen($this->max) : 4;
}
/** /**
* Gets the progress bar maximal steps. * Gets the progress bar maximal steps.
* *
@ -196,21 +162,21 @@ class ProgressBar
/** /**
* Gets the progress bar step. * Gets the progress bar step.
* *
* @deprecated since 2.6, to be removed in 3.0. Use {@link getCurrent()} instead. * @deprecated since 2.6, to be removed in 3.0. Use {@link getProgress()} instead.
* *
* @return int The progress bar step * @return int The progress bar step
*/ */
public function getStep() public function getStep()
{ {
return $this->getCurrent(); return $this->getProgress();
} }
/** /**
* Gets the progress bar step. * Gets the current step position.
* *
* @return int The progress bar step * @return int The progress bar step
*/ */
public function getCurrent() public function getProgress()
{ {
return $this->step; return $this->step;
} }
@ -218,7 +184,7 @@ class ProgressBar
/** /**
* Gets the progress bar step width. * Gets the progress bar step width.
* *
* @deprecated since 2.6, it will be marked private from 3.0. * @internal This method is public for PHP 5.3 compatibility, it should not be used.
* *
* @return int The progress bar step width * @return int The progress bar step width
*/ */
@ -230,7 +196,7 @@ class ProgressBar
/** /**
* Gets the current progress bar percent. * Gets the current progress bar percent.
* *
* @return int The current progress bar percent * @return float The current progress bar percent
*/ */
public function getProgressPercent() public function getProgressPercent()
{ {
@ -274,6 +240,10 @@ class ProgressBar
*/ */
public function getBarCharacter() public function getBarCharacter()
{ {
if (null === $this->barChar) {
return $this->max ? '=' : $this->emptyBarChar;
}
return $this->barChar; return $this->barChar;
} }
@ -325,10 +295,10 @@ class ProgressBar
public function setFormat($format) public function setFormat($format)
{ {
// try to use the _nomax variant if available // try to use the _nomax variant if available
if (!$this->max && isset(self::$formats[$format.'_nomax'])) { if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
$this->format = self::$formats[$format.'_nomax']; $this->format = self::getFormatDefinition($format.'_nomax');
} elseif (isset(self::$formats[$format])) { } elseif (null !== self::getFormatDefinition($format)) {
$this->format = self::$formats[$format]; $this->format = self::getFormatDefinition($format);
} else { } else {
$this->format = $format; $this->format = $format;
} }
@ -349,17 +319,16 @@ class ProgressBar
/** /**
* Starts the progress output. * Starts the progress output.
* *
* @param int $max Maximum Step (0 if unknown) * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
*/ */
public function start($max = 0) public function start($max = null)
{ {
if (0 !== $max) { $this->startTime = time();
$this->setMaxSteps($max); $this->step = 0;
} $this->percent = 0.0;
if (!$this->max) { if (null !== $max) {
$this->barCharOriginal = $this->barChar; $this->setMaxSteps($max);
$this->barChar = $this->emptyBarChar;
} }
$this->display(); $this->display();
@ -374,7 +343,21 @@ class ProgressBar
*/ */
public function advance($step = 1) public function advance($step = 1)
{ {
$this->setCurrent($this->step + $step); $this->setProgress($this->step + $step);
}
/**
* Sets the current progress.
*
* @deprecated since 2.6, to be removed in 3.0. Use {@link setProgress()} instead.
*
* @param int $step The current progress
*
* @throws \LogicException
*/
public function setCurrent($step)
{
$this->setProgress($step);
} }
/** /**
@ -384,21 +367,21 @@ class ProgressBar
* *
* @throws \LogicException * @throws \LogicException
*/ */
public function setCurrent($step) public function setProgress($step)
{ {
$step = (int) $step; $step = (int) $step;
if ($step < $this->step) { if ($step < $this->step) {
throw new \LogicException('You can\'t regress the progress bar.'); throw new \LogicException('You can\'t regress the progress bar.');
} }
if ($this->max > 0 && $step > $this->max) { if ($this->max && $step > $this->max) {
$this->max = $step; $this->max = $step;
} }
$prevPeriod = intval($this->step / $this->redrawFreq); $prevPeriod = intval($this->step / $this->redrawFreq);
$currPeriod = intval($step / $this->redrawFreq); $currPeriod = intval($step / $this->redrawFreq);
$this->step = $step; $this->step = $step;
$this->percent = $this->max > 0 ? (float) $this->step / $this->max : 0; $this->percent = $this->max ? (float) $this->step / $this->max : 0;
if ($prevPeriod !== $currPeriod || $this->max === $step) { if ($prevPeriod !== $currPeriod || $this->max === $step) {
$this->display(); $this->display();
} }
@ -410,22 +393,14 @@ class ProgressBar
public function finish() public function finish()
{ {
if (!$this->max) { if (!$this->max) {
$this->barChar = $this->barCharOriginal;
$this->max = $this->step; $this->max = $this->step;
$this->setCurrent($this->max);
$this->max = 0;
$this->barChar = $this->emptyBarChar;
} else {
$this->setCurrent($this->max);
} }
$this->startTime = null; $this->setProgress($this->max);
} }
/** /**
* Outputs the current progress string. * Outputs the current progress string.
*
* @throws \LogicException
*/ */
public function display() public function display()
{ {
@ -466,6 +441,17 @@ class ProgressBar
$this->overwrite(str_repeat("\n", $this->formatLineCount)); $this->overwrite(str_repeat("\n", $this->formatLineCount));
} }
/**
* Sets the progress bar maximal steps.
*
* @param int The progress bar max steps
*/
private function setMaxSteps($max)
{
$this->max = max(0, (int) $max);
$this->stepWidth = $this->max ? Helper::strlen($this->max) : 4;
}
/** /**
* Overwrites a previous message to the output. * Overwrites a previous message to the output.
* *
@ -505,13 +491,13 @@ class ProgressBar
switch ($this->output->getVerbosity()) { switch ($this->output->getVerbosity()) {
// OutputInterface::VERBOSITY_QUIET: display is disabled anyway // OutputInterface::VERBOSITY_QUIET: display is disabled anyway
case OutputInterface::VERBOSITY_VERBOSE: case OutputInterface::VERBOSITY_VERBOSE:
return $this->max > 0 ? 'verbose' : 'verbose_nomax'; return $this->max ? 'verbose' : 'verbose_nomax';
case OutputInterface::VERBOSITY_VERY_VERBOSE: case OutputInterface::VERBOSITY_VERY_VERBOSE:
return $this->max > 0 ? 'very_verbose' : 'very_verbose_nomax'; return $this->max ? 'very_verbose' : 'very_verbose_nomax';
case OutputInterface::VERBOSITY_DEBUG: case OutputInterface::VERBOSITY_DEBUG:
return $this->max > 0 ? 'debug' : 'debug_nomax'; return $this->max ? 'debug' : 'debug_nomax';
default: default:
return $this->max > 0 ? 'normal' : 'normal_nomax'; return $this->max ? 'normal' : 'normal_nomax';
} }
} }
@ -519,7 +505,7 @@ class ProgressBar
{ {
return array( return array(
'bar' => function (ProgressBar $bar, OutputInterface $output) { 'bar' => function (ProgressBar $bar, OutputInterface $output) {
$completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getStep() % $bar->getBarWidth()); $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth());
$display = str_repeat($bar->getBarCharacter(), $completeBars); $display = str_repeat($bar->getBarCharacter(), $completeBars);
if ($completeBars < $bar->getBarWidth()) { if ($completeBars < $bar->getBarWidth()) {
$emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter());
@ -536,10 +522,10 @@ class ProgressBar
throw new \LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); throw new \LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
} }
if (!$bar->getStep()) { if (!$bar->getProgress()) {
$remaining = 0; $remaining = 0;
} else { } else {
$remaining = round((time() - $bar->getStartTime()) / $bar->getStep() * ($bar->getMaxSteps() - $bar->getStep())); $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress()));
} }
return Helper::formatTime($remaining); return Helper::formatTime($remaining);
@ -549,10 +535,10 @@ class ProgressBar
throw new \LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); throw new \LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
} }
if (!$bar->getStep()) { if (!$bar->getProgress()) {
$estimated = 0; $estimated = 0;
} else { } else {
$estimated = round((time() - $bar->getStartTime()) / $bar->getStep() * $bar->getMaxSteps()); $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps());
} }
return Helper::formatTime($estimated); return Helper::formatTime($estimated);
@ -561,7 +547,7 @@ class ProgressBar
return Helper::formatMemory(memory_get_usage(true)); return Helper::formatMemory(memory_get_usage(true));
}, },
'current' => function (ProgressBar $bar) { 'current' => function (ProgressBar $bar) {
return str_pad($bar->getStep(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT);
}, },
'max' => function (ProgressBar $bar) { 'max' => function (ProgressBar $bar) {
return $bar->getMaxSteps(); return $bar->getMaxSteps();

View File

@ -17,14 +17,20 @@ use Symfony\Component\Console\Output\StreamOutput;
class ProgressBarTest extends \PHPUnit_Framework_TestCase class ProgressBarTest extends \PHPUnit_Framework_TestCase
{ {
protected $lastMessagesLength; public function testMultipleStart()
/**
* @expectedException InvalidArgumentException
*/
public function testInitializeWithNegativeMax()
{ {
$bar = new ProgressBar($output = $this->getOutputStream(), -1); $bar = new ProgressBar($output = $this->getOutputStream());
$bar->start();
$bar->advance();
$bar->start();
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0 [>---------------------------]').
$this->generateOutput(' 1 [->--------------------------]').
$this->generateOutput(' 0 [>---------------------------]'),
stream_get_contents($output->getStream())
);
} }
public function testAdvance() public function testAdvance()
@ -74,7 +80,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
public function testAdvanceOverMax() public function testAdvanceOverMax()
{ {
$bar = new ProgressBar($output = $this->getOutputStream(), 10); $bar = new ProgressBar($output = $this->getOutputStream(), 10);
$bar->setCurrent(9); $bar->setProgress(9);
$bar->advance(); $bar->advance();
$bar->advance(); $bar->advance();
@ -187,12 +193,10 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
$bar = new ProgressBar($output = $this->getOutputStream()); $bar = new ProgressBar($output = $this->getOutputStream());
$bar->setFormat('%current%/%max% [%bar%]'); $bar->setFormat('%current%/%max% [%bar%]');
$bar->start(50); $bar->start(50);
$bar->display();
$bar->advance(); $bar->advance();
rewind($output->getStream()); rewind($output->getStream());
$this->assertEquals( $this->assertEquals(
$this->generateOutput(' 0/50 [>---------------------------]').
$this->generateOutput(' 0/50 [>---------------------------]'). $this->generateOutput(' 0/50 [>---------------------------]').
$this->generateOutput(' 1/50 [>---------------------------]'), $this->generateOutput(' 1/50 [>---------------------------]'),
stream_get_contents($output->getStream()) stream_get_contents($output->getStream())
@ -205,8 +209,8 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
$bar->start(); $bar->start();
$bar->display(); $bar->display();
$bar->advance(); $bar->advance();
$bar->setCurrent(15); $bar->setProgress(15);
$bar->setCurrent(25); $bar->setProgress(25);
rewind($output->getStream()); rewind($output->getStream());
$this->assertEquals( $this->assertEquals(
@ -224,7 +228,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
public function testSetCurrentBeforeStarting() public function testSetCurrentBeforeStarting()
{ {
$bar = new ProgressBar($this->getOutputStream()); $bar = new ProgressBar($this->getOutputStream());
$bar->setCurrent(15); $bar->setProgress(15);
$this->assertNotNull($bar->getStartTime()); $this->assertNotNull($bar->getStartTime());
} }
@ -236,8 +240,8 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
{ {
$bar = new ProgressBar($output = $this->getOutputStream(), 50); $bar = new ProgressBar($output = $this->getOutputStream(), 50);
$bar->start(); $bar->start();
$bar->setCurrent(15); $bar->setProgress(15);
$bar->setCurrent(10); $bar->setProgress(10);
} }
public function testRedrawFrequency() public function testRedrawFrequency()
@ -247,7 +251,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
$bar->setRedrawFrequency(2); $bar->setRedrawFrequency(2);
$bar->start(); $bar->start();
$bar->setCurrent(1); $bar->setProgress(1);
$bar->advance(2); $bar->advance(2);
$bar->advance(2); $bar->advance(2);
$bar->advance(1); $bar->advance(1);
@ -276,7 +280,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
{ {
$bar = new ProgressBar($output = $this->getOutputStream(), 50); $bar = new ProgressBar($output = $this->getOutputStream(), 50);
$bar->start(); $bar->start();
$bar->setCurrent(25); $bar->setProgress(25);
$bar->clear(); $bar->clear();
rewind($output->getStream()); rewind($output->getStream());
@ -375,10 +379,32 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
); );
} }
public function testWithoutMax()
{
$output = $this->getOutputStream();
$bar = new ProgressBar($output);
$bar->start();
$bar->advance();
$bar->advance();
$bar->advance();
$bar->finish();
rewind($output->getStream());
$this->assertEquals(
rtrim($this->generateOutput(' 0 [>---------------------------]')).
rtrim($this->generateOutput(' 1 [->--------------------------]')).
rtrim($this->generateOutput(' 2 [-->-------------------------]')).
rtrim($this->generateOutput(' 3 [--->------------------------]')).
rtrim($this->generateOutput(' 3 [============================]')),
stream_get_contents($output->getStream())
);
}
public function testAddingPlaceholderFormatter() public function testAddingPlaceholderFormatter()
{ {
ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) { ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) {
return $bar->getMaxSteps() - $bar->getCurrent(); return $bar->getMaxSteps() - $bar->getProgress();
}); });
$bar = new ProgressBar($output = $this->getOutputStream(), 3); $bar = new ProgressBar($output = $this->getOutputStream(), 3);
$bar->setFormat(' %remaining_steps% [%bar%]'); $bar->setFormat(' %remaining_steps% [%bar%]');