8000 [2.2][Console] Add ProgressHelper · markross/symfony@4b89aae · GitHub
[go: up one dir, main page]

Skip to content

Commit 4b89aae

Browse files
leekfabpot
authored andcommitted
[2.2][Console] Add ProgressHelper
1 parent fb053f6 commit 4b89aae

File tree

1 file changed

+354
-0
lines changed

1 file changed

+354
-0
lines changed
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Helper;
13+
14+
use Symfony\Component\Console\Output\OutputInterface;
15+
16+
/**
17+
* The Progress class providers helpers to display progress output.
18+
*
19+
* @author Chris Jones <leeked@gmail.com>
20+
*/
21+
class ProgressHelper extends Helper
22+
{
23+
const FORMAT_QUIET = ' %percent%%';
24+
const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%';
25+
const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%';
26+
const FORMAT_QUIET_NOMAX = ' %current%';
27+
const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]';
28+
const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%';
29+
30+
/**
31+
* @var array
32+
*/
33+
protected $options = array(
34+
'barWidth' => null,
35+
'barChar' => null,
36+
'emptyBarChar' => null,
37+
'progressChar' => null,
38+
'format' => null,
39+
'redrawFreq' => null,
40+
);
41+
42+
/**
43+
* @var array
44+
*/
45+
private $defaultOptions = array(
46+
'barWidth' => 28,
47+
'barChar' => '=',
48+
'emptyBarChar' => '-',
49+
'progressChar' => '>',
50+
'format' => self::FORMAT_NORMAL_NOMAX,
51+
'redrawFreq' => 1,
52+
);
53+
54+
/**
55+
* @var OutputInterface
56+
*/
57+
private $output;
58+
59+
/**
60+
* Current step
61+
*
62+
* @var integer
63+
*/
64+
private $current;
65+
66+
/**
67+
* Maximum number of steps
68+
*
69+
* @var integer
70+
*/
71+
private $max;
72+
73+
/**
74+
* Start time of the progress bar
75+
*
76+
* @var integer
77+
*/
78+
private $startTime;
79+
80+
/**
81+
* List of formatting variables
82+
*
83+
* @var array
84+
*/
85+
private $defaultFormatVars = array(
86+
'current',
87+
'max',
88+
'bar',
89+
'percent',
90+
'elapsed',
91+
);
92+
93+
/**
94+
* Available formatting variables
95+
*
96+
* @var array
97+
*/
98+
private $formatVars;
99+
100+
/**
101+
* Stored format part widths (used for padding)
102+
*
103+
* @var array
104+
*/
105+
private $widths = array(
106+
'current' => 4,
107+
'max' => 4,
108+
'percent' => 3,
109+
'elapsed' => 6,
110+
);
111+
112+
/**
113+
* Various time formats
114+
*
115+
* @var array
116+
*/
117+
private $timeFormats = array(
118+
array(0, '???'),
119+
array(2, '1 sec'),
120+
array(59, 'secs', 1),
121+
array(60, '1 min'),
122+
array(3600, 'mins', 60),
123+
array(5400, '1 hr'),
124+
array(86400, 'hrs', 3600),
125+
array(129600, '1 day'),
126+
array(604800, 'days', 86400),
127+
);
128+
129+
/**
130+
* Starts the progress output.
131+
*
132+
* @param OutputInterface $output An Output instance
133+
* @param integer $max Maximum steps
134+
* @param array $options Options for progress helper
135+
*/
136+
public function start(OutputInterface $output, $max = null, array $options = array())
137+
{
138+
$this->startTime = time();
139+
$this->current = 0;
140+
$this->max = (int) $max;
141+
$this->output = $output;
142+
143+
switch ($output->getVerbosity()) {
144+
case OutputInterface::VERBOSITY_QUIET:
145+
$this->options['format'] = self::FORMAT_QUIET_NOMAX;
146+
if ($this->max > 0) {
147+
$this->options['format'] = self::FORMAT_QUIET;
148+
}
149+
break;
150+
case OutputInterface::VERBOSITY_VERBOSE:
151+
$this->options['format'] = self::FORMAT_VERBOSE_NOMAX;
152+
if ($this->max > 0) {
153+
$this->options['format'] = self::FORMAT_VERBOSE;
154+
}
155+
break;
156+
default:
157+
if ($this->max > 0) {
158+
$this->options['format'] = self::FORMAT_NORMAL;
159+
}
160+
break;
161+
}
162+
163+
$this->options = array_merge($this->defaultOptions, $options);
164+
$this->inititalize();
165+
}
166+
167+
/**
168+
* Advances the progress output X steps.
169+
*
170+
* @param integer $step Number of steps to advance
171+
* @param Boolean $redraw Whether to redraw or not
172+
*/
173+
public function advance($step = 1, $redraw = false)
174+
{
175+
if ($this->current === 0) {
176+
$redraw = true;
177+
}
178+
$this->current += $step;
179+
if ($redraw || $this->current % $this->options['redrawFreq'] === 0) {
180+
$this->display();
181+
}
182+
}
183+
184+
/**
185+
* Outputs the current progress string.
186+
*
187+
* @param Boolean $finish Forces the end result
188+
*/
189+
public function display($finish = false)
190+
{
191+
$message = $this->options['format'];
192+
foreach ($this->generate($finish) as $name => $value) {
193+
$message = str_replace("%{$name}%", $value, $message);
194+
}
195+
$this->overwrite($this->output, $message);
196+
}
197+
198+
/**
199+
* Finish the progress output
200+
*/
201+
public function finish()
202+
{
203+
if ($this->startTime !== null) {
204+
if (!$this->max) {
205+
$this->options['barChar'] = $this->options['barCharOriginal'];
206+
$this->display(true);
207+
}
208+
$this->startTime = null;
209+
$this->output->writeln('');
210+
$this->output = null;
211+
}
212+
}
213+
214+
/**
215+
* Initialize the progress helper.
216+
*/
217+
protected function inititalize()
218+
{
219+
$this->formatVars = array();
220+
foreach ($this->defaultFormatVars as $var) {
221+
if (strpos($this->options['format'], "%{$var}%") !== false) {
222+
$this->formatVars[$var] = true;
223+
}
224+
}
225+
226+
if ($this->max > 0) {
227+
$this->widths['max'] = strlen($this->max);
228+
$this->widths['current'] = $this->widths['max'];
229+
} else {
230+
$this->options['barCharOriginal'] = $this->options['barChar'];
231+
$this->options['barChar'] = $this->options['emptyBarChar'];
232+
}
233+
}
234+
235+
/**
236+
* Generates the array map of format variables to values.
237+
*
238+
* @param Boolean $finish Forces the end result
239+
* @return array Array of format vars and values
240+
*/
241+
protected function generate($finish = false)
242+
{
243+
$vars = array();
244+
$percent = 0;
245+
if ($this->max > 0) {
246+
$percent = (double) round($this->current / $this->max, 1);
247+
}
248+
249+
if (isset($this->formatVars['bar'])) {
250+
$completeBars = 0;
251+
$emptyBars = 0;
252+
if ($this->max > 0) {
253+
$completeBars = floor($percent * $this->options['barWidth']);
254+
} else {
255+
if (!$finish) {
256+
$completeBars = floor($this->current % $this->options['barWidth']);
257+
} else {
258+
$completeBars = $this->options['barWidth'];
259+
}
260+
}
261+
262+
$emptyBars = $this->options['barWidth'] - $completeBars - strlen($this->options['progressChar']);
263+
$bar = str_repeat($this->options['barChar'], $completeBars);
264+
if ($completeBars < $this->options['barWidth']) {
265+
$bar .= $this->options['progressChar'];
266+
$bar .= str_repeat($this->options['emptyBarChar'], $emptyBars);
267+
}
268+
269+
$vars['bar'] = $bar;
270+
}
271+
272+
if (isset($this->formatVars['elapsed'])) {
273+
$elapsed = time() - $this->startTime;
274+
$vars['elapsed'] = str_pad($this->humaneTime($elapsed), $this->widths['elapsed'], ' ', STR_PAD_LEFT);
275+
}
276+
277+
if (isset($this->formatVars['current'])) {
278+
$vars['current'] = str_pad($this->current, $this->widths['current'], ' ', STR_PAD_LEFT);
279+
}
280+
281+
if (isset($this->formatVars['max'])) {
282+
$vars['max'] = $this->max;
283+
}
284+
285+
if (isset($this->formatVars['percent'])) {
286+
$vars['percent'] = str_pad($percent * 100, $this->widths['percent'], ' ', STR_PAD_LEFT);
287+
}
288+
289+
return $vars;
290+
}
291+
292+
/**
293+
* Converts seconds into human-readable format.
294+
*
295+
* @param integer $secs Number of seconds
296+
* @return string Time in readable format
297+
*/
298+
private function humaneTime($secs)
299+
{
300+
$text = '';
301+
foreach ($this->timeFormats as $format) {
302+
if ($secs < $format[0]) {
303+
if (count($format) == 2) {
304+
$text = $format[1];
305+
break;
306+
} else {
307+
$text = ceil($secs / $format[2]) . ' ' . $format[1];
308+
break;
309+
}
310+
}
311+
}
312+
return $text;
313+
}
314+
315+
/**
316+
* Overwrites a previous message to the output.
317+
*
318+
* @param OutputInterface $output An Output instance
319+
* @param string|array $messages The message as an array of lines or a single string
320+
* @param Boolean $newline Whether to add a newline or not
321+
* @param integer $size The size of line
322+
*/
323+
private function overwrite(OutputInterface $output, $messages, $newline = true, $size = 80)
324+
{
325+
for ($place = $size; $place > 0; $place--) {
326+
$output->write("\x08", false);
327+
}
328+
329+
$output->write($messages, false);
330+
331+
for ($place = ($size - strlen($messages)); $place > 0; $place--) {
332+
$output->write(' ', false);
333+
}
334+
335+
// clean up the end line
336+
for ($place = ($size - strlen($messages)); $place > 0; $place--) {
337+
$output->write("\x08", false);
338+
}
339+
340+
if ($newline) {
341+
$output->write('');
342+
}
343+
}
344+
345+
/**
346+
* Returns the canonical name of this helper.
347+
*
348+
* @return string The canonical name
349+
*/
350+
public function getName()
351+
{
352+
return 'progress';
353+
}
354+
}

0 commit comments

Comments
 (0)
0