Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/console/Helper/ProgressBar.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 1fec387a4317 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
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\ConsoleOutputInterface; | |
15 use Symfony\Component\Console\Output\OutputInterface; | |
16 use Symfony\Component\Console\Exception\LogicException; | |
17 use Symfony\Component\Console\Terminal; | |
18 | |
19 /** | |
20 * The ProgressBar provides helpers to display progress output. | |
21 * | |
22 * @author Fabien Potencier <fabien@symfony.com> | |
23 * @author Chris Jones <leeked@gmail.com> | |
24 */ | |
25 class ProgressBar | |
26 { | |
27 // options | |
28 private $barWidth = 28; | |
29 private $barChar; | |
30 private $emptyBarChar = '-'; | |
31 private $progressChar = '>'; | |
32 private $format; | |
33 private $internalFormat; | |
34 private $redrawFreq = 1; | |
35 | |
36 /** | |
37 * @var OutputInterface | |
38 */ | |
39 private $output; | |
40 private $step = 0; | |
41 private $max; | |
42 private $startTime; | |
43 private $stepWidth; | |
44 private $percent = 0.0; | |
45 private $formatLineCount; | |
46 private $messages = array(); | |
47 private $overwrite = true; | |
48 private $terminal; | |
49 private $firstRun = true; | |
50 | |
51 private static $formatters; | |
52 private static $formats; | |
53 | |
54 /** | |
55 * @param OutputInterface $output An OutputInterface instance | |
56 * @param int $max Maximum steps (0 if unknown) | |
57 */ | |
58 public function __construct(OutputInterface $output, $max = 0) | |
59 { | |
60 if ($output instanceof ConsoleOutputInterface) { | |
61 $output = $output->getErrorOutput(); | |
62 } | |
63 | |
64 $this->output = $output; | |
65 $this->setMaxSteps($max); | |
66 $this->terminal = new Terminal(); | |
67 | |
68 if (!$this->output->isDecorated()) { | |
69 // disable overwrite when output does not support ANSI codes. | |
70 $this->overwrite = false; | |
71 | |
72 // set a reasonable redraw frequency so output isn't flooded | |
73 $this->setRedrawFrequency($max / 10); | |
74 } | |
75 | |
76 $this->startTime = time(); | |
77 } | |
78 | |
79 /** | |
80 * Sets a placeholder formatter for a given name. | |
81 * | |
82 * This method also allow you to override an existing placeholder. | |
83 * | |
84 * @param string $name The placeholder name (including the delimiter char like %) | |
85 * @param callable $callable A PHP callable | |
86 */ | |
87 public static function setPlaceholderFormatterDefinition($name, callable $callable) | |
88 { | |
89 if (!self::$formatters) { | |
90 self::$formatters = self::initPlaceholderFormatters(); | |
91 } | |
92 | |
93 self::$formatters[$name] = $callable; | |
94 } | |
95 | |
96 /** | |
97 * Gets the placeholder formatter for a given name. | |
98 * | |
99 * @param string $name The placeholder name (including the delimiter char like %) | |
100 * | |
101 * @return callable|null A PHP callable | |
102 */ | |
103 public static function getPlaceholderFormatterDefinition($name) | |
104 { | |
105 if (!self::$formatters) { | |
106 self::$formatters = self::initPlaceholderFormatters(); | |
107 } | |
108 | |
109 return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; | |
110 } | |
111 | |
112 /** | |
113 * Sets a format for a given name. | |
114 * | |
115 * This method also allow you to override an existing format. | |
116 * | |
117 * @param string $name The format name | |
118 * @param string $format A format string | |
119 */ | |
120 public static function setFormatDefinition($name, $format) | |
121 { | |
122 if (!self::$formats) { | |
123 self::$formats = self::initFormats(); | |
124 } | |
125 | |
126 self::$formats[$name] = $format; | |
127 } | |
128 | |
129 /** | |
130 * Gets the format for a given name. | |
131 * | |
132 * @param string $name The format name | |
133 * | |
134 * @return string|null A format string | |
135 */ | |
136 public static function getFormatDefinition($name) | |
137 { | |
138 if (!self::$formats) { | |
139 self::$formats = self::initFormats(); | |
140 } | |
141 | |
142 return isset(self::$formats[$name]) ? self::$formats[$name] : null; | |
143 } | |
144 | |
145 /** | |
146 * Associates a text with a named placeholder. | |
147 * | |
148 * The text is displayed when the progress bar is rendered but only | |
149 * when the corresponding placeholder is part of the custom format line | |
150 * (by wrapping the name with %). | |
151 * | |
152 * @param string $message The text to associate with the placeholder | |
153 * @param string $name The name of the placeholder | |
154 */ | |
155 public function setMessage($message, $name = 'message') | |
156 { | |
157 $this->messages[$name] = $message; | |
158 } | |
159 | |
160 public function getMessage($name = 'message') | |
161 { | |
162 return $this->messages[$name]; | |
163 } | |
164 | |
165 /** | |
166 * Gets the progress bar start time. | |
167 * | |
168 * @return int The progress bar start time | |
169 */ | |
170 public function getStartTime() | |
171 { | |
172 return $this->startTime; | |
173 } | |
174 | |
175 /** | |
176 * Gets the progress bar maximal steps. | |
177 * | |
178 * @return int The progress bar max steps | |
179 */ | |
180 public function getMaxSteps() | |
181 { | |
182 return $this->max; | |
183 } | |
184 | |
185 /** | |
186 * Gets the current step position. | |
187 * | |
188 * @return int The progress bar step | |
189 */ | |
190 public function getProgress() | |
191 { | |
192 return $this->step; | |
193 } | |
194 | |
195 /** | |
196 * Gets the progress bar step width. | |
197 * | |
198 * @return int The progress bar step width | |
199 */ | |
200 private function getStepWidth() | |
201 { | |
202 return $this->stepWidth; | |
203 } | |
204 | |
205 /** | |
206 * Gets the current progress bar percent. | |
207 * | |
208 * @return float The current progress bar percent | |
209 */ | |
210 public function getProgressPercent() | |
211 { | |
212 return $this->percent; | |
213 } | |
214 | |
215 /** | |
216 * Sets the progress bar width. | |
217 * | |
218 * @param int $size The progress bar size | |
219 */ | |
220 public function setBarWidth($size) | |
221 { | |
222 $this->barWidth = max(1, (int) $size); | |
223 } | |
224 | |
225 /** | |
226 * Gets the progress bar width. | |
227 * | |
228 * @return int The progress bar size | |
229 */ | |
230 public function getBarWidth() | |
231 { | |
232 return $this->barWidth; | |
233 } | |
234 | |
235 /** | |
236 * Sets the bar character. | |
237 * | |
238 * @param string $char A character | |
239 */ | |
240 public function setBarCharacter($char) | |
241 { | |
242 $this->barChar = $char; | |
243 } | |
244 | |
245 /** | |
246 * Gets the bar character. | |
247 * | |
248 * @return string A character | |
249 */ | |
250 public function getBarCharacter() | |
251 { | |
252 if (null === $this->barChar) { | |
253 return $this->max ? '=' : $this->emptyBarChar; | |
254 } | |
255 | |
256 return $this->barChar; | |
257 } | |
258 | |
259 /** | |
260 * Sets the empty bar character. | |
261 * | |
262 * @param string $char A character | |
263 */ | |
264 public function setEmptyBarCharacter($char) | |
265 { | |
266 $this->emptyBarChar = $char; | |
267 } | |
268 | |
269 /** | |
270 * Gets the empty bar character. | |
271 * | |
272 * @return string A character | |
273 */ | |
274 public function getEmptyBarCharacter() | |
275 { | |
276 return $this->emptyBarChar; | |
277 } | |
278 | |
279 /** | |
280 * Sets the progress bar character. | |
281 * | |
282 * @param string $char A character | |
283 */ | |
284 public function setProgressCharacter($char) | |
285 { | |
286 $this->progressChar = $char; | |
287 } | |
288 | |
289 /** | |
290 * Gets the progress bar character. | |
291 * | |
292 * @return string A character | |
293 */ | |
294 public function getProgressCharacter() | |
295 { | |
296 return $this->progressChar; | |
297 } | |
298 | |
299 /** | |
300 * Sets the progress bar format. | |
301 * | |
302 * @param string $format The format | |
303 */ | |
304 public function setFormat($format) | |
305 { | |
306 $this->format = null; | |
307 $this->internalFormat = $format; | |
308 } | |
309 | |
310 /** | |
311 * Sets the redraw frequency. | |
312 * | |
313 * @param int|float $freq The frequency in steps | |
314 */ | |
315 public function setRedrawFrequency($freq) | |
316 { | |
317 $this->redrawFreq = max((int) $freq, 1); | |
318 } | |
319 | |
320 /** | |
321 * Starts the progress output. | |
322 * | |
323 * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged | |
324 */ | |
325 public function start($max = null) | |
326 { | |
327 $this->startTime = time(); | |
328 $this->step = 0; | |
329 $this->percent = 0.0; | |
330 | |
331 if (null !== $max) { | |
332 $this->setMaxSteps($max); | |
333 } | |
334 | |
335 $this->display(); | |
336 } | |
337 | |
338 /** | |
339 * Advances the progress output X steps. | |
340 * | |
341 * @param int $step Number of steps to advance | |
342 */ | |
343 public function advance($step = 1) | |
344 { | |
345 $this->setProgress($this->step + $step); | |
346 } | |
347 | |
348 /** | |
349 * Sets whether to overwrite the progressbar, false for new line. | |
350 * | |
351 * @param bool $overwrite | |
352 */ | |
353 public function setOverwrite($overwrite) | |
354 { | |
355 $this->overwrite = (bool) $overwrite; | |
356 } | |
357 | |
358 /** | |
359 * Sets the current progress. | |
360 * | |
361 * @param int $step The current progress | |
362 */ | |
363 public function setProgress($step) | |
364 { | |
365 $step = (int) $step; | |
366 | |
367 if ($this->max && $step > $this->max) { | |
368 $this->max = $step; | |
369 } elseif ($step < 0) { | |
370 $step = 0; | |
371 } | |
372 | |
373 $prevPeriod = (int) ($this->step / $this->redrawFreq); | |
374 $currPeriod = (int) ($step / $this->redrawFreq); | |
375 $this->step = $step; | |
376 $this->percent = $this->max ? (float) $this->step / $this->max : 0; | |
377 if ($prevPeriod !== $currPeriod || $this->max === $step) { | |
378 $this->display(); | |
379 } | |
380 } | |
381 | |
382 /** | |
383 * Finishes the progress output. | |
384 */ | |
385 public function finish() | |
386 { | |
387 if (!$this->max) { | |
388 $this->max = $this->step; | |
389 } | |
390 | |
391 if ($this->step === $this->max && !$this->overwrite) { | |
392 // prevent double 100% output | |
393 return; | |
394 } | |
395 | |
396 $this->setProgress($this->max); | |
397 } | |
398 | |
399 /** | |
400 * Outputs the current progress string. | |
401 */ | |
402 public function display() | |
403 { | |
404 if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { | |
405 return; | |
406 } | |
407 | |
408 if (null === $this->format) { | |
409 $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); | |
410 } | |
411 | |
412 $this->overwrite($this->buildLine()); | |
413 } | |
414 | |
415 /** | |
416 * Removes the progress bar from the current line. | |
417 * | |
418 * This is useful if you wish to write some output | |
419 * while a progress bar is running. | |
420 * Call display() to show the progress bar again. | |
421 */ | |
422 public function clear() | |
423 { | |
424 if (!$this->overwrite) { | |
425 return; | |
426 } | |
427 | |
428 if (null === $this->format) { | |
429 $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); | |
430 } | |
431 | |
432 $this->overwrite(''); | |
433 } | |
434 | |
435 /** | |
436 * Sets the progress bar format. | |
437 * | |
438 * @param string $format The format | |
439 */ | |
440 private function setRealFormat($format) | |
441 { | |
442 // try to use the _nomax variant if available | |
443 if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { | |
444 $this->format = self::getFormatDefinition($format.'_nomax'); | |
445 } elseif (null !== self::getFormatDefinition($format)) { | |
446 $this->format = self::getFormatDefinition($format); | |
447 } else { | |
448 $this->format = $format; | |
449 } | |
450 | |
451 $this->formatLineCount = substr_count($this->format, "\n"); | |
452 } | |
453 | |
454 /** | |
455 * Sets the progress bar maximal steps. | |
456 * | |
457 * @param int $max The progress bar max steps | |
458 */ | |
459 private function setMaxSteps($max) | |
460 { | |
461 $this->max = max(0, (int) $max); | |
462 $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; | |
463 } | |
464 | |
465 /** | |
466 * Overwrites a previous message to the output. | |
467 * | |
468 * @param string $message The message | |
469 */ | |
470 private function overwrite($message) | |
471 { | |
472 if ($this->overwrite) { | |
473 if (!$this->firstRun) { | |
474 // Move the cursor to the beginning of the line | |
475 $this->output->write("\x0D"); | |
476 | |
477 // Erase the line | |
478 $this->output->write("\x1B[2K"); | |
479 | |
480 // Erase previous lines | |
481 if ($this->formatLineCount > 0) { | |
482 $this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount)); | |
483 } | |
484 } | |
485 } elseif ($this->step > 0) { | |
486 $this->output->writeln(''); | |
487 } | |
488 | |
489 $this->firstRun = false; | |
490 | |
491 $this->output->write($message); | |
492 } | |
493 | |
494 private function determineBestFormat() | |
495 { | |
496 switch ($this->output->getVerbosity()) { | |
497 // OutputInterface::VERBOSITY_QUIET: display is disabled anyway | |
498 case OutputInterface::VERBOSITY_VERBOSE: | |
499 return $this->max ? 'verbose' : 'verbose_nomax'; | |
500 case OutputInterface::VERBOSITY_VERY_VERBOSE: | |
501 return $this->max ? 'very_verbose' : 'very_verbose_nomax'; | |
502 case OutputInterface::VERBOSITY_DEBUG: | |
503 return $this->max ? 'debug' : 'debug_nomax'; | |
504 default: | |
505 return $this->max ? 'normal' : 'normal_nomax'; | |
506 } | |
507 } | |
508 | |
509 private static function initPlaceholderFormatters() | |
510 { | |
511 return array( | |
512 'bar' => function (ProgressBar $bar, OutputInterface $output) { | |
513 $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth()); | |
514 $display = str_repeat($bar->getBarCharacter(), $completeBars); | |
515 if ($completeBars < $bar->getBarWidth()) { | |
516 $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); | |
517 $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); | |
518 } | |
519 | |
520 return $display; | |
521 }, | |
522 'elapsed' => function (ProgressBar $bar) { | |
523 return Helper::formatTime(time() - $bar->getStartTime()); | |
524 }, | |
525 'remaining' => function (ProgressBar $bar) { | |
526 if (!$bar->getMaxSteps()) { | |
527 throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); | |
528 } | |
529 | |
530 if (!$bar->getProgress()) { | |
531 $remaining = 0; | |
532 } else { | |
533 $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress())); | |
534 } | |
535 | |
536 return Helper::formatTime($remaining); | |
537 }, | |
538 'estimated' => function (ProgressBar $bar) { | |
539 if (!$bar->getMaxSteps()) { | |
540 throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); | |
541 } | |
542 | |
543 if (!$bar->getProgress()) { | |
544 $estimated = 0; | |
545 } else { | |
546 $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps()); | |
547 } | |
548 | |
549 return Helper::formatTime($estimated); | |
550 }, | |
551 'memory' => function (ProgressBar $bar) { | |
552 return Helper::formatMemory(memory_get_usage(true)); | |
553 }, | |
554 'current' => function (ProgressBar $bar) { | |
555 return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); | |
556 }, | |
557 'max' => function (ProgressBar $bar) { | |
558 return $bar->getMaxSteps(); | |
559 }, | |
560 'percent' => function (ProgressBar $bar) { | |
561 return floor($bar->getProgressPercent() * 100); | |
562 }, | |
563 ); | |
564 } | |
565 | |
566 private static function initFormats() | |
567 { | |
568 return array( | |
569 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', | |
570 'normal_nomax' => ' %current% [%bar%]', | |
571 | |
572 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', | |
573 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', | |
574 | |
575 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', | |
576 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', | |
577 | |
578 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', | |
579 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%', | |
580 ); | |
581 } | |
582 | |
583 /** | |
584 * @return string | |
585 */ | |
586 private function buildLine() | |
587 { | |
588 $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; | |
589 $callback = function ($matches) { | |
590 if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { | |
591 $text = call_user_func($formatter, $this, $this->output); | |
592 } elseif (isset($this->messages[$matches[1]])) { | |
593 $text = $this->messages[$matches[1]]; | |
594 } else { | |
595 return $matches[0]; | |
596 } | |
597 | |
598 if (isset($matches[2])) { | |
599 $text = sprintf('%'.$matches[2], $text); | |
600 } | |
601 | |
602 return $text; | |
603 }; | |
604 $line = preg_replace_callback($regex, $callback, $this->format); | |
605 | |
606 // gets string length for each sub line with multiline format | |
607 $linesLength = array_map(function ($subLine) { | |
608 return Helper::strlenWithoutDecoration($this->output->getFormatter(), rtrim($subLine, "\r")); | |
609 }, explode("\n", $line)); | |
610 | |
611 $linesWidth = max($linesLength); | |
612 | |
613 $terminalWidth = $this->terminal->getWidth(); | |
614 if ($linesWidth <= $terminalWidth) { | |
615 return $line; | |
616 } | |
617 | |
618 $this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth); | |
619 | |
620 return preg_replace_callback($regex, $callback, $this->format); | |
621 } | |
622 } |