summaryrefslogtreecommitdiff
path: root/vendor/symfony/console/Helper/ProgressIndicator.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/symfony/console/Helper/ProgressIndicator.php')
-rw-r--r--vendor/symfony/console/Helper/ProgressIndicator.php225
1 files changed, 225 insertions, 0 deletions
diff --git a/vendor/symfony/console/Helper/ProgressIndicator.php b/vendor/symfony/console/Helper/ProgressIndicator.php
new file mode 100644
index 0000000..969d835
--- /dev/null
+++ b/vendor/symfony/console/Helper/ProgressIndicator.php
@@ -0,0 +1,225 @@
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
12namespace Symfony\Component\Console\Helper;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Component\Console\Exception\LogicException;
16use Symfony\Component\Console\Output\OutputInterface;
17
18/**
19 * @author Kevin Bond <kevinbond@gmail.com>
20 */
21class ProgressIndicator
22{
23 private const FORMATS = [
24 'normal' => ' %indicator% %message%',
25 'normal_no_ansi' => ' %message%',
26
27 'verbose' => ' %indicator% %message% (%elapsed:6s%)',
28 'verbose_no_ansi' => ' %message% (%elapsed:6s%)',
29
30 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',
31 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',
32 ];
33
34 private int $startTime;
35 private ?string $format = null;
36 private ?string $message = null;
37 private array $indicatorValues;
38 private int $indicatorCurrent;
39 private float $indicatorUpdateTime;
40 private bool $started = false;
41
42 /**
43 * @var array<string, callable>
44 */
45 private static array $formatters;
46
47 /**
48 * @param int $indicatorChangeInterval Change interval in milliseconds
49 * @param array|null $indicatorValues Animated indicator characters
50 */
51 public function __construct(
52 private OutputInterface $output,
53 ?string $format = null,
54 private int $indicatorChangeInterval = 100,
55 ?array $indicatorValues = null,
56 ) {
57
58 $format ??= $this->determineBestFormat();
59 $indicatorValues ??= ['-', '\\', '|', '/'];
60 $indicatorValues = array_values($indicatorValues);
61
62 if (2 > \count($indicatorValues)) {
63 throw new InvalidArgumentException('Must have at least 2 indicator value characters.');
64 }
65
66 $this->format = self::getFormatDefinition($format);
67 $this->indicatorValues = $indicatorValues;
68 $this->startTime = time();
69 }
70
71 /**
72 * Sets the current indicator message.
73 */
74 public function setMessage(?string $message): void
75 {
76 $this->message = $message;
77
78 $this->display();
79 }
80
81 /**
82 * Starts the indicator output.
83 */
84 public function start(string $message): void
85 {
86 if ($this->started) {
87 throw new LogicException('Progress indicator already started.');
88 }
89
90 $this->message = $message;
91 $this->started = true;
92 $this->startTime = time();
93 $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval;
94 $this->indicatorCurrent = 0;
95
96 $this->display();
97 }
98
99 /**
100 * Advances the indicator.
101 */
102 public function advance(): void
103 {
104 if (!$this->started) {
105 throw new LogicException('Progress indicator has not yet been started.');
106 }
107
108 if (!$this->output->isDecorated()) {
109 return;
110 }
111
112 $currentTime = $this->getCurrentTimeInMilliseconds();
113
114 if ($currentTime < $this->indicatorUpdateTime) {
115 return;
116 }
117
118 $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval;
119 ++$this->indicatorCurrent;
120
121 $this->display();
122 }
123
124 /**
125 * Finish the indicator with message.
126 */
127 public function finish(string $message): void
128 {
129 if (!$this->started) {
130 throw new LogicException('Progress indicator has not yet been started.');
131 }
132
133 $this->message = $message;
134 $this->display();
135 $this->output->writeln('');
136 $this->started = false;
137 }
138
139 /**
140 * Gets the format for a given name.
141 */
142 public static function getFormatDefinition(string $name): ?string
143 {
144 return self::FORMATS[$name] ?? null;
145 }
146
147 /**
148 * Sets a placeholder formatter for a given name.
149 *
150 * This method also allow you to override an existing placeholder.
151 */
152 public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void
153 {
154 self::$formatters ??= self::initPlaceholderFormatters();
155
156 self::$formatters[$name] = $callable;
157 }
158
159 /**
160 * Gets the placeholder formatter for a given name (including the delimiter char like %).
161 */
162 public static function getPlaceholderFormatterDefinition(string $name): ?callable
163 {
164 self::$formatters ??= self::initPlaceholderFormatters();
165
166 return self::$formatters[$name] ?? null;
167 }
168
169 private function display(): void
170 {
171 if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
172 return;
173 }
174
175 $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) {
176 if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) {
177 return $formatter($this);
178 }
179
180 return $matches[0];
181 }, $this->format ?? ''));
182 }
183
184 private function determineBestFormat(): string
185 {
186 return match ($this->output->getVerbosity()) {
187 // OutputInterface::VERBOSITY_QUIET: display is disabled anyway
188 OutputInterface::VERBOSITY_VERBOSE => $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi',
189 OutputInterface::VERBOSITY_VERY_VERBOSE,
190 OutputInterface::VERBOSITY_DEBUG => $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi',
191 default => $this->output->isDecorated() ? 'normal' : 'normal_no_ansi',
192 };
193 }
194
195 /**
196 * Overwrites a previous message to the output.
197 */
198 private function overwrite(string $message): void
199 {
200 if ($this->output->isDecorated()) {
201 $this->output->write("\x0D\x1B[2K");
202 $this->output->write($message);
203 } else {
204 $this->output->writeln($message);
205 }
206 }
207
208 private function getCurrentTimeInMilliseconds(): float
209 {
210 return round(microtime(true) * 1000);
211 }
212
213 /**
214 * @return array<string, \Closure>
215 */
216 private static function initPlaceholderFormatters(): array
217 {
218 return [
219 'indicator' => fn (self $indicator) => $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)],
220 'message' => fn (self $indicator) => $indicator->message,
221 'elapsed' => fn (self $indicator) => Helper::formatTime(time() - $indicator->startTime, 2),
222 'memory' => fn () => Helper::formatMemory(memory_get_usage(true)),
223 ];
224 }
225}