This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/src/Symfony/Component/Console/Helper/ProgressBar.php
Nicolas Grekas a7ed68b38f Merge branch '2.8' into 3.0
* 2.8: (22 commits)
  Fix merge
  [HttpFoundation] Use UPSERT for sessions stored in PgSql >= 9.5
  [Console] fixed PHPDoc
  [travis] HHVM 3.12 LTS
  Fix feature detection for IE
  [Form] Fixed collapsed choice attributes
  [Console] added explanation of messages usage in a progress bar
  force enabling the external XML entity loaders
  [Yaml] properly count skipped comment lines
  [WebProfilerBundle] Fix invalid CSS style
  Added progressive jpeg to mime types guesser
  [Yaml] Fix wrong line number when comments are inserted in the middle of a block.
  Fixed singular of committee
  Do not inject web debug toolbar on attachments
  bumped Symfony version to 2.8.8
  updated VERSION for 2.8.7
  updated CHANGELOG for 2.8.7
  bumped Symfony version to 2.7.15
  updated VERSION for 2.7.14
  update CONTRIBUTORS for 2.7.14
  ...

Conflicts:
	CHANGELOG-2.7.md
	CHANGELOG-3.0.md
	src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
	src/Symfony/Component/HttpKernel/Kernel.php
2016-06-14 13:04:19 +02:00

596 lines
16 KiB
PHP

<?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\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\LogicException;
/**
* The ProgressBar provides helpers to display progress output.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Chris Jones <leeked@gmail.com>
*/
class ProgressBar
{
// options
private $barWidth = 28;
private $barChar;
private $emptyBarChar = '-';
private $progressChar = '>';
private $format;
private $internalFormat;
private $redrawFreq = 1;
/**
* @var OutputInterface
*/
private $output;
private $step = 0;
private $max;
private $startTime;
private $stepWidth;
private $percent = 0.0;
private $formatLineCount;
private $messages = array();
private $overwrite = true;
private static $formatters;
private static $formats;
/**
* Constructor.
*
* @param OutputInterface $output An OutputInterface instance
* @param int $max Maximum steps (0 if unknown)
*/
public function __construct(OutputInterface $output, $max = 0)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$this->output = $output;
$this->setMaxSteps($max);
if (!$this->output->isDecorated()) {
// disable overwrite when output does not support ANSI codes.
$this->overwrite = false;
// set a reasonable redraw frequency so output isn't flooded
$this->setRedrawFrequency($max / 10);
}
$this->startTime = time();
}
/**
* Sets a placeholder formatter for a given name.
*
* This method also allow you to override an existing placeholder.
*
* @param string $name The placeholder name (including the delimiter char like %)
* @param callable $callable A PHP callable
*/
public static function setPlaceholderFormatterDefinition($name, callable $callable)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
self::$formatters[$name] = $callable;
}
/**
* Gets the placeholder formatter for a given name.
*
* @param string $name The placeholder name (including the delimiter char like %)
*
* @return callable|null A PHP callable
*/
public static function getPlaceholderFormatterDefinition($name)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;
}
/**
* Sets a format for a given name.
*
* This method also allow you to override an existing format.
*
* @param string $name The format name
* @param string $format A format string
*/
public static function setFormatDefinition($name, $format)
{
if (!self::$formats) {
self::$formats = self::initFormats();
}
self::$formats[$name] = $format;
}
/**
* Gets the format for a given name.
*
* @param string $name The format name
*
* @return string|null A format string
*/
public static function getFormatDefinition($name)
{
if (!self::$formats) {
self::$formats = self::initFormats();
}
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
}
/**
* Associates a text with a named placeholder.
*
* The text is displayed when the progress bar is rendered but only
* when the corresponding placeholder is part of the custom format line
* (by wrapping the name with %).
*
* @param string $message The text to associate with the placeholder
* @param string $name The name of the placeholder
*/
public function setMessage($message, $name = 'message')
{
$this->messages[$name] = $message;
}
public function getMessage($name = 'message')
{
return $this->messages[$name];
}
/**
* Gets the progress bar start time.
*
* @return int The progress bar start time
*/
public function getStartTime()
{
return $this->startTime;
}
/**
* Gets the progress bar maximal steps.
*
* @return int The progress bar max steps
*/
public function getMaxSteps()
{
return $this->max;
}
/**
* Gets the current step position.
*
* @return int The progress bar step
*/
public function getProgress()
{
return $this->step;
}
/**
* Gets the progress bar step width.
*
* @return int The progress bar step width
*/
private function getStepWidth()
{
return $this->stepWidth;
}
/**
* Gets the current progress bar percent.
*
* @return float The current progress bar percent
*/
public function getProgressPercent()
{
return $this->percent;
}
/**
* Sets the progress bar width.
*
* @param int $size The progress bar size
*/
public function setBarWidth($size)
{
$this->barWidth = (int) $size;
}
/**
* Gets the progress bar width.
*
* @return int The progress bar size
*/
public function getBarWidth()
{
return $this->barWidth;
}
/**
* Sets the bar character.
*
* @param string $char A character
*/
public function setBarCharacter($char)
{
$this->barChar = $char;
}
/**
* Gets the bar character.
*
* @return string A character
*/
public function getBarCharacter()
{
if (null === $this->barChar) {
return $this->max ? '=' : $this->emptyBarChar;
}
return $this->barChar;
}
/**
* Sets the empty bar character.
*
* @param string $char A character
*/
public function setEmptyBarCharacter($char)
{
$this->emptyBarChar = $char;
}
/**
* Gets the empty bar character.
*
* @return string A character
*/
public function getEmptyBarCharacter()
{
return $this->emptyBarChar;
}
/**
* Sets the progress bar character.
*
* @param string $char A character
*/
public function setProgressCharacter($char)
{
$this->progressChar = $char;
}
/**
* Gets the progress bar character.
*
* @return string A character
*/
public function getProgressCharacter()
{
return $this->progressChar;
}
/**
* Sets the progress bar format.
*
* @param string $format The format
*/
public function setFormat($format)
{
$this->format = null;
$this->internalFormat = $format;
}
/**
* Sets the redraw frequency.
*
* @param int|float $freq The frequency in steps
*/
public function setRedrawFrequency($freq)
{
$this->redrawFreq = max((int) $freq, 1);
}
/**
* Starts the progress output.
*
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
*/
public function start($max = null)
{
$this->startTime = time();
$this->step = 0;
$this->percent = 0.0;
if (null !== $max) {
$this->setMaxSteps($max);
}
$this->display();
}
/**
* Advances the progress output X steps.
*
* @param int $step Number of steps to advance
*
* @throws LogicException
*/
public function advance($step = 1)
{
$this->setProgress($this->step + $step);
}
/**
* Sets whether to overwrite the progressbar, false for new line.
*
* @param bool $overwrite
*/
public function setOverwrite($overwrite)
{
$this->overwrite = (bool) $overwrite;
}
/**
* Sets the current progress.
*
* @param int $step The current progress
*
* @throws LogicException
*/
public function setProgress($step)
{
$step = (int) $step;
if ($step < $this->step) {
throw new LogicException('You can\'t regress the progress bar.');
}
if ($this->max && $step > $this->max) {
$this->max = $step;
}
$prevPeriod = (int) ($this->step / $this->redrawFreq);
$currPeriod = (int) ($step / $this->redrawFreq);
$this->step = $step;
$this->percent = $this->max ? (float) $this->step / $this->max : 0;
if ($prevPeriod !== $currPeriod || $this->max === $step) {
$this->display();
}
}
/**
* Finishes the progress output.
*/
public function finish()
{
if (!$this->max) {
$this->max = $this->step;
}
if ($this->step === $this->max && !$this->overwrite) {
// prevent double 100% output
return;
}
$this->setProgress($this->max);
}
/**
* Outputs the current progress string.
*/
public function display()
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}
if (null === $this->format) {
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) {
if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
$text = call_user_func($formatter, $this, $this->output);
} elseif (isset($this->messages[$matches[1]])) {
$text = $this->messages[$matches[1]];
} else {
return $matches[0];
}
if (isset($matches[2])) {
$text = sprintf('%'.$matches[2], $text);
}
return $text;
}, $this->format));
}
/**
* Removes the progress bar from the current line.
*
* This is useful if you wish to write some output
* while a progress bar is running.
* Call display() to show the progress bar again.
*/
public function clear()
{
if (!$this->overwrite) {
return;
}
if (null === $this->format) {
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}
$this->overwrite('');
}
/**
* Sets the progress bar format.
*
* @param string $format The format
*/
private function setRealFormat($format)
{
// try to use the _nomax variant if available
if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
$this->format = self::getFormatDefinition($format.'_nomax');
} elseif (null !== self::getFormatDefinition($format)) {
$this->format = self::getFormatDefinition($format);
} else {
$this->format = $format;
}
$this->formatLineCount = substr_count($this->format, "\n");
}
/**
* Sets the progress bar maximal steps.
*
* @param int $max 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.
*
* @param string $message The message
*/
private function overwrite($message)
{
if ($this->overwrite) {
// Move the cursor to the beginning of the line
$this->output->write("\x0D");
// Erase the line
$this->output->write("\x1B[2K");
// Erase previous lines
if ($this->formatLineCount > 0) {
$this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount));
}
} elseif ($this->step > 0) {
$this->output->writeln('');
}
$this->output->write($message);
}
private function determineBestFormat()
{
switch ($this->output->getVerbosity()) {
// OutputInterface::VERBOSITY_QUIET: display is disabled anyway
case OutputInterface::VERBOSITY_VERBOSE:
return $this->max ? 'verbose' : 'verbose_nomax';
case OutputInterface::VERBOSITY_VERY_VERBOSE:
return $this->max ? 'very_verbose' : 'very_verbose_nomax';
case OutputInterface::VERBOSITY_DEBUG:
return $this->max ? 'debug' : 'debug_nomax';
default:
return $this->max ? 'normal' : 'normal_nomax';
}
}
private static function initPlaceholderFormatters()
{
return array(
'bar' => function (ProgressBar $bar, OutputInterface $output) {
$completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth());
$display = str_repeat($bar->getBarCharacter(), $completeBars);
if ($completeBars < $bar->getBarWidth()) {
$emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter());
$display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars);
}
return $display;
},
'elapsed' => function (ProgressBar $bar) {
return Helper::formatTime(time() - $bar->getStartTime());
},
'remaining' => function (ProgressBar $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
}
if (!$bar->getProgress()) {
$remaining = 0;
} else {
$remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress()));
}
return Helper::formatTime($remaining);
},
'estimated' => function (ProgressBar $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
}
if (!$bar->getProgress()) {
$estimated = 0;
} else {
$estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps());
}
return Helper::formatTime($estimated);
},
'memory' => function (ProgressBar $bar) {
return Helper::formatMemory(memory_get_usage(true));
},
'current' => function (ProgressBar $bar) {
return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT);
},
'max' => function (ProgressBar $bar) {
return $bar->getMaxSteps();
},
'percent' => function (ProgressBar $bar) {
return floor($bar->getProgressPercent() * 100);
},
);
}
private static function initFormats()
{
return array(
'normal' => ' %current%/%max% [%bar%] %percent:3s%%',
'normal_nomax' => ' %current% [%bar%]',
'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%',
'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',
'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%',
'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',
'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%',
'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
);
}
}