Merge branch 'progress-helper'
* progress-helper: [Console] added some basic tests for the ProgressHelper class [Console] converted options to proper setters in ProgressHelper [2.2][Console] Add ProgressHelper
This commit is contained in:
commit
649fd5b3d2
@ -27,6 +27,7 @@ use Symfony\Component\Console\Command\ListCommand;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\FormatterHelper;
|
||||
use Symfony\Component\Console\Helper\DialogHelper;
|
||||
use Symfony\Component\Console\Helper\ProgressHelper;
|
||||
|
||||
/**
|
||||
* An Application is the container for a collection of commands.
|
||||
@ -934,6 +935,7 @@ class Application
|
||||
return new HelperSet(array(
|
||||
new FormatterHelper(),
|
||||
new DialogHelper(),
|
||||
new ProgressHelper(),
|
||||
));
|
||||
}
|
||||
|
||||
|
413
src/Symfony/Component/Console/Helper/ProgressHelper.php
Normal file
413
src/Symfony/Component/Console/Helper/ProgressHelper.php
Normal file
@ -0,0 +1,413 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Console\Helper;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* The Progress class providers helpers to display progress output.
|
||||
*
|
||||
* @author Chris Jones <leeked@gmail.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ProgressHelper extends Helper
|
||||
{
|
||||
const FORMAT_QUIET = ' %percent%%';
|
||||
const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%';
|
||||
const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%';
|
||||
const FORMAT_QUIET_NOMAX = ' %current%';
|
||||
const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]';
|
||||
const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%';
|
||||
|
||||
// options
|
||||
private $barWidth = 28;
|
||||
private $barChar = '=';
|
||||
private $emptyBarChar = '-';
|
||||
private $progressChar = '>';
|
||||
private $format = null;
|
||||
private $redrawFreq = 1;
|
||||
|
||||
private $barCharOriginal;
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* Current step
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $current;
|
||||
|
||||
/**
|
||||
* Maximum number of steps
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $max;
|
||||
|
||||
/**
|
||||
* Start time of the progress bar
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $startTime;
|
||||
|
||||
/**
|
||||
* List of formatting variables
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $defaultFormatVars = array(
|
||||
'current',
|
||||
'max',
|
||||
'bar',
|
||||
'percent',
|
||||
'elapsed',
|
||||
);
|
||||
|
||||
/**
|
||||
* Available formatting variables
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $formatVars;
|
||||
|
||||
/**
|
||||
* Stored format part widths (used for padding)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $widths = array(
|
||||
'current' => 4,
|
||||
'max' => 4,
|
||||
'percent' => 3,
|
||||
'elapsed' => 6,
|
||||
);
|
||||
|
||||
/**
|
||||
* Various time formats
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $timeFormats = array(
|
||||
array(0, '???'),
|
||||
array(2, '1 sec'),
|
||||
array(59, 'secs', 1),
|
||||
array(60, '1 min'),
|
||||
array(3600, 'mins', 60),
|
||||
array(5400, '1 hr'),
|
||||
array(86400, 'hrs', 3600),
|
||||
array(129600, '1 day'),
|
||||
array(604800, 'days', 86400),
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the progress bar width.
|
||||
*
|
||||
* @param int $size The progress bar size
|
||||
*/
|
||||
public function setBarWidth($size)
|
||||
{
|
||||
$this->barWidth = (int) $size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bar character.
|
||||
*
|
||||
* @param string $char A character
|
||||
*/
|
||||
public function setBarCharacter($char)
|
||||
{
|
||||
$this->barChar = $char;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the empty bar character.
|
||||
*
|
||||
* @param string $char A character
|
||||
*/
|
||||
public function setEmptyBarCharacter($char)
|
||||
{
|
||||
$this->emptyBarChar = $char;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress bar character.
|
||||
*
|
||||
* @param string $char A character
|
||||
*/
|
||||
public function setProgressCharacter($char)
|
||||
{
|
||||
$this->progressChar = $char;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress bar format.
|
||||
*
|
||||
* @param string $format The format
|
||||
*/
|
||||
public function setFormat($format)
|
||||
{
|
||||
$this->format = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redraw frequency.
|
||||
*
|
||||
* @param int $freq The frequency in seconds
|
||||
*/
|
||||
public function setRedrawFrequency($freq)
|
||||
{
|
||||
$this->redrawFreq = (int) $freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the progress output.
|
||||
*
|
||||
* @param OutputInterface $output An Output instance
|
||||
* @param integer $max Maximum steps
|
||||
*/
|
||||
public function start(OutputInterface $output, $max = null)
|
||||
{
|
||||
$this->startTime = time();
|
||||
$this->current = 0;
|
||||
$this->max = (int) $max;
|
||||
$this->output = $output;
|
||||
|
||||
if (null === $this->format) {
|
||||
switch ($output->getVerbosity()) {
|
||||
case OutputInterface::VERBOSITY_QUIET:
|
||||
$this->format = self::FORMAT_QUIET_NOMAX;
|
||||
if ($this->max > 0) {
|
||||
$this->format = self::FORMAT_QUIET;
|
||||
}
|
||||
break;
|
||||
case OutputInterface::VERBOSITY_VERBOSE:
|
||||
$this->format = self::FORMAT_VERBOSE_NOMAX;
|
||||
if ($this->max > 0) {
|
||||
$this->format = self::FORMAT_VERBOSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($this->max > 0) {
|
||||
$this->format = self::FORMAT_NORMAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the progress output X steps.
|
||||
*
|
||||
* @param integer $step Number of steps to advance
|
||||
* @param Boolean $redraw Whether to redraw or not
|
||||
*/
|
||||
public function advance($step = 1, $redraw = false)
|
||||
{
|
||||
if (null === $this->startTime) {
|
||||
throw new \LogicException('You must start the progress bar before calling advance().');
|
||||
}
|
||||
|
||||
if ($this->current === 0) {
|
||||
$redraw = true;
|
||||
}
|
||||
$this->current += $step;
|
||||
if ($redraw || $this->current % $this->redrawFreq === 0) {
|
||||
$this->display();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the current progress string.
|
||||
*
|
||||
* @param Boolean $finish Forces the end result
|
||||
*/
|
||||
public function display($finish = false)
|
||||
{
|
||||
if (null === $this->startTime) {
|
||||
throw new \LogicException('You must start the progress bar before calling display().');
|
||||
}
|
||||
|
||||
$message = $this->format;
|
||||
foreach ($this->generate($finish) as $name => $value) {
|
||||
$message = str_replace("%{$name}%", $value, $message);
|
||||
}
|
||||
$this->overwrite($this->output, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the progress output.
|
||||
*/
|
||||
public function finish()
|
||||
{
|
||||
if (null === $this->startTime) {
|
||||
throw new \LogicException('You must start the progress bar before calling finish().');
|
||||
}
|
||||
|
||||
if ($this->startTime !== null) {
|
||||
if (!$this->max) {
|
||||
$this->barChar = $this->barCharOriginal;
|
||||
$this->display(true);
|
||||
}
|
||||
$this->startTime = null;
|
||||
$this->output->writeln('');
|
||||
$this->output = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the progress helper.
|
||||
*/
|
||||
private function initialize()
|
||||
{
|
||||
$this->formatVars = array();
|
||||
foreach ($this->defaultFormatVars as $var) {
|
||||
if (strpos($this->format, "%{$var}%") !== false) {
|
||||
$this->formatVars[$var] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->max > 0) {
|
||||
$this->widths['max'] = strlen($this->max);
|
||||
$this->widths['current'] = $this->widths['max'];
|
||||
} else {
|
||||
$this->barCharOriginal = $this->barChar;
|
||||
$this->barChar = $this->emptyBarChar;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the array map of format variables to values.
|
||||
*
|
||||
* @param Boolean $finish Forces the end result
|
||||
* @return array Array of format vars and values
|
||||
*/
|
||||
private function generate($finish = false)
|
||||
{
|
||||
$vars = array();
|
||||
$percent = 0;
|
||||
if ($this->max > 0) {
|
||||
$percent = (double) round($this->current / $this->max, 1);
|
||||
}
|
||||
|
||||
if (isset($this->formatVars['bar'])) {
|
||||
$completeBars = 0;
|
||||
$emptyBars = 0;
|
||||
if ($this->max > 0) {
|
||||
$completeBars = floor($percent * $this->barWidth);
|
||||
} else {
|
||||
if (!$finish) {
|
||||
$completeBars = floor($this->current % $this->barWidth);
|
||||
} else {
|
||||
$completeBars = $this->barWidth;
|
||||
}
|
||||
}
|
||||
|
||||
$emptyBars = $this->barWidth - $completeBars - strlen($this->progressChar);
|
||||
$bar = str_repeat($this->barChar, $completeBars);
|
||||
if ($completeBars < $this->barWidth) {
|
||||
$bar .= $this->progressChar;
|
||||
$bar .= str_repeat($this->emptyBarChar, $emptyBars);
|
||||
}
|
||||
|
||||
$vars['bar'] = $bar;
|
||||
}
|
||||
|
||||
if (isset($this->formatVars['elapsed'])) {
|
||||
$elapsed = time() - $this->startTime;
|
||||
$vars['elapsed'] = str_pad($this->humaneTime($elapsed), $this->widths['elapsed'], ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
if (isset($this->formatVars['current'])) {
|
||||
$vars['current'] = str_pad($this->current, $this->widths['current'], ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
if (isset($this->formatVars['max'])) {
|
||||
$vars['max'] = $this->max;
|
||||
}
|
||||
|
||||
if (isset($this->formatVars['percent'])) {
|
||||
$vars['percent'] = str_pad($percent * 100, $this->widths['percent'], ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts seconds into human-readable format.
|
||||
*
|
||||
* @param integer $secs Number of seconds
|
||||
* @return string Time in readable format
|
||||
*/
|
||||
private function humaneTime($secs)
|
||||
{
|
||||
$text = '';
|
||||
foreach ($this->timeFormats as $format) {
|
||||
if ($secs < $format[0]) {
|
||||
if (count($format) == 2) {
|
||||
$text = $format[1];
|
||||
break;
|
||||
} else {
|
||||
$text = ceil($secs / $format[2]) . ' ' . $format[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites a previous message to the output.
|
||||
*
|
||||
* @param OutputInterface $output An Output instance
|
||||
* @param string|array $messages The message as an array of lines or a single string
|
||||
* @param Boolean $newline Whether to add a newline or not
|
||||
* @param integer $size The size of line
|
||||
*/
|
||||
private function overwrite(OutputInterface $output, $messages, $newline = true, $size = 80)
|
||||
{
|
||||
for ($place = $size; $place > 0; $place--) {
|
||||
$output->write("\x08", false);
|
||||
}
|
||||
|
||||
$output->write($messages, false);
|
||||
|
||||
for ($place = ($size - strlen($messages)); $place > 0; $place--) {
|
||||
$output->write(' ', false);
|
||||
}
|
||||
|
||||
// clean up the end line
|
||||
for ($place = ($size - strlen($messages)); $place > 0; $place--) {
|
||||
$output->write("\x08", false);
|
||||
}
|
||||
|
||||
if ($newline) {
|
||||
$output->write('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the canonical name of this helper.
|
||||
*
|
||||
* @return string The canonical name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'progress';
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Console\Tests\Helper;
|
||||
|
||||
use Symfony\Component\Console\Helper\ProgressHelper;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Output\StreamOutput;
|
||||
|
||||
class ProgressHelperTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testAdvance()
|
||||
{
|
||||
$progress = new ProgressHelper();
|
||||
$progress->start($output = $this->getOutputStream());
|
||||
$progress->advance();
|
||||
|
||||
rewind($output->getStream());
|
||||
$this->assertEquals($this->generateOutput(' 1 [->--------------------------]'), stream_get_contents($output->getStream()));
|
||||
}
|
||||
|
||||
public function testAdvanceWithStep()
|
||||
{
|
||||
$progress = new ProgressHelper();
|
||||
$progress->start($output = $this->getOutputStream());
|
||||
$progress->advance(5);
|
||||
|
||||
rewind($output->getStream());
|
||||
$this->assertEquals($this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream()));
|
||||
}
|
||||
|
||||
public function testAdvanceMultipleTimes()
|
||||
{
|
||||
$progress = new ProgressHelper();
|
||||
$progress->start($output = $this->getOutputStream());
|
||||
$progress->advance(3);
|
||||
$progress->advance(2);
|
||||
|
||||
rewind($output->getStream());
|
||||
$this->assertEquals($this->generateOutput(' 3 [--->------------------------]').$this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream()));
|
||||
}
|
||||
|
||||
public function testCustomizations()
|
||||
{
|
||||
$progress = new ProgressHelper();
|
||||
$progress->setBarWidth(10);
|
||||
$progress->setBarCharacter('_');
|
||||
$progress->setEmptyBarCharacter(' ');
|
||||
$progress->setProgressCharacter('/');
|
||||
$progress->setFormat(' %current%/%max% [%bar%] %percent%%');
|
||||
$progress->start($output = $this->getOutputStream(), 10);
|
||||
$progress->advance();
|
||||
|
||||
rewind($output->getStream());
|
||||
$this->assertEquals($this->generateOutput(' 1/10 [_/ ] 10%'), stream_get_contents($output->getStream()));
|
||||
}
|
||||
|
||||
protected function getOutputStream()
|
||||
{
|
||||
return new StreamOutput(fopen('php://memory', 'r+', false));
|
||||
}
|
||||
|
||||
protected function generateOutput($expected)
|
||||
{
|
||||
return str_repeat("\x08", 80).$expected.str_repeat(' ', 80 - strlen($expected)).str_repeat("\x08", 80 - strlen($expected));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user