PluginProbe
Depicter — Popup & Slider Builder / 1.2.0
Depicter — Popup & Slider Builder v1.2.0
4.8.1 trunk 1.0.0 1.1.0 1.1.2 1.1.4 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.3.5 1.3.8 1.5.0 1.5.1 1.5.2 1.5.5 1.6.0 1.6.1 1.6.2 1.7.0 All 76 releases
depicter / vendor / symfony / console / Helper / ProgressBar.php

ProgressBar.php in Depicter — Popup & Slider Builder 1.2.0, at vendor/symfony/console/Helper/ProgressBar.php

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