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/ProgressHelper.php

355 lines
9.6 KiB
PHP
Raw Normal View History

2012-03-05 00:02:59 +00:00
<?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>
*/
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%';
/**
* @var array
*/
protected $options = array(
'barWidth' => null,
'barChar' => null,
'emptyBarChar' => null,
'progressChar' => null,
'format' => null,
'redrawFreq' => null,
);
/**
* @var array
*/
private $defaultOptions = array(
'barWidth' => 28,
'barChar' => '=',
'emptyBarChar' => '-',
'progressChar' => '>',
'format' => self::FORMAT_NORMAL_NOMAX,
'redrawFreq' => 1,
);
/**
* @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),
);
/**
* Starts the progress output.
*
* @param OutputInterface $output An Output instance
* @param integer $max Maximum steps
* @param array $options Options for progress helper
*/
public function start(OutputInterface $output, $max = null, array $options = array())
{
$this->startTime = time();
$this->current = 0;
$this->max = (int) $max;
$this->output = $output;
switch ($output->getVerbosity()) {
case OutputInterface::VERBOSITY_QUIET:
$this->options['format'] = self::FORMAT_QUIET_NOMAX;
if ($this->max > 0) {
$this->options['format'] = self::FORMAT_QUIET;
}
break;
case OutputInterface::VERBOSITY_VERBOSE:
$this->options['format'] = self::FORMAT_VERBOSE_NOMAX;
if ($this->max > 0) {
$this->options['format'] = self::FORMAT_VERBOSE;
}
break;
default:
if ($this->max > 0) {
$this->options['format'] = self::FORMAT_NORMAL;
}
break;
}
$this->options = array_merge($this->defaultOptions, $options);
$this->inititalize();
}
/**
* 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 ($this->current === 0) {
$redraw = true;
}
$this->current += $step;
if ($redraw || $this->current % $this->options['redrawFreq'] === 0) {
$this->display();
}
}
/**
* Outputs the current progress string.
*
* @param Boolean $finish Forces the end result
*/
public function display($finish = false)
{
$message = $this->options['format'];
foreach ($this->generate($finish) as $name => $value) {
$message = str_replace("%{$name}%", $value, $message);
}
$this->overwrite($this->output, $message);
}
/**
* Finish the progress output
*/
public function finish()
{
if ($this->startTime !== null) {
if (!$this->max) {
$this->options['barChar'] = $this->options['barCharOriginal'];
$this->display(true);
}
$this->startTime = null;
$this->output->writeln('');
$this->output = null;
}
}
/**
* Initialize the progress helper.
*/
protected function inititalize()
{
$this->formatVars = array();
foreach ($this->defaultFormatVars as $var) {
if (strpos($this->options['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->options['barCharOriginal'] = $this->options['barChar'];
$this->options['barChar'] = $this->options['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
*/
protected 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->options['barWidth']);
} else {
if (!$finish) {
$completeBars = floor($this->current % $this->options['barWidth']);
} else {
$completeBars = $this->options['barWidth'];
}
}
$emptyBars = $this->options['barWidth'] - $completeBars - strlen($this->options['progressChar']);
$bar = str_repeat($this->options['barChar'], $completeBars);
if ($completeBars < $this->options['barWidth']) {
$bar .= $this->options['progressChar'];
$bar .= str_repeat($this->options['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';
}
}