namespace Symfony\Component\Console\Helper;
+use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Exception\LogicException;
+use Symfony\Component\Console\Terminal;
/**
* The ProgressBar provides helpers to display progress output.
* @author Fabien Potencier <fabien@symfony.com>
* @author Chris Jones <leeked@gmail.com>
*/
-class ProgressBar
+final class ProgressBar
{
- // options
private $barWidth = 28;
private $barChar;
private $emptyBarChar = '-';
private $format;
private $internalFormat;
private $redrawFreq = 1;
-
- /**
- * @var OutputInterface
- */
private $output;
private $step = 0;
private $max;
private $formatLineCount;
private $messages = array();
private $overwrite = true;
+ private $terminal;
private $firstRun = true;
private static $formatters;
private static $formats;
/**
- * Constructor.
- *
* @param OutputInterface $output An OutputInterface instance
* @param int $max Maximum steps (0 if unknown)
*/
$this->output = $output;
$this->setMaxSteps($max);
+ $this->terminal = new Terminal();
if (!$this->output->isDecorated()) {
// disable overwrite when output does not support ANSI codes.
* @param string $name The placeholder name (including the delimiter char like %)
* @param callable $callable A PHP callable
*/
- public static function setPlaceholderFormatterDefinition($name, $callable)
+ public static function setPlaceholderFormatterDefinition($name, callable $callable)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
return $this->max;
}
- /**
- * Gets the progress bar step.
- *
- * @deprecated since version 2.6, to be removed in 3.0. Use {@link getProgress()} instead.
- *
- * @return int The progress bar step
- */
- public function getStep()
- {
- @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the getProgress() method instead.', E_USER_DEPRECATED);
-
- return $this->getProgress();
- }
-
/**
* Gets the current step position.
*
/**
* Gets the progress bar step width.
*
- * @internal This method is public for PHP 5.3 compatibility, it should not be used.
- *
* @return int The progress bar step width
*/
- public function getStepWidth()
+ private function getStepWidth()
{
return $this->stepWidth;
}
*/
public function setBarWidth($size)
{
- $this->barWidth = (int) $size;
+ $this->barWidth = max(1, (int) $size);
}
/**
* 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 the current progress.
- *
- * @deprecated since version 2.6, to be removed in 3.0. Use {@link setProgress()} instead.
- *
- * @param int $step The current progress
- *
- * @throws LogicException
- */
- public function setCurrent($step)
- {
- @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the setProgress() method instead.', E_USER_DEPRECATED);
-
- $this->setProgress($step);
- }
-
/**
* Sets whether to overwrite the progressbar, false for new line.
*
* 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;
+ } elseif ($step < 0) {
+ $step = 0;
}
$prevPeriod = (int) ($this->step / $this->redrawFreq);
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}
- // these 3 variables can be removed in favor of using $this in the closure when support for PHP 5.3 will be dropped.
- $self = $this;
- $output = $this->output;
- $messages = $this->messages;
- $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self, $output, $messages) {
- if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {
- $text = call_user_func($formatter, $self, $output);
- } elseif (isset($messages[$matches[1]])) {
- $text = $messages[$matches[1]];
- } else {
- return $matches[0];
- }
-
- if (isset($matches[2])) {
- $text = sprintf('%'.$matches[2], $text);
- }
-
- return $text;
- }, $this->format));
+ $this->overwrite($this->buildLine());
}
/**
'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
);
}
+
+ /**
+ * @return string
+ */
+ private function buildLine()
+ {
+ $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
+ $callback = 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;
+ };
+ $line = preg_replace_callback($regex, $callback, $this->format);
+
+ // gets string length for each sub line with multiline format
+ $linesLength = array_map(function ($subLine) {
+ return Helper::strlenWithoutDecoration($this->output->getFormatter(), rtrim($subLine, "\r"));
+ }, explode("\n", $line));
+
+ $linesWidth = max($linesLength);
+
+ $terminalWidth = $this->terminal->getWidth();
+ if ($linesWidth <= $terminalWidth) {
+ return $line;
+ }
+
+ $this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth);
+
+ return preg_replace_callback($regex, $callback, $this->format);
+ }
}