summaryrefslogtreecommitdiff
path: root/vendor/symfony/console
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
committerpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
commitbf6655a534a6775d30cafa67bd801276bda1d98d (patch)
treec6381e3f6c81c33eab72508f410b165ba05f7e9c /vendor/symfony/console
parent94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff)
downloadAppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/symfony/console')
-rw-r--r--vendor/symfony/console/Application.php1282
-rw-r--r--vendor/symfony/console/Attribute/AsCommand.php45
-rw-r--r--vendor/symfony/console/CHANGELOG.md274
-rw-r--r--vendor/symfony/console/CI/GithubActionReporter.php99
-rw-r--r--vendor/symfony/console/Color.php133
-rw-r--r--vendor/symfony/console/Command/Command.php664
-rw-r--r--vendor/symfony/console/Command/CompleteCommand.php212
-rw-r--r--vendor/symfony/console/Command/DumpCompletionCommand.php151
-rw-r--r--vendor/symfony/console/Command/HelpCommand.php76
-rw-r--r--vendor/symfony/console/Command/LazyCommand.php206
-rw-r--r--vendor/symfony/console/Command/ListCommand.php72
-rw-r--r--vendor/symfony/console/Command/LockableTrait.php74
-rw-r--r--vendor/symfony/console/Command/SignalableCommandInterface.php32
-rw-r--r--vendor/symfony/console/Command/TraceableCommand.php356
-rw-r--r--vendor/symfony/console/CommandLoader/CommandLoaderInterface.php38
-rw-r--r--vendor/symfony/console/CommandLoader/ContainerCommandLoader.php52
-rw-r--r--vendor/symfony/console/CommandLoader/FactoryCommandLoader.php52
-rw-r--r--vendor/symfony/console/Completion/CompletionInput.php248
-rw-r--r--vendor/symfony/console/Completion/CompletionSuggestions.php97
-rw-r--r--vendor/symfony/console/Completion/Output/BashCompletionOutput.php33
-rw-r--r--vendor/symfony/console/Completion/Output/CompletionOutputInterface.php25
-rw-r--r--vendor/symfony/console/Completion/Output/FishCompletionOutput.php36
-rw-r--r--vendor/symfony/console/Completion/Output/ZshCompletionOutput.php36
-rw-r--r--vendor/symfony/console/Completion/Suggestion.php41
-rw-r--r--vendor/symfony/console/ConsoleEvents.php72
-rw-r--r--vendor/symfony/console/Cursor.php204
-rw-r--r--vendor/symfony/console/DataCollector/CommandDataCollector.php234
-rw-r--r--vendor/symfony/console/Debug/CliRequest.php70
-rw-r--r--vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php131
-rw-r--r--vendor/symfony/console/Descriptor/ApplicationDescription.php136
-rw-r--r--vendor/symfony/console/Descriptor/Descriptor.php74
-rw-r--r--vendor/symfony/console/Descriptor/DescriptorInterface.php24
-rw-r--r--vendor/symfony/console/Descriptor/JsonDescriptor.php166
-rw-r--r--vendor/symfony/console/Descriptor/MarkdownDescriptor.php173
-rw-r--r--vendor/symfony/console/Descriptor/ReStructuredTextDescriptor.php272
-rw-r--r--vendor/symfony/console/Descriptor/TextDescriptor.php317
-rw-r--r--vendor/symfony/console/Descriptor/XmlDescriptor.php232
-rw-r--r--vendor/symfony/console/Event/ConsoleCommandEvent.php54
-rw-r--r--vendor/symfony/console/Event/ConsoleErrorEvent.php58
-rw-r--r--vendor/symfony/console/Event/ConsoleEvent.php56
-rw-r--r--vendor/symfony/console/Event/ConsoleSignalEvent.php56
-rw-r--r--vendor/symfony/console/Event/ConsoleTerminateEvent.php50
-rw-r--r--vendor/symfony/console/EventListener/ErrorListener.php93
-rw-r--r--vendor/symfony/console/Exception/CommandNotFoundException.php43
-rw-r--r--vendor/symfony/console/Exception/ExceptionInterface.php21
-rw-r--r--vendor/symfony/console/Exception/InvalidArgumentException.php19
-rw-r--r--vendor/symfony/console/Exception/InvalidOptionException.php21
-rw-r--r--vendor/symfony/console/Exception/LogicException.php19
-rw-r--r--vendor/symfony/console/Exception/MissingInputException.php21
-rw-r--r--vendor/symfony/console/Exception/NamespaceNotFoundException.php21
-rw-r--r--vendor/symfony/console/Exception/RunCommandFailedException.php29
-rw-r--r--vendor/symfony/console/Exception/RuntimeException.php19
-rw-r--r--vendor/symfony/console/Formatter/NullOutputFormatter.php51
-rw-r--r--vendor/symfony/console/Formatter/NullOutputFormatterStyle.php48
-rw-r--r--vendor/symfony/console/Formatter/OutputFormatter.php268
-rw-r--r--vendor/symfony/console/Formatter/OutputFormatterInterface.php52
-rw-r--r--vendor/symfony/console/Formatter/OutputFormatterStyle.php89
-rw-r--r--vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php50
-rw-r--r--vendor/symfony/console/Formatter/OutputFormatterStyleStack.php103
-rw-r--r--vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php25
-rw-r--r--vendor/symfony/console/Helper/DebugFormatterHelper.php98
-rw-r--r--vendor/symfony/console/Helper/DescriptorHelper.php91
-rw-r--r--vendor/symfony/console/Helper/Dumper.php53
-rw-r--r--vendor/symfony/console/Helper/FormatterHelper.php81
-rw-r--r--vendor/symfony/console/Helper/Helper.php159
-rw-r--r--vendor/symfony/console/Helper/HelperInterface.php35
-rw-r--r--vendor/symfony/console/Helper/HelperSet.php74
-rw-r--r--vendor/symfony/console/Helper/InputAwareHelper.php30
-rw-r--r--vendor/symfony/console/Helper/OutputWrapper.php76
-rw-r--r--vendor/symfony/console/Helper/ProcessHelper.php137
-rw-r--r--vendor/symfony/console/Helper/ProgressBar.php645
-rw-r--r--vendor/symfony/console/Helper/ProgressIndicator.php225
-rw-r--r--vendor/symfony/console/Helper/QuestionHelper.php589
-rw-r--r--vendor/symfony/console/Helper/SymfonyQuestionHelper.php103
-rw-r--r--vendor/symfony/console/Helper/Table.php924
-rw-r--r--vendor/symfony/console/Helper/TableCell.php71
-rw-r--r--vendor/symfony/console/Helper/TableCellStyle.php84
-rw-r--r--vendor/symfony/console/Helper/TableRows.php28
-rw-r--r--vendor/symfony/console/Helper/TableSeparator.php25
-rw-r--r--vendor/symfony/console/Helper/TableStyle.php362
-rw-r--r--vendor/symfony/console/Input/ArgvInput.php396
-rw-r--r--vendor/symfony/console/Input/ArrayInput.php191
-rw-r--r--vendor/symfony/console/Input/Input.php174
-rw-r--r--vendor/symfony/console/Input/InputArgument.php160
-rw-r--r--vendor/symfony/console/Input/InputAwareInterface.php26
-rw-r--r--vendor/symfony/console/Input/InputDefinition.php402
-rw-r--r--vendor/symfony/console/Input/InputInterface.php138
-rw-r--r--vendor/symfony/console/Input/InputOption.php262
-rw-r--r--vendor/symfony/console/Input/StreamableInputInterface.php37
-rw-r--r--vendor/symfony/console/Input/StringInput.php85
-rw-r--r--vendor/symfony/console/LICENSE19
-rw-r--r--vendor/symfony/console/Logger/ConsoleLogger.php120
-rw-r--r--vendor/symfony/console/Messenger/RunCommandContext.php25
-rw-r--r--vendor/symfony/console/Messenger/RunCommandMessage.php36
-rw-r--r--vendor/symfony/console/Messenger/RunCommandMessageHandler.php49
-rw-r--r--vendor/symfony/console/Output/AnsiColorMode.php106
-rw-r--r--vendor/symfony/console/Output/BufferedOutput.php40
-rw-r--r--vendor/symfony/console/Output/ConsoleOutput.php153
-rw-r--r--vendor/symfony/console/Output/ConsoleOutputInterface.php30
-rw-r--r--vendor/symfony/console/Output/ConsoleSectionOutput.php237
-rw-r--r--vendor/symfony/console/Output/NullOutput.php89
-rw-r--r--vendor/symfony/console/Output/Output.php138
-rw-r--r--vendor/symfony/console/Output/OutputInterface.php100
-rw-r--r--vendor/symfony/console/Output/StreamOutput.php122
-rw-r--r--vendor/symfony/console/Output/TrimmedBufferOutput.php58
-rw-r--r--vendor/symfony/console/Question/ChoiceQuestion.php178
-rw-r--r--vendor/symfony/console/Question/ConfirmationQuestion.php57
-rw-r--r--vendor/symfony/console/Question/Question.php280
-rw-r--r--vendor/symfony/console/README.md27
-rw-r--r--vendor/symfony/console/Resources/bin/hiddeninput.exebin0 -> 9216 bytes
-rw-r--r--vendor/symfony/console/Resources/completion.bash94
-rw-r--r--vendor/symfony/console/Resources/completion.fish25
-rw-r--r--vendor/symfony/console/Resources/completion.zsh82
-rw-r--r--vendor/symfony/console/SignalRegistry/SignalMap.php36
-rw-r--r--vendor/symfony/console/SignalRegistry/SignalRegistry.php57
-rw-r--r--vendor/symfony/console/SingleCommandApplication.php72
-rw-r--r--vendor/symfony/console/Style/OutputStyle.php109
-rw-r--r--vendor/symfony/console/Style/StyleInterface.php110
-rw-r--r--vendor/symfony/console/Style/SymfonyStyle.php455
-rw-r--r--vendor/symfony/console/Terminal.php228
-rw-r--r--vendor/symfony/console/Tester/ApplicationTester.php83
-rw-r--r--vendor/symfony/console/Tester/CommandCompletionTester.php54
-rw-r--r--vendor/symfony/console/Tester/CommandTester.php74
-rw-r--r--vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php43
-rw-r--r--vendor/symfony/console/Tester/TesterTrait.php178
-rw-r--r--vendor/symfony/console/composer.json54
126 files changed, 17084 insertions, 0 deletions
diff --git a/vendor/symfony/console/Application.php b/vendor/symfony/console/Application.php
new file mode 100644
index 0000000..87eb7a6
--- /dev/null
+++ b/vendor/symfony/console/Application.php
@@ -0,0 +1,1282 @@
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;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Command\CompleteCommand;
16use Symfony\Component\Console\Command\DumpCompletionCommand;
17use Symfony\Component\Console\Command\HelpCommand;
18use Symfony\Component\Console\Command\LazyCommand;
19use Symfony\Component\Console\Command\ListCommand;
20use Symfony\Component\Console\Command\SignalableCommandInterface;
21use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
22use Symfony\Component\Console\Completion\CompletionInput;
23use Symfony\Component\Console\Completion\CompletionSuggestions;
24use Symfony\Component\Console\Completion\Suggestion;
25use Symfony\Component\Console\Event\ConsoleCommandEvent;
26use Symfony\Component\Console\Event\ConsoleErrorEvent;
27use Symfony\Component\Console\Event\ConsoleSignalEvent;
28use Symfony\Component\Console\Event\ConsoleTerminateEvent;
29use Symfony\Component\Console\Exception\CommandNotFoundException;
30use Symfony\Component\Console\Exception\ExceptionInterface;
31use Symfony\Component\Console\Exception\LogicException;
32use Symfony\Component\Console\Exception\NamespaceNotFoundException;
33use Symfony\Component\Console\Exception\RuntimeException;
34use Symfony\Component\Console\Formatter\OutputFormatter;
35use Symfony\Component\Console\Helper\DebugFormatterHelper;
36use Symfony\Component\Console\Helper\DescriptorHelper;
37use Symfony\Component\Console\Helper\FormatterHelper;
38use Symfony\Component\Console\Helper\Helper;
39use Symfony\Component\Console\Helper\HelperSet;
40use Symfony\Component\Console\Helper\ProcessHelper;
41use Symfony\Component\Console\Helper\QuestionHelper;
42use Symfony\Component\Console\Input\ArgvInput;
43use Symfony\Component\Console\Input\ArrayInput;
44use Symfony\Component\Console\Input\InputArgument;
45use Symfony\Component\Console\Input\InputAwareInterface;
46use Symfony\Component\Console\Input\InputDefinition;
47use Symfony\Component\Console\Input\InputInterface;
48use Symfony\Component\Console\Input\InputOption;
49use Symfony\Component\Console\Output\ConsoleOutput;
50use Symfony\Component\Console\Output\ConsoleOutputInterface;
51use Symfony\Component\Console\Output\OutputInterface;
52use Symfony\Component\Console\SignalRegistry\SignalRegistry;
53use Symfony\Component\Console\Style\SymfonyStyle;
54use Symfony\Component\ErrorHandler\ErrorHandler;
55use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
56use Symfony\Contracts\Service\ResetInterface;
57
58/**
59 * An Application is the container for a collection of commands.
60 *
61 * It is the main entry point of a Console application.
62 *
63 * This class is optimized for a standard CLI environment.
64 *
65 * Usage:
66 *
67 * $app = new Application('myapp', '1.0 (stable)');
68 * $app->add(new SimpleCommand());
69 * $app->run();
70 *
71 * @author Fabien Potencier <fabien@symfony.com>
72 */
73class Application implements ResetInterface
74{
75 private array $commands = [];
76 private bool $wantHelps = false;
77 private ?Command $runningCommand = null;
78 private ?CommandLoaderInterface $commandLoader = null;
79 private bool $catchExceptions = true;
80 private bool $catchErrors = false;
81 private bool $autoExit = true;
82 private InputDefinition $definition;
83 private HelperSet $helperSet;
84 private ?EventDispatcherInterface $dispatcher = null;
85 private Terminal $terminal;
86 private string $defaultCommand;
87 private bool $singleCommand = false;
88 private bool $initialized = false;
89 private ?SignalRegistry $signalRegistry = null;
90 private array $signalsToDispatchEvent = [];
91
92 public function __construct(
93 private string $name = 'UNKNOWN',
94 private string $version = 'UNKNOWN',
95 ) {
96 $this->terminal = new Terminal();
97 $this->defaultCommand = 'list';
98 if (\defined('SIGINT') && SignalRegistry::isSupported()) {
99 $this->signalRegistry = new SignalRegistry();
100 $this->signalsToDispatchEvent = [\SIGINT, \SIGQUIT, \SIGTERM, \SIGUSR1, \SIGUSR2];
101 }
102 }
103
104 /**
105 * @final
106 */
107 public function setDispatcher(EventDispatcherInterface $dispatcher): void
108 {
109 $this->dispatcher = $dispatcher;
110 }
111
112 public function setCommandLoader(CommandLoaderInterface $commandLoader): void
113 {
114 $this->commandLoader = $commandLoader;
115 }
116
117 public function getSignalRegistry(): SignalRegistry
118 {
119 if (!$this->signalRegistry) {
120 throw new RuntimeException('Signals are not supported. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
121 }
122
123 return $this->signalRegistry;
124 }
125
126 public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent): void
127 {
128 $this->signalsToDispatchEvent = $signalsToDispatchEvent;
129 }
130
131 /**
132 * Runs the current application.
133 *
134 * @return int 0 if everything went fine, or an error code
135 *
136 * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}.
137 */
138 public function run(?InputInterface $input = null, ?OutputInterface $output = null): int
139 {
140 if (\function_exists('putenv')) {
141 @putenv('LINES='.$this->terminal->getHeight());
142 @putenv('COLUMNS='.$this->terminal->getWidth());
143 }
144
145 $input ??= new ArgvInput();
146 $output ??= new ConsoleOutput();
147
148 $renderException = function (\Throwable $e) use ($output) {
149 if ($output instanceof ConsoleOutputInterface) {
150 $this->renderThrowable($e, $output->getErrorOutput());
151 } else {
152 $this->renderThrowable($e, $output);
153 }
154 };
155 if ($phpHandler = set_exception_handler($renderException)) {
156 restore_exception_handler();
157 if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
158 $errorHandler = true;
159 } elseif ($errorHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
160 $phpHandler[0]->setExceptionHandler($errorHandler);
161 }
162 }
163
164 $this->configureIO($input, $output);
165
166 try {
167 $exitCode = $this->doRun($input, $output);
168 } catch (\Throwable $e) {
169 if ($e instanceof \Exception && !$this->catchExceptions) {
170 throw $e;
171 }
172 if (!$e instanceof \Exception && !$this->catchErrors) {
173 throw $e;
174 }
175
176 $renderException($e);
177
178 $exitCode = $e->getCode();
179 if (is_numeric($exitCode)) {
180 $exitCode = (int) $exitCode;
181 if ($exitCode <= 0) {
182 $exitCode = 1;
183 }
184 } else {
185 $exitCode = 1;
186 }
187 } finally {
188 // if the exception handler changed, keep it
189 // otherwise, unregister $renderException
190 if (!$phpHandler) {
191 if (set_exception_handler($renderException) === $renderException) {
192 restore_exception_handler();
193 }
194 restore_exception_handler();
195 } elseif (!$errorHandler) {
196 $finalHandler = $phpHandler[0]->setExceptionHandler(null);
197 if ($finalHandler !== $renderException) {
198 $phpHandler[0]->setExceptionHandler($finalHandler);
199 }
200 }
201 }
202
203 if ($this->autoExit) {
204 if ($exitCode > 255) {
205 $exitCode = 255;
206 }
207
208 exit($exitCode);
209 }
210
211 return $exitCode;
212 }
213
214 /**
215 * Runs the current application.
216 *
217 * @return int 0 if everything went fine, or an error code
218 */
219 public function doRun(InputInterface $input, OutputInterface $output): int
220 {
221 if (true === $input->hasParameterOption(['--version', '-V'], true)) {
222 $output->writeln($this->getLongVersion());
223
224 return 0;
225 }
226
227 try {
228 // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument.
229 $input->bind($this->getDefinition());
230 } catch (ExceptionInterface) {
231 // Errors must be ignored, full binding/validation happens later when the command is known.
232 }
233
234 $name = $this->getCommandName($input);
235 if (true === $input->hasParameterOption(['--help', '-h'], true)) {
236 if (!$name) {
237 $name = 'help';
238 $input = new ArrayInput(['command_name' => $this->defaultCommand]);
239 } else {
240 $this->wantHelps = true;
241 }
242 }
243
244 if (!$name) {
245 $name = $this->defaultCommand;
246 $definition = $this->getDefinition();
247 $definition->setArguments(array_merge(
248 $definition->getArguments(),
249 [
250 'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
251 ]
252 ));
253 }
254
255 try {
256 $this->runningCommand = null;
257 // the command name MUST be the first element of the input
258 $command = $this->find($name);
259 } catch (\Throwable $e) {
260 if (($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) && 1 === \count($alternatives = $e->getAlternatives()) && $input->isInteractive()) {
261 $alternative = $alternatives[0];
262
263 $style = new SymfonyStyle($input, $output);
264 $output->writeln('');
265 $formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true);
266 $output->writeln($formattedBlock);
267 if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
268 if (null !== $this->dispatcher) {
269 $event = new ConsoleErrorEvent($input, $output, $e);
270 $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
271
272 return $event->getExitCode();
273 }
274
275 return 1;
276 }
277
278 $command = $this->find($alternative);
279 } else {
280 if (null !== $this->dispatcher) {
281 $event = new ConsoleErrorEvent($input, $output, $e);
282 $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
283
284 if (0 === $event->getExitCode()) {
285 return 0;
286 }
287
288 $e = $event->getError();
289 }
290
291 try {
292 if ($e instanceof CommandNotFoundException && $namespace = $this->findNamespace($name)) {
293 $helper = new DescriptorHelper();
294 $helper->describe($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output, $this, [
295 'format' => 'txt',
296 'raw_text' => false,
297 'namespace' => $namespace,
298 'short' => false,
299 ]);
300
301 return isset($event) ? $event->getExitCode() : 1;
302 }
303
304 throw $e;
305 } catch (NamespaceNotFoundException) {
306 throw $e;
307 }
308 }
309 }
310
311 if ($command instanceof LazyCommand) {
312 $command = $command->getCommand();
313 }
314
315 $this->runningCommand = $command;
316 $exitCode = $this->doRunCommand($command, $input, $output);
317 $this->runningCommand = null;
318
319 return $exitCode;
320 }
321
322 public function reset(): void
323 {
324 }
325
326 public function setHelperSet(HelperSet $helperSet): void
327 {
328 $this->helperSet = $helperSet;
329 }
330
331 /**
332 * Get the helper set associated with the command.
333 */
334 public function getHelperSet(): HelperSet
335 {
336 return $this->helperSet ??= $this->getDefaultHelperSet();
337 }
338
339 public function setDefinition(InputDefinition $definition): void
340 {
341 $this->definition = $definition;
342 }
343
344 /**
345 * Gets the InputDefinition related to this Application.
346 */
347 public function getDefinition(): InputDefinition
348 {
349 $this->definition ??= $this->getDefaultInputDefinition();
350
351 if ($this->singleCommand) {
352 $inputDefinition = $this->definition;
353 $inputDefinition->setArguments();
354
355 return $inputDefinition;
356 }
357
358 return $this->definition;
359 }
360
361 /**
362 * Adds suggestions to $suggestions for the current completion input (e.g. option or argument).
363 */
364 public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
365 {
366 if (
367 CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType()
368 && 'command' === $input->getCompletionName()
369 ) {
370 foreach ($this->all() as $name => $command) {
371 // skip hidden commands and aliased commands as they already get added below
372 if ($command->isHidden() || $command->getName() !== $name) {
373 continue;
374 }
375 $suggestions->suggestValue(new Suggestion($command->getName(), $command->getDescription()));
376 foreach ($command->getAliases() as $name) {
377 $suggestions->suggestValue(new Suggestion($name, $command->getDescription()));
378 }
379 }
380
381 return;
382 }
383
384 if (CompletionInput::TYPE_OPTION_NAME === $input->getCompletionType()) {
385 $suggestions->suggestOptions($this->getDefinition()->getOptions());
386
387 return;
388 }
389 }
390
391 /**
392 * Gets the help message.
393 */
394 public function getHelp(): string
395 {
396 return $this->getLongVersion();
397 }
398
399 /**
400 * Gets whether to catch exceptions or not during commands execution.
401 */
402 public function areExceptionsCaught(): bool
403 {
404 return $this->catchExceptions;
405 }
406
407 /**
408 * Sets whether to catch exceptions or not during commands execution.
409 */
410 public function setCatchExceptions(bool $boolean): void
411 {
412 $this->catchExceptions = $boolean;
413 }
414
415 /**
416 * Sets whether to catch errors or not during commands execution.
417 */
418 public function setCatchErrors(bool $catchErrors = true): void
419 {
420 $this->catchErrors = $catchErrors;
421 }
422
423 /**
424 * Gets whether to automatically exit after a command execution or not.
425 */
426 public function isAutoExitEnabled(): bool
427 {
428 return $this->autoExit;
429 }
430
431 /**
432 * Sets whether to automatically exit after a command execution or not.
433 */
434 public function setAutoExit(bool $boolean): void
435 {
436 $this->autoExit = $boolean;
437 }
438
439 /**
440 * Gets the name of the application.
441 */
442 public function getName(): string
443 {
444 return $this->name;
445 }
446
447 /**
448 * Sets the application name.
449 */
450 public function setName(string $name): void
451 {
452 $this->name = $name;
453 }
454
455 /**
456 * Gets the application version.
457 */
458 public function getVersion(): string
459 {
460 return $this->version;
461 }
462
463 /**
464 * Sets the application version.
465 */
466 public function setVersion(string $version): void
467 {
468 $this->version = $version;
469 }
470
471 /**
472 * Returns the long version of the application.
473 */
474 public function getLongVersion(): string
475 {
476 if ('UNKNOWN' !== $this->getName()) {
477 if ('UNKNOWN' !== $this->getVersion()) {
478 return sprintf('%s <info>%s</info>', $this->getName(), $this->getVersion());
479 }
480
481 return $this->getName();
482 }
483
484 return 'Console Tool';
485 }
486
487 /**
488 * Registers a new command.
489 */
490 public function register(string $name): Command
491 {
492 return $this->add(new Command($name));
493 }
494
495 /**
496 * Adds an array of command objects.
497 *
498 * If a Command is not enabled it will not be added.
499 *
500 * @param Command[] $commands An array of commands
501 */
502 public function addCommands(array $commands): void
503 {
504 foreach ($commands as $command) {
505 $this->add($command);
506 }
507 }
508
509 /**
510 * Adds a command object.
511 *
512 * If a command with the same name already exists, it will be overridden.
513 * If the command is not enabled it will not be added.
514 */
515 public function add(Command $command): ?Command
516 {
517 $this->init();
518
519 $command->setApplication($this);
520
521 if (!$command->isEnabled()) {
522 $command->setApplication(null);
523
524 return null;
525 }
526
527 if (!$command instanceof LazyCommand) {
528 // Will throw if the command is not correctly initialized.
529 $command->getDefinition();
530 }
531
532 if (!$command->getName()) {
533 throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command)));
534 }
535
536 $this->commands[$command->getName()] = $command;
537
538 foreach ($command->getAliases() as $alias) {
539 $this->commands[$alias] = $command;
540 }
541
542 return $command;
543 }
544
545 /**
546 * Returns a registered command by name or alias.
547 *
548 * @throws CommandNotFoundException When given command name does not exist
549 */
550 public function get(string $name): Command
551 {
552 $this->init();
553
554 if (!$this->has($name)) {
555 throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
556 }
557
558 // When the command has a different name than the one used at the command loader level
559 if (!isset($this->commands[$name])) {
560 throw new CommandNotFoundException(sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name));
561 }
562
563 $command = $this->commands[$name];
564
565 if ($this->wantHelps) {
566 $this->wantHelps = false;
567
568 $helpCommand = $this->get('help');
569 $helpCommand->setCommand($command);
570
571 return $helpCommand;
572 }
573
574 return $command;
575 }
576
577 /**
578 * Returns true if the command exists, false otherwise.
579 */
580 public function has(string $name): bool
581 {
582 $this->init();
583
584 return isset($this->commands[$name]) || ($this->commandLoader?->has($name) && $this->add($this->commandLoader->get($name)));
585 }
586
587 /**
588 * Returns an array of all unique namespaces used by currently registered commands.
589 *
590 * It does not return the global namespace which always exists.
591 *
592 * @return string[]
593 */
594 public function getNamespaces(): array
595 {
596 $namespaces = [];
597 foreach ($this->all() as $command) {
598 if ($command->isHidden()) {
599 continue;
600 }
601
602 $namespaces[] = $this->extractAllNamespaces($command->getName());
603
604 foreach ($command->getAliases() as $alias) {
605 $namespaces[] = $this->extractAllNamespaces($alias);
606 }
607 }
608
609 return array_values(array_unique(array_filter(array_merge([], ...$namespaces))));
610 }
611
612 /**
613 * Finds a registered namespace by a name or an abbreviation.
614 *
615 * @throws NamespaceNotFoundException When namespace is incorrect or ambiguous
616 */
617 public function findNamespace(string $namespace): string
618 {
619 $allNamespaces = $this->getNamespaces();
620 $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $namespace))).'[^:]*';
621 $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);
622
623 if (!$namespaces) {
624 $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
625
626 if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
627 if (1 == \count($alternatives)) {
628 $message .= "\n\nDid you mean this?\n ";
629 } else {
630 $message .= "\n\nDid you mean one of these?\n ";
631 }
632
633 $message .= implode("\n ", $alternatives);
634 }
635
636 throw new NamespaceNotFoundException($message, $alternatives);
637 }
638
639 $exact = \in_array($namespace, $namespaces, true);
640 if (\count($namespaces) > 1 && !$exact) {
641 throw new NamespaceNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
642 }
643
644 return $exact ? $namespace : reset($namespaces);
645 }
646
647 /**
648 * Finds a command by name or alias.
649 *
650 * Contrary to get, this command tries to find the best
651 * match if you give it an abbreviation of a name or alias.
652 *
653 * @throws CommandNotFoundException When command name is incorrect or ambiguous
654 */
655 public function find(string $name): Command
656 {
657 $this->init();
658
659 $aliases = [];
660
661 foreach ($this->commands as $command) {
662 foreach ($command->getAliases() as $alias) {
663 if (!$this->has($alias)) {
664 $this->commands[$alias] = $command;
665 }
666 }
667 }
668
669 if ($this->has($name)) {
670 return $this->get($name);
671 }
672
673 $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
674 $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $name))).'[^:]*';
675 $commands = preg_grep('{^'.$expr.'}', $allCommands);
676
677 if (!$commands) {
678 $commands = preg_grep('{^'.$expr.'}i', $allCommands);
679 }
680
681 // if no commands matched or we just matched namespaces
682 if (!$commands || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) {
683 if (false !== $pos = strrpos($name, ':')) {
684 // check if a namespace exists and contains commands
685 $this->findNamespace(substr($name, 0, $pos));
686 }
687
688 $message = sprintf('Command "%s" is not defined.', $name);
689
690 if ($alternatives = $this->findAlternatives($name, $allCommands)) {
691 // remove hidden commands
692 $alternatives = array_filter($alternatives, fn ($name) => !$this->get($name)->isHidden());
693
694 if (1 == \count($alternatives)) {
695 $message .= "\n\nDid you mean this?\n ";
696 } else {
697 $message .= "\n\nDid you mean one of these?\n ";
698 }
699 $message .= implode("\n ", $alternatives);
700 }
701
702 throw new CommandNotFoundException($message, array_values($alternatives));
703 }
704
705 // filter out aliases for commands which are already on the list
706 if (\count($commands) > 1) {
707 $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands;
708 $commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) {
709 if (!$commandList[$nameOrAlias] instanceof Command) {
710 $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias);
711 }
712
713 $commandName = $commandList[$nameOrAlias]->getName();
714
715 $aliases[$nameOrAlias] = $commandName;
716
717 return $commandName === $nameOrAlias || !\in_array($commandName, $commands, true);
718 }));
719 }
720
721 if (\count($commands) > 1) {
722 $usableWidth = $this->terminal->getWidth() - 10;
723 $abbrevs = array_values($commands);
724 $maxLen = 0;
725 foreach ($abbrevs as $abbrev) {
726 $maxLen = max(Helper::width($abbrev), $maxLen);
727 }
728 $abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen, &$commands) {
729 if ($commandList[$cmd]->isHidden()) {
730 unset($commands[array_search($cmd, $commands)]);
731
732 return false;
733 }
734
735 $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();
736
737 return Helper::width($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
738 }, array_values($commands));
739
740 if (\count($commands) > 1) {
741 $suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs));
742
743 throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands));
744 }
745 }
746
747 $command = $this->get(reset($commands));
748
749 if ($command->isHidden()) {
750 throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
751 }
752
753 return $command;
754 }
755
756 /**
757 * Gets the commands (registered in the given namespace if provided).
758 *
759 * The array keys are the full names and the values the command instances.
760 *
761 * @return Command[]
762 */
763 public function all(?string $namespace = null): array
764 {
765 $this->init();
766
767 if (null === $namespace) {
768 if (!$this->commandLoader) {
769 return $this->commands;
770 }
771
772 $commands = $this->commands;
773 foreach ($this->commandLoader->getNames() as $name) {
774 if (!isset($commands[$name]) && $this->has($name)) {
775 $commands[$name] = $this->get($name);
776 }
777 }
778
779 return $commands;
780 }
781
782 $commands = [];
783 foreach ($this->commands as $name => $command) {
784 if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
785 $commands[$name] = $command;
786 }
787 }
788
789 if ($this->commandLoader) {
790 foreach ($this->commandLoader->getNames() as $name) {
791 if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) {
792 $commands[$name] = $this->get($name);
793 }
794 }
795 }
796
797 return $commands;
798 }
799
800 /**
801 * Returns an array of possible abbreviations given a set of names.
802 *
803 * @return string[][]
804 */
805 public static function getAbbreviations(array $names): array
806 {
807 $abbrevs = [];
808 foreach ($names as $name) {
809 for ($len = \strlen($name); $len > 0; --$len) {
810 $abbrev = substr($name, 0, $len);
811 $abbrevs[$abbrev][] = $name;
812 }
813 }
814
815 return $abbrevs;
816 }
817
818 public function renderThrowable(\Throwable $e, OutputInterface $output): void
819 {
820 $output->writeln('', OutputInterface::VERBOSITY_QUIET);
821
822 $this->doRenderThrowable($e, $output);
823
824 if (null !== $this->runningCommand) {
825 $output->writeln(sprintf('<info>%s</info>', OutputFormatter::escape(sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET);
826 $output->writeln('', OutputInterface::VERBOSITY_QUIET);
827 }
828 }
829
830 protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void
831 {
832 do {
833 $message = trim($e->getMessage());
834 if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
835 $class = get_debug_type($e);
836 $title = sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
837 $len = Helper::width($title);
838 } else {
839 $len = 0;
840 }
841
842 if (str_contains($message, "@anonymous\0")) {
843 $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $message);
844 }
845
846 $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX;
847 $lines = [];
848 foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) {
849 foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
850 // pre-format lines to get the right string length
851 $lineLength = Helper::width($line) + 4;
852 $lines[] = [$line, $lineLength];
853
854 $len = max($lineLength, $len);
855 }
856 }
857
858 $messages = [];
859 if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
860 $messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a')));
861 }
862 $messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
863 if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
864 $messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::width($title))));
865 }
866 foreach ($lines as $line) {
867 $messages[] = sprintf('<error> %s %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
868 }
869 $messages[] = $emptyLine;
870 $messages[] = '';
871
872 $output->writeln($messages, OutputInterface::VERBOSITY_QUIET);
873
874 if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
875 $output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET);
876
877 // exception related properties
878 $trace = $e->getTrace();
879
880 array_unshift($trace, [
881 'function' => '',
882 'file' => $e->getFile() ?: 'n/a',
883 'line' => $e->getLine() ?: 'n/a',
884 'args' => [],
885 ]);
886
887 for ($i = 0, $count = \count($trace); $i < $count; ++$i) {
888 $class = $trace[$i]['class'] ?? '';
889 $type = $trace[$i]['type'] ?? '';
890 $function = $trace[$i]['function'] ?? '';
891 $file = $trace[$i]['file'] ?? 'n/a';
892 $line = $trace[$i]['line'] ?? 'n/a';
893
894 $output->writeln(sprintf(' %s%s at <info>%s:%s</info>', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET);
895 }
896
897 $output->writeln('', OutputInterface::VERBOSITY_QUIET);
898 }
899 } while ($e = $e->getPrevious());
900 }
901
902 /**
903 * Configures the input and output instances based on the user arguments and options.
904 */
905 protected function configureIO(InputInterface $input, OutputInterface $output): void
906 {
907 if (true === $input->hasParameterOption(['--ansi'], true)) {
908 $output->setDecorated(true);
909 } elseif (true === $input->hasParameterOption(['--no-ansi'], true)) {
910 $output->setDecorated(false);
911 }
912
913 if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) {
914 $input->setInteractive(false);
915 }
916
917 switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
918 case -1:
919 $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
920 break;
921 case 1:
922 $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
923 break;
924 case 2:
925 $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
926 break;
927 case 3:
928 $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
929 break;
930 default:
931 $shellVerbosity = 0;
932 break;
933 }
934
935 if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
936 $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
937 $shellVerbosity = -1;
938 } else {
939 if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) {
940 $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
941 $shellVerbosity = 3;
942 } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) {
943 $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
944 $shellVerbosity = 2;
945 } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) {
946 $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
947 $shellVerbosity = 1;
948 }
949 }
950
951 if (-1 === $shellVerbosity) {
952 $input->setInteractive(false);
953 }
954
955 if (\function_exists('putenv')) {
956 @putenv('SHELL_VERBOSITY='.$shellVerbosity);
957 }
958 $_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
959 $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
960 }
961
962 /**
963 * Runs the current command.
964 *
965 * If an event dispatcher has been attached to the application,
966 * events are also dispatched during the life-cycle of the command.
967 *
968 * @return int 0 if everything went fine, or an error code
969 */
970 protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int
971 {
972 foreach ($command->getHelperSet() as $helper) {
973 if ($helper instanceof InputAwareInterface) {
974 $helper->setInput($input);
975 }
976 }
977
978 $commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];
979 if ($commandSignals || $this->dispatcher && $this->signalsToDispatchEvent) {
980 if (!$this->signalRegistry) {
981 throw new RuntimeException('Unable to subscribe to signal events. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
982 }
983
984 if (Terminal::hasSttyAvailable()) {
985 $sttyMode = shell_exec('stty -g');
986
987 foreach ([\SIGINT, \SIGQUIT, \SIGTERM] as $signal) {
988 $this->signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode));
989 }
990 }
991
992 if ($this->dispatcher) {
993 // We register application signals, so that we can dispatch the event
994 foreach ($this->signalsToDispatchEvent as $signal) {
995 $event = new ConsoleSignalEvent($command, $input, $output, $signal);
996
997 $this->signalRegistry->register($signal, function ($signal) use ($event, $command, $commandSignals) {
998 $this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL);
999 $exitCode = $event->getExitCode();
1000
1001 // If the command is signalable, we call the handleSignal() method
1002 if (\in_array($signal, $commandSignals, true)) {
1003 $exitCode = $command->handleSignal($signal, $exitCode);
1004 }
1005
1006 if (false !== $exitCode) {
1007 $event = new ConsoleTerminateEvent($command, $event->getInput(), $event->getOutput(), $exitCode, $signal);
1008 $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);
1009
1010 exit($event->getExitCode());
1011 }
1012 });
1013 }
1014
1015 // then we register command signals, but not if already handled after the dispatcher
1016 $commandSignals = array_diff($commandSignals, $this->signalsToDispatchEvent);
1017 }
1018
1019 foreach ($commandSignals as $signal) {
1020 $this->signalRegistry->register($signal, function (int $signal) use ($command): void {
1021 if (false !== $exitCode = $command->handleSignal($signal)) {
1022 exit($exitCode);
1023 }
1024 });
1025 }
1026 }
1027
1028 if (null === $this->dispatcher) {
1029 return $command->run($input, $output);
1030 }
1031
1032 // bind before the console.command event, so the listeners have access to input options/arguments
1033 try {
1034 $command->mergeApplicationDefinition();
1035 $input->bind($command->getDefinition());
1036 } catch (ExceptionInterface) {
1037 // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition
1038 }
1039
1040 $event = new ConsoleCommandEvent($command, $input, $output);
1041 $e = null;
1042
1043 try {
1044 $this->dispatcher->dispatch($event, ConsoleEvents::COMMAND);
1045
1046 if ($event->commandShouldRun()) {
1047 $exitCode = $command->run($input, $output);
1048 } else {
1049 $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
1050 }
1051 } catch (\Throwable $e) {
1052 $event = new ConsoleErrorEvent($input, $output, $e, $command);
1053 $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
1054 $e = $event->getError();
1055
1056 if (0 === $exitCode = $event->getExitCode()) {
1057 $e = null;
1058 }
1059 }
1060
1061 $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
1062 $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);
1063
1064 if (null !== $e) {
1065 throw $e;
1066 }
1067
1068 return $event->getExitCode();
1069 }
1070
1071 /**
1072 * Gets the name of the command based on input.
1073 */
1074 protected function getCommandName(InputInterface $input): ?string
1075 {
1076 return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument();
1077 }
1078
1079 /**
1080 * Gets the default input definition.
1081 */
1082 protected function getDefaultInputDefinition(): InputDefinition
1083 {
1084 return new InputDefinition([
1085 new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
1086 new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display help for the given command. When no command is given display help for the <info>'.$this->defaultCommand.'</info> command'),
1087 new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
1088 new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
1089 new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
1090 new InputOption('--ansi', '', InputOption::VALUE_NEGATABLE, 'Force (or disable --no-ansi) ANSI output', null),
1091 new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
1092 ]);
1093 }
1094
1095 /**
1096 * Gets the default commands that should always be available.
1097 *
1098 * @return Command[]
1099 */
1100 protected function getDefaultCommands(): array
1101 {
1102 return [new HelpCommand(), new ListCommand(), new CompleteCommand(), new DumpCompletionCommand()];
1103 }
1104
1105 /**
1106 * Gets the default helper set with the helpers that should always be available.
1107 */
1108 protected function getDefaultHelperSet(): HelperSet
1109 {
1110 return new HelperSet([
1111 new FormatterHelper(),
1112 new DebugFormatterHelper(),
1113 new ProcessHelper(),
1114 new QuestionHelper(),
1115 ]);
1116 }
1117
1118 /**
1119 * Returns abbreviated suggestions in string format.
1120 */
1121 private function getAbbreviationSuggestions(array $abbrevs): string
1122 {
1123 return ' '.implode("\n ", $abbrevs);
1124 }
1125
1126 /**
1127 * Returns the namespace part of the command name.
1128 *
1129 * This method is not part of public API and should not be used directly.
1130 */
1131 public function extractNamespace(string $name, ?int $limit = null): string
1132 {
1133 $parts = explode(':', $name, -1);
1134
1135 return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit));
1136 }
1137
1138 /**
1139 * Finds alternative of $name among $collection,
1140 * if nothing is found in $collection, try in $abbrevs.
1141 *
1142 * @return string[]
1143 */
1144 private function findAlternatives(string $name, iterable $collection): array
1145 {
1146 $threshold = 1e3;
1147 $alternatives = [];
1148
1149 $collectionParts = [];
1150 foreach ($collection as $item) {
1151 $collectionParts[$item] = explode(':', $item);
1152 }
1153
1154 foreach (explode(':', $name) as $i => $subname) {
1155 foreach ($collectionParts as $collectionName => $parts) {
1156 $exists = isset($alternatives[$collectionName]);
1157 if (!isset($parts[$i]) && $exists) {
1158 $alternatives[$collectionName] += $threshold;
1159 continue;
1160 } elseif (!isset($parts[$i])) {
1161 continue;
1162 }
1163
1164 $lev = levenshtein($subname, $parts[$i]);
1165 if ($lev <= \strlen($subname) / 3 || '' !== $subname && str_contains($parts[$i], $subname)) {
1166 $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
1167 } elseif ($exists) {
1168 $alternatives[$collectionName] += $threshold;
1169 }
1170 }
1171 }
1172
1173 foreach ($collection as $item) {
1174 $lev = levenshtein($name, $item);
1175 if ($lev <= \strlen($name) / 3 || str_contains($item, $name)) {
1176 $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
1177 }
1178 }
1179
1180 $alternatives = array_filter($alternatives, fn ($lev) => $lev < 2 * $threshold);
1181 ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE);
1182
1183 return array_keys($alternatives);
1184 }
1185
1186 /**
1187 * Sets the default Command name.
1188 *
1189 * @return $this
1190 */
1191 public function setDefaultCommand(string $commandName, bool $isSingleCommand = false): static
1192 {
1193 $this->defaultCommand = explode('|', ltrim($commandName, '|'))[0];
1194
1195 if ($isSingleCommand) {
1196 // Ensure the command exist
1197 $this->find($commandName);
1198
1199 $this->singleCommand = true;
1200 }
1201
1202 return $this;
1203 }
1204
1205 /**
1206 * @internal
1207 */
1208 public function isSingleCommand(): bool
1209 {
1210 return $this->singleCommand;
1211 }
1212
1213 private function splitStringByWidth(string $string, int $width): array
1214 {
1215 // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
1216 // additionally, array_slice() is not enough as some character has doubled width.
1217 // we need a function to split string not by character count but by string width
1218 if (false === $encoding = mb_detect_encoding($string, null, true)) {
1219 return str_split($string, $width);
1220 }
1221
1222 $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
1223 $lines = [];
1224 $line = '';
1225
1226 $offset = 0;
1227 while (preg_match('/.{1,10000}/u', $utf8String, $m, 0, $offset)) {
1228 $offset += \strlen($m[0]);
1229
1230 foreach (preg_split('//u', $m[0]) as $char) {
1231 // test if $char could be appended to current line
1232 if (mb_strwidth($line.$char, 'utf8') <= $width) {
1233 $line .= $char;
1234 continue;
1235 }
1236 // if not, push current line to array and make new line
1237 $lines[] = str_pad($line, $width);
1238 $line = $char;
1239 }
1240 }
1241
1242 $lines[] = \count($lines) ? str_pad($line, $width) : $line;
1243
1244 mb_convert_variables($encoding, 'utf8', $lines);
1245
1246 return $lines;
1247 }
1248
1249 /**
1250 * Returns all namespaces of the command name.
1251 *
1252 * @return string[]
1253 */
1254 private function extractAllNamespaces(string $name): array
1255 {
1256 // -1 as third argument is needed to skip the command short name when exploding
1257 $parts = explode(':', $name, -1);
1258 $namespaces = [];
1259
1260 foreach ($parts as $part) {
1261 if (\count($namespaces)) {
1262 $namespaces[] = end($namespaces).':'.$part;
1263 } else {
1264 $namespaces[] = $part;
1265 }
1266 }
1267
1268 return $namespaces;
1269 }
1270
1271 private function init(): void
1272 {
1273 if ($this->initialized) {
1274 return;
1275 }
1276 $this->initialized = true;
1277
1278 foreach ($this->getDefaultCommands() as $command) {
1279 $this->add($command);
1280 }
1281 }
1282}
diff --git a/vendor/symfony/console/Attribute/AsCommand.php b/vendor/symfony/console/Attribute/AsCommand.php
new file mode 100644
index 0000000..6066d7c
--- /dev/null
+++ b/vendor/symfony/console/Attribute/AsCommand.php
@@ -0,0 +1,45 @@
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\Attribute;
13
14/**
15 * Service tag to autoconfigure commands.
16 */
17#[\Attribute(\Attribute::TARGET_CLASS)]
18class AsCommand
19{
20 /**
21 * @param string $name The name of the command, used when calling it (i.e. "cache:clear")
22 * @param string|null $description The description of the command, displayed with the help page
23 * @param string[] $aliases The list of aliases of the command. The command will be executed when using one of them (i.e. "cache:clean")
24 * @param bool $hidden If true, the command won't be shown when listing all the available commands, but it can still be run as any other command
25 */
26 public function __construct(
27 public string $name,
28 public ?string $description = null,
29 array $aliases = [],
30 bool $hidden = false,
31 ) {
32 if (!$hidden && !$aliases) {
33 return;
34 }
35
36 $name = explode('|', $name);
37 $name = array_merge($name, $aliases);
38
39 if ($hidden && '' !== $name[0]) {
40 array_unshift($name, '');
41 }
42
43 $this->name = implode('|', $name);
44 }
45}
diff --git a/vendor/symfony/console/CHANGELOG.md b/vendor/symfony/console/CHANGELOG.md
new file mode 100644
index 0000000..25d7f71
--- /dev/null
+++ b/vendor/symfony/console/CHANGELOG.md
@@ -0,0 +1,274 @@
1CHANGELOG
2=========
3
47.1
5---
6
7 * Add `ArgvInput::getRawTokens()`
8
97.0
10---
11
12 * Add method `__toString()` to `InputInterface`
13 * Remove `Command::$defaultName` and `Command::$defaultDescription`, use the `AsCommand` attribute instead
14 * Require explicit argument when calling `*Command::setApplication()`, `*FormatterStyle::setForeground/setBackground()`, `Helper::setHelpSet()`, `Input*::setDefault()` and `Question::setAutocompleterCallback/setValidator()`
15 * Remove `StringInput::REGEX_STRING`
16
176.4
18---
19
20 * Add `SignalMap` to map signal value to its name
21 * Multi-line text in vertical tables is aligned properly
22 * The application can also catch errors with `Application::setCatchErrors(true)`
23 * Add `RunCommandMessage` and `RunCommandMessageHandler`
24 * Dispatch `ConsoleTerminateEvent` after an exit on signal handling and add `ConsoleTerminateEvent::getInterruptingSignal()`
25
266.3
27---
28
29 * Add support for choosing exit code while handling signal, or to not exit at all
30 * Add `ProgressBar::setPlaceholderFormatter` to set a placeholder attached to a instance, instead of being global.
31 * Add `ReStructuredTextDescriptor`
32
336.2
34---
35
36 * Improve truecolor terminal detection in some cases
37 * Add support for 256 color terminals (conversion from Ansi24 to Ansi8 if terminal is capable of it)
38 * Deprecate calling `*Command::setApplication()`, `*FormatterStyle::setForeground/setBackground()`, `Helper::setHelpSet()`, `Input*::setDefault()`, `Question::setAutocompleterCallback/setValidator()`without any arguments
39 * Change the signature of `OutputFormatterStyleInterface::setForeground/setBackground()` to `setForeground/setBackground(?string)`
40 * Change the signature of `HelperInterface::setHelperSet()` to `setHelperSet(?HelperSet)`
41
426.1
43---
44
45 * Add support to display table vertically when calling setVertical()
46 * Add method `__toString()` to `InputInterface`
47 * Added `OutputWrapper` to prevent truncated URL in `SymfonyStyle::createBlock`.
48 * Deprecate `Command::$defaultName` and `Command::$defaultDescription`, use the `AsCommand` attribute instead
49 * Add suggested values for arguments and options in input definition, for input completion
50 * Add `$resumeAt` parameter to `ProgressBar#start()`, so that one can easily 'resume' progress on longer tasks, and still get accurate `getEstimate()` and `getRemaining()` results.
51
526.0
53---
54
55 * `Command::setHidden()` has a default value (`true`) for `$hidden` parameter and is final
56 * Remove `Helper::strlen()`, use `Helper::width()` instead
57 * Remove `Helper::strlenWithoutDecoration()`, use `Helper::removeDecoration()` instead
58 * `AddConsoleCommandPass` can not be configured anymore
59 * Remove `HelperSet::setCommand()` and `getCommand()` without replacement
60
615.4
62---
63
64 * Add `TesterTrait::assertCommandIsSuccessful()` to test command
65 * Deprecate `HelperSet::setCommand()` and `getCommand()` without replacement
66
675.3
68---
69
70 * Add `GithubActionReporter` to render annotations in a Github Action
71 * Add `InputOption::VALUE_NEGATABLE` flag to handle `--foo`/`--no-foo` options
72 * Add the `Command::$defaultDescription` static property and the `description` attribute
73 on the `console.command` tag to allow the `list` command to instantiate commands lazily
74 * Add option `--short` to the `list` command
75 * Add support for bright colors
76 * Add `#[AsCommand]` attribute for declaring commands on PHP 8
77 * Add `Helper::width()` and `Helper::length()`
78 * The `--ansi` and `--no-ansi` options now default to `null`.
79
805.2.0
81-----
82
83 * Added `SingleCommandApplication::setAutoExit()` to allow testing via `CommandTester`
84 * added support for multiline responses to questions through `Question::setMultiline()`
85 and `Question::isMultiline()`
86 * Added `SignalRegistry` class to stack signals handlers
87 * Added support for signals:
88 * Added `Application::getSignalRegistry()` and `Application::setSignalsToDispatchEvent()` methods
89 * Added `SignalableCommandInterface` interface
90 * Added `TableCellStyle` class to customize table cell
91 * Removed `php ` prefix invocation from help messages.
92
935.1.0
94-----
95
96 * `Command::setHidden()` is final since Symfony 5.1
97 * Add `SingleCommandApplication`
98 * Add `Cursor` class
99
1005.0.0
101-----
102
103 * removed support for finding hidden commands using an abbreviation, use the full name instead
104 * removed `TableStyle::setCrossingChar()` method in favor of `TableStyle::setDefaultCrossingChar()`
105 * removed `TableStyle::setHorizontalBorderChar()` method in favor of `TableStyle::setDefaultCrossingChars()`
106 * removed `TableStyle::getHorizontalBorderChar()` method in favor of `TableStyle::getBorderChars()`
107 * removed `TableStyle::setVerticalBorderChar()` method in favor of `TableStyle::setVerticalBorderChars()`
108 * removed `TableStyle::getVerticalBorderChar()` method in favor of `TableStyle::getBorderChars()`
109 * removed support for returning `null` from `Command::execute()`, return `0` instead
110 * `ProcessHelper::run()` accepts only `array|Symfony\Component\Process\Process` for its `command` argument
111 * `Application::setDispatcher` accepts only `Symfony\Contracts\EventDispatcher\EventDispatcherInterface`
112 for its `dispatcher` argument
113 * renamed `Application::renderException()` and `Application::doRenderException()`
114 to `renderThrowable()` and `doRenderThrowable()` respectively.
115
1164.4.0
117-----
118
119 * deprecated finding hidden commands using an abbreviation, use the full name instead
120 * added `Question::setTrimmable` default to true to allow the answer to be trimmed
121 * added method `minSecondsBetweenRedraws()` and `maxSecondsBetweenRedraws()` on `ProgressBar`
122 * `Application` implements `ResetInterface`
123 * marked all dispatched event classes as `@final`
124 * added support for displaying table horizontally
125 * deprecated returning `null` from `Command::execute()`, return `0` instead
126 * Deprecated the `Application::renderException()` and `Application::doRenderException()` methods,
127 use `renderThrowable()` and `doRenderThrowable()` instead.
128 * added support for the `NO_COLOR` env var (https://no-color.org/)
129
1304.3.0
131-----
132
133 * added support for hyperlinks
134 * added `ProgressBar::iterate()` method that simplify updating the progress bar when iterating
135 * added `Question::setAutocompleterCallback()` to provide a callback function
136 that dynamically generates suggestions as the user types
137
1384.2.0
139-----
140
141 * allowed passing commands as `[$process, 'ENV_VAR' => 'value']` to
142 `ProcessHelper::run()` to pass environment variables
143 * deprecated passing a command as a string to `ProcessHelper::run()`,
144 pass it the command as an array of its arguments instead
145 * made the `ProcessHelper` class final
146 * added `WrappableOutputFormatterInterface::formatAndWrap()` (implemented in `OutputFormatter`)
147 * added `capture_stderr_separately` option to `CommandTester::execute()`
148
1494.1.0
150-----
151
152 * added option to run suggested command if command is not found and only 1 alternative is available
153 * added option to modify console output and print multiple modifiable sections
154 * added support for iterable messages in output `write` and `writeln` methods
155
1564.0.0
157-----
158
159 * `OutputFormatter` throws an exception when unknown options are used
160 * removed `QuestionHelper::setInputStream()/getInputStream()`
161 * removed `Application::getTerminalWidth()/getTerminalHeight()` and
162 `Application::setTerminalDimensions()/getTerminalDimensions()`
163 * removed `ConsoleExceptionEvent`
164 * removed `ConsoleEvents::EXCEPTION`
165
1663.4.0
167-----
168
169 * added `SHELL_VERBOSITY` env var to control verbosity
170 * added `CommandLoaderInterface`, `FactoryCommandLoader` and PSR-11
171 `ContainerCommandLoader` for commands lazy-loading
172 * added a case-insensitive command name matching fallback
173 * added static `Command::$defaultName/getDefaultName()`, allowing for
174 commands to be registered at compile time in the application command loader.
175 Setting the `$defaultName` property avoids the need for filling the `command`
176 attribute on the `console.command` tag when using `AddConsoleCommandPass`.
177
1783.3.0
179-----
180
181 * added `ExceptionListener`
182 * added `AddConsoleCommandPass` (originally in FrameworkBundle)
183 * [BC BREAK] `Input::getOption()` no longer returns the default value for options
184 with value optional explicitly passed empty
185 * added console.error event to catch exceptions thrown by other listeners
186 * deprecated console.exception event in favor of console.error
187 * added ability to handle `CommandNotFoundException` through the
188 `console.error` event
189 * deprecated default validation in `SymfonyQuestionHelper::ask`
190
1913.2.0
192------
193
194 * added `setInputs()` method to CommandTester for ease testing of commands expecting inputs
195 * added `setStream()` and `getStream()` methods to Input (implement StreamableInputInterface)
196 * added StreamableInputInterface
197 * added LockableTrait
198
1993.1.0
200-----
201
202 * added truncate method to FormatterHelper
203 * added setColumnWidth(s) method to Table
204
2052.8.3
206-----
207
208 * remove readline support from the question helper as it caused issues
209
2102.8.0
211-----
212
213 * use readline for user input in the question helper when available to allow
214 the use of arrow keys
215
2162.6.0
217-----
218
219 * added a Process helper
220 * added a DebugFormatter helper
221
2222.5.0
223-----
224
225 * deprecated the dialog helper (use the question helper instead)
226 * deprecated TableHelper in favor of Table
227 * deprecated ProgressHelper in favor of ProgressBar
228 * added ConsoleLogger
229 * added a question helper
230 * added a way to set the process name of a command
231 * added a way to set a default command instead of `ListCommand`
232
2332.4.0
234-----
235
236 * added a way to force terminal dimensions
237 * added a convenient method to detect verbosity level
238 * [BC BREAK] made descriptors use output instead of returning a string
239
2402.3.0
241-----
242
243 * added multiselect support to the select dialog helper
244 * added Table Helper for tabular data rendering
245 * added support for events in `Application`
246 * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()`
247 * added a way to set the progress bar progress via the `setCurrent` method
248 * added support for multiple InputOption shortcuts, written as `'-a|-b|-c'`
249 * added two additional verbosity levels, VERBOSITY_VERY_VERBOSE and VERBOSITY_DEBUG
250
2512.2.0
252-----
253
254 * added support for colorization on Windows via ConEmu
255 * add a method to Dialog Helper to ask for a question and hide the response
256 * added support for interactive selections in console (DialogHelper::select())
257 * added support for autocompletion as you type in Dialog Helper
258
2592.1.0
260-----
261
262 * added ConsoleOutputInterface
263 * added the possibility to disable a command (Command::isEnabled())
264 * added suggestions when a command does not exist
265 * added a --raw option to the list command
266 * added support for STDERR in the console output class (errors are now sent
267 to STDERR)
268 * made the defaults (helper set, commands, input definition) in Application
269 more easily customizable
270 * added support for the shell even if readline is not available
271 * added support for process isolation in Symfony shell via
272 `--process-isolation` switch
273 * added support for `--`, which disables options parsing after that point
274 (tokens will be parsed as arguments)
diff --git a/vendor/symfony/console/CI/GithubActionReporter.php b/vendor/symfony/console/CI/GithubActionReporter.php
new file mode 100644
index 0000000..2cae6fd
--- /dev/null
+++ b/vendor/symfony/console/CI/GithubActionReporter.php
@@ -0,0 +1,99 @@
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\CI;
13
14use Symfony\Component\Console\Output\OutputInterface;
15
16/**
17 * Utility class for Github actions.
18 *
19 * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
20 */
21class GithubActionReporter
22{
23 private OutputInterface $output;
24
25 /**
26 * @see https://github.com/actions/toolkit/blob/5e5e1b7aacba68a53836a34db4a288c3c1c1585b/packages/core/src/command.ts#L80-L85
27 */
28 private const ESCAPED_DATA = [
29 '%' => '%25',
30 "\r" => '%0D',
31 "\n" => '%0A',
32 ];
33
34 /**
35 * @see https://github.com/actions/toolkit/blob/5e5e1b7aacba68a53836a34db4a288c3c1c1585b/packages/core/src/command.ts#L87-L94
36 */
37 private const ESCAPED_PROPERTIES = [
38 '%' => '%25',
39 "\r" => '%0D',
40 "\n" => '%0A',
41 ':' => '%3A',
42 ',' => '%2C',
43 ];
44
45 public function __construct(OutputInterface $output)
46 {
47 $this->output = $output;
48 }
49
50 public static function isGithubActionEnvironment(): bool
51 {
52 return false !== getenv('GITHUB_ACTIONS');
53 }
54
55 /**
56 * Output an error using the Github annotations format.
57 *
58 * @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
59 */
60 public function error(string $message, ?string $file = null, ?int $line = null, ?int $col = null): void
61 {
62 $this->log('error', $message, $file, $line, $col);
63 }
64
65 /**
66 * Output a warning using the Github annotations format.
67 *
68 * @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message
69 */
70 public function warning(string $message, ?string $file = null, ?int $line = null, ?int $col = null): void
71 {
72 $this->log('warning', $message, $file, $line, $col);
73 }
74
75 /**
76 * Output a debug log using the Github annotations format.
77 *
78 * @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-a-debug-message
79 */
80 public function debug(string $message, ?string $file = null, ?int $line = null, ?int $col = null): void
81 {
82 $this->log('debug', $message, $file, $line, $col);
83 }
84
85 private function log(string $type, string $message, ?string $file = null, ?int $line = null, ?int $col = null): void
86 {
87 // Some values must be encoded.
88 $message = strtr($message, self::ESCAPED_DATA);
89
90 if (!$file) {
91 // No file provided, output the message solely:
92 $this->output->writeln(sprintf('::%s::%s', $type, $message));
93
94 return;
95 }
96
97 $this->output->writeln(sprintf('::%s file=%s,line=%s,col=%s::%s', $type, strtr($file, self::ESCAPED_PROPERTIES), strtr($line ?? 1, self::ESCAPED_PROPERTIES), strtr($col ?? 0, self::ESCAPED_PROPERTIES), $message));
98 }
99}
diff --git a/vendor/symfony/console/Color.php b/vendor/symfony/console/Color.php
new file mode 100644
index 0000000..60ed046
--- /dev/null
+++ b/vendor/symfony/console/Color.php
@@ -0,0 +1,133 @@
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;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15
16/**
17 * @author Fabien Potencier <fabien@symfony.com>
18 */
19final class Color
20{
21 private const COLORS = [
22 'black' => 0,
23 'red' => 1,
24 'green' => 2,
25 'yellow' => 3,
26 'blue' => 4,
27 'magenta' => 5,
28 'cyan' => 6,
29 'white' => 7,
30 'default' => 9,
31 ];
32
33 private const BRIGHT_COLORS = [
34 'gray' => 0,
35 'bright-red' => 1,
36 'bright-green' => 2,
37 'bright-yellow' => 3,
38 'bright-blue' => 4,
39 'bright-magenta' => 5,
40 'bright-cyan' => 6,
41 'bright-white' => 7,
42 ];
43
44 private const AVAILABLE_OPTIONS = [
45 'bold' => ['set' => 1, 'unset' => 22],
46 'underscore' => ['set' => 4, 'unset' => 24],
47 'blink' => ['set' => 5, 'unset' => 25],
48 'reverse' => ['set' => 7, 'unset' => 27],
49 'conceal' => ['set' => 8, 'unset' => 28],
50 ];
51
52 private string $foreground;
53 private string $background;
54 private array $options = [];
55
56 public function __construct(string $foreground = '', string $background = '', array $options = [])
57 {
58 $this->foreground = $this->parseColor($foreground);
59 $this->background = $this->parseColor($background, true);
60
61 foreach ($options as $option) {
62 if (!isset(self::AVAILABLE_OPTIONS[$option])) {
63 throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s).', $option, implode(', ', array_keys(self::AVAILABLE_OPTIONS))));
64 }
65
66 $this->options[$option] = self::AVAILABLE_OPTIONS[$option];
67 }
68 }
69
70 public function apply(string $text): string
71 {
72 return $this->set().$text.$this->unset();
73 }
74
75 public function set(): string
76 {
77 $setCodes = [];
78 if ('' !== $this->foreground) {
79 $setCodes[] = $this->foreground;
80 }
81 if ('' !== $this->background) {
82 $setCodes[] = $this->background;
83 }
84 foreach ($this->options as $option) {
85 $setCodes[] = $option['set'];
86 }
87 if (0 === \count($setCodes)) {
88 return '';
89 }
90
91 return sprintf("\033[%sm", implode(';', $setCodes));
92 }
93
94 public function unset(): string
95 {
96 $unsetCodes = [];
97 if ('' !== $this->foreground) {
98 $unsetCodes[] = 39;
99 }
100 if ('' !== $this->background) {
101 $unsetCodes[] = 49;
102 }
103 foreach ($this->options as $option) {
104 $unsetCodes[] = $option['unset'];
105 }
106 if (0 === \count($unsetCodes)) {
107 return '';
108 }
109
110 return sprintf("\033[%sm", implode(';', $unsetCodes));
111 }
112
113 private function parseColor(string $color, bool $background = false): string
114 {
115 if ('' === $color) {
116 return '';
117 }
118
119 if ('#' === $color[0]) {
120 return ($background ? '4' : '3').Terminal::getColorMode()->convertFromHexToAnsiColorCode($color);
121 }
122
123 if (isset(self::COLORS[$color])) {
124 return ($background ? '4' : '3').self::COLORS[$color];
125 }
126
127 if (isset(self::BRIGHT_COLORS[$color])) {
128 return ($background ? '10' : '9').self::BRIGHT_COLORS[$color];
129 }
130
131 throw new InvalidArgumentException(sprintf('Invalid "%s" color; expected one of (%s).', $color, implode(', ', array_merge(array_keys(self::COLORS), array_keys(self::BRIGHT_COLORS)))));
132 }
133}
diff --git a/vendor/symfony/console/Command/Command.php b/vendor/symfony/console/Command/Command.php
new file mode 100644
index 0000000..03da6db
--- /dev/null
+++ b/vendor/symfony/console/Command/Command.php
@@ -0,0 +1,664 @@
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\Command;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Attribute\AsCommand;
16use Symfony\Component\Console\Completion\CompletionInput;
17use Symfony\Component\Console\Completion\CompletionSuggestions;
18use Symfony\Component\Console\Completion\Suggestion;
19use Symfony\Component\Console\Exception\ExceptionInterface;
20use Symfony\Component\Console\Exception\InvalidArgumentException;
21use Symfony\Component\Console\Exception\LogicException;
22use Symfony\Component\Console\Helper\HelperInterface;
23use Symfony\Component\Console\Helper\HelperSet;
24use Symfony\Component\Console\Input\InputArgument;
25use Symfony\Component\Console\Input\InputDefinition;
26use Symfony\Component\Console\Input\InputInterface;
27use Symfony\Component\Console\Input\InputOption;
28use Symfony\Component\Console\Output\OutputInterface;
29
30/**
31 * Base class for all commands.
32 *
33 * @author Fabien Potencier <fabien@symfony.com>
34 */
35class Command
36{
37 // see https://tldp.org/LDP/abs/html/exitcodes.html
38 public const SUCCESS = 0;
39 public const FAILURE = 1;
40 public const INVALID = 2;
41
42 private ?Application $application = null;
43 private ?string $name = null;
44 private ?string $processTitle = null;
45 private array $aliases = [];
46 private InputDefinition $definition;
47 private bool $hidden = false;
48 private string $help = '';
49 private string $description = '';
50 private ?InputDefinition $fullDefinition = null;
51 private bool $ignoreValidationErrors = false;
52 private ?\Closure $code = null;
53 private array $synopsis = [];
54 private array $usages = [];
55 private ?HelperSet $helperSet = null;
56
57 public static function getDefaultName(): ?string
58 {
59 if ($attribute = (new \ReflectionClass(static::class))->getAttributes(AsCommand::class)) {
60 return $attribute[0]->newInstance()->name;
61 }
62
63 return null;
64 }
65
66 public static function getDefaultDescription(): ?string
67 {
68 if ($attribute = (new \ReflectionClass(static::class))->getAttributes(AsCommand::class)) {
69 return $attribute[0]->newInstance()->description;
70 }
71
72 return null;
73 }
74
75 /**
76 * @param string|null $name The name of the command; passing null means it must be set in configure()
77 *
78 * @throws LogicException When the command name is empty
79 */
80 public function __construct(?string $name = null)
81 {
82 $this->definition = new InputDefinition();
83
84 if (null === $name && null !== $name = static::getDefaultName()) {
85 $aliases = explode('|', $name);
86
87 if ('' === $name = array_shift($aliases)) {
88 $this->setHidden(true);
89 $name = array_shift($aliases);
90 }
91
92 $this->setAliases($aliases);
93 }
94
95 if (null !== $name) {
96 $this->setName($name);
97 }
98
99 if ('' === $this->description) {
100 $this->setDescription(static::getDefaultDescription() ?? '');
101 }
102
103 $this->configure();
104 }
105
106 /**
107 * Ignores validation errors.
108 *
109 * This is mainly useful for the help command.
110 */
111 public function ignoreValidationErrors(): void
112 {
113 $this->ignoreValidationErrors = true;
114 }
115
116 public function setApplication(?Application $application): void
117 {
118 $this->application = $application;
119 if ($application) {
120 $this->setHelperSet($application->getHelperSet());
121 } else {
122 $this->helperSet = null;
123 }
124
125 $this->fullDefinition = null;
126 }
127
128 public function setHelperSet(HelperSet $helperSet): void
129 {
130 $this->helperSet = $helperSet;
131 }
132
133 /**
134 * Gets the helper set.
135 */
136 public function getHelperSet(): ?HelperSet
137 {
138 return $this->helperSet;
139 }
140
141 /**
142 * Gets the application instance for this command.
143 */
144 public function getApplication(): ?Application
145 {
146 return $this->application;
147 }
148
149 /**
150 * Checks whether the command is enabled or not in the current environment.
151 *
152 * Override this to check for x or y and return false if the command cannot
153 * run properly under the current conditions.
154 */
155 public function isEnabled(): bool
156 {
157 return true;
158 }
159
160 /**
161 * Configures the current command.
162 *
163 * @return void
164 */
165 protected function configure()
166 {
167 }
168
169 /**
170 * Executes the current command.
171 *
172 * This method is not abstract because you can use this class
173 * as a concrete class. In this case, instead of defining the
174 * execute() method, you set the code to execute by passing
175 * a Closure to the setCode() method.
176 *
177 * @return int 0 if everything went fine, or an exit code
178 *
179 * @throws LogicException When this abstract method is not implemented
180 *
181 * @see setCode()
182 */
183 protected function execute(InputInterface $input, OutputInterface $output): int
184 {
185 throw new LogicException('You must override the execute() method in the concrete command class.');
186 }
187
188 /**
189 * Interacts with the user.
190 *
191 * This method is executed before the InputDefinition is validated.
192 * This means that this is the only place where the command can
193 * interactively ask for values of missing required arguments.
194 *
195 * @return void
196 */
197 protected function interact(InputInterface $input, OutputInterface $output)
198 {
199 }
200
201 /**
202 * Initializes the command after the input has been bound and before the input
203 * is validated.
204 *
205 * This is mainly useful when a lot of commands extends one main command
206 * where some things need to be initialized based on the input arguments and options.
207 *
208 * @see InputInterface::bind()
209 * @see InputInterface::validate()
210 *
211 * @return void
212 */
213 protected function initialize(InputInterface $input, OutputInterface $output)
214 {
215 }
216
217 /**
218 * Runs the command.
219 *
220 * The code to execute is either defined directly with the
221 * setCode() method or by overriding the execute() method
222 * in a sub-class.
223 *
224 * @return int The command exit code
225 *
226 * @throws ExceptionInterface When input binding fails. Bypass this by calling {@link ignoreValidationErrors()}.
227 *
228 * @see setCode()
229 * @see execute()
230 */
231 public function run(InputInterface $input, OutputInterface $output): int
232 {
233 // add the application arguments and options
234 $this->mergeApplicationDefinition();
235
236 // bind the input against the command specific arguments/options
237 try {
238 $input->bind($this->getDefinition());
239 } catch (ExceptionInterface $e) {
240 if (!$this->ignoreValidationErrors) {
241 throw $e;
242 }
243 }
244
245 $this->initialize($input, $output);
246
247 if (null !== $this->processTitle) {
248 if (\function_exists('cli_set_process_title')) {
249 if (!@cli_set_process_title($this->processTitle)) {
250 if ('Darwin' === \PHP_OS) {
251 $output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
252 } else {
253 cli_set_process_title($this->processTitle);
254 }
255 }
256 } elseif (\function_exists('setproctitle')) {
257 setproctitle($this->processTitle);
258 } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
259 $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
260 }
261 }
262
263 if ($input->isInteractive()) {
264 $this->interact($input, $output);
265 }
266
267 // The command name argument is often omitted when a command is executed directly with its run() method.
268 // It would fail the validation if we didn't make sure the command argument is present,
269 // since it's required by the application.
270 if ($input->hasArgument('command') && null === $input->getArgument('command')) {
271 $input->setArgument('command', $this->getName());
272 }
273
274 $input->validate();
275
276 if ($this->code) {
277 $statusCode = ($this->code)($input, $output);
278 } else {
279 $statusCode = $this->execute($input, $output);
280 }
281
282 return is_numeric($statusCode) ? (int) $statusCode : 0;
283 }
284
285 /**
286 * Supplies suggestions when resolving possible completion options for input (e.g. option or argument).
287 */
288 public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
289 {
290 $definition = $this->getDefinition();
291 if (CompletionInput::TYPE_OPTION_VALUE === $input->getCompletionType() && $definition->hasOption($input->getCompletionName())) {
292 $definition->getOption($input->getCompletionName())->complete($input, $suggestions);
293 } elseif (CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType() && $definition->hasArgument($input->getCompletionName())) {
294 $definition->getArgument($input->getCompletionName())->complete($input, $suggestions);
295 }
296 }
297
298 /**
299 * Sets the code to execute when running this command.
300 *
301 * If this method is used, it overrides the code defined
302 * in the execute() method.
303 *
304 * @param callable $code A callable(InputInterface $input, OutputInterface $output)
305 *
306 * @return $this
307 *
308 * @throws InvalidArgumentException
309 *
310 * @see execute()
311 */
312 public function setCode(callable $code): static
313 {
314 if ($code instanceof \Closure) {
315 $r = new \ReflectionFunction($code);
316 if (null === $r->getClosureThis()) {
317 set_error_handler(static function () {});
318 try {
319 if ($c = \Closure::bind($code, $this)) {
320 $code = $c;
321 }
322 } finally {
323 restore_error_handler();
324 }
325 }
326 } else {
327 $code = $code(...);
328 }
329
330 $this->code = $code;
331
332 return $this;
333 }
334
335 /**
336 * Merges the application definition with the command definition.
337 *
338 * This method is not part of public API and should not be used directly.
339 *
340 * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
341 *
342 * @internal
343 */
344 public function mergeApplicationDefinition(bool $mergeArgs = true): void
345 {
346 if (null === $this->application) {
347 return;
348 }
349
350 $this->fullDefinition = new InputDefinition();
351 $this->fullDefinition->setOptions($this->definition->getOptions());
352 $this->fullDefinition->addOptions($this->application->getDefinition()->getOptions());
353
354 if ($mergeArgs) {
355 $this->fullDefinition->setArguments($this->application->getDefinition()->getArguments());
356 $this->fullDefinition->addArguments($this->definition->getArguments());
357 } else {
358 $this->fullDefinition->setArguments($this->definition->getArguments());
359 }
360 }
361
362 /**
363 * Sets an array of argument and option instances.
364 *
365 * @return $this
366 */
367 public function setDefinition(array|InputDefinition $definition): static
368 {
369 if ($definition instanceof InputDefinition) {
370 $this->definition = $definition;
371 } else {
372 $this->definition->setDefinition($definition);
373 }
374
375 $this->fullDefinition = null;
376
377 return $this;
378 }
379
380 /**
381 * Gets the InputDefinition attached to this Command.
382 */
383 public function getDefinition(): InputDefinition
384 {
385 return $this->fullDefinition ?? $this->getNativeDefinition();
386 }
387
388 /**
389 * Gets the InputDefinition to be used to create representations of this Command.
390 *
391 * Can be overridden to provide the original command representation when it would otherwise
392 * be changed by merging with the application InputDefinition.
393 *
394 * This method is not part of public API and should not be used directly.
395 */
396 public function getNativeDefinition(): InputDefinition
397 {
398 return $this->definition ?? throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class));
399 }
400
401 /**
402 * Adds an argument.
403 *
404 * @param $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
405 * @param $default The default value (for InputArgument::OPTIONAL mode only)
406 * @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
407 *
408 * @return $this
409 *
410 * @throws InvalidArgumentException When argument mode is not valid
411 */
412 public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static
413 {
414 $this->definition->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues));
415 $this->fullDefinition?->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues));
416
417 return $this;
418 }
419
420 /**
421 * Adds an option.
422 *
423 * @param $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
424 * @param $mode The option mode: One of the InputOption::VALUE_* constants
425 * @param $default The default value (must be null for InputOption::VALUE_NONE)
426 * @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
427 *
428 * @return $this
429 *
430 * @throws InvalidArgumentException If option mode is invalid or incompatible
431 */
432 public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static
433 {
434 $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues));
435 $this->fullDefinition?->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues));
436
437 return $this;
438 }
439
440 /**
441 * Sets the name of the command.
442 *
443 * This method can set both the namespace and the name if
444 * you separate them by a colon (:)
445 *
446 * $command->setName('foo:bar');
447 *
448 * @return $this
449 *
450 * @throws InvalidArgumentException When the name is invalid
451 */
452 public function setName(string $name): static
453 {
454 $this->validateName($name);
455
456 $this->name = $name;
457
458 return $this;
459 }
460
461 /**
462 * Sets the process title of the command.
463 *
464 * This feature should be used only when creating a long process command,
465 * like a daemon.
466 *
467 * @return $this
468 */
469 public function setProcessTitle(string $title): static
470 {
471 $this->processTitle = $title;
472
473 return $this;
474 }
475
476 /**
477 * Returns the command name.
478 */
479 public function getName(): ?string
480 {
481 return $this->name;
482 }
483
484 /**
485 * @param bool $hidden Whether or not the command should be hidden from the list of commands
486 *
487 * @return $this
488 */
489 public function setHidden(bool $hidden = true): static
490 {
491 $this->hidden = $hidden;
492
493 return $this;
494 }
495
496 /**
497 * @return bool whether the command should be publicly shown or not
498 */
499 public function isHidden(): bool
500 {
501 return $this->hidden;
502 }
503
504 /**
505 * Sets the description for the command.
506 *
507 * @return $this
508 */
509 public function setDescription(string $description): static
510 {
511 $this->description = $description;
512
513 return $this;
514 }
515
516 /**
517 * Returns the description for the command.
518 */
519 public function getDescription(): string
520 {
521 return $this->description;
522 }
523
524 /**
525 * Sets the help for the command.
526 *
527 * @return $this
528 */
529 public function setHelp(string $help): static
530 {
531 $this->help = $help;
532
533 return $this;
534 }
535
536 /**
537 * Returns the help for the command.
538 */
539 public function getHelp(): string
540 {
541 return $this->help;
542 }
543
544 /**
545 * Returns the processed help for the command replacing the %command.name% and
546 * %command.full_name% patterns with the real values dynamically.
547 */
548 public function getProcessedHelp(): string
549 {
550 $name = $this->name;
551 $isSingleCommand = $this->application?->isSingleCommand();
552
553 $placeholders = [
554 '%command.name%',
555 '%command.full_name%',
556 ];
557 $replacements = [
558 $name,
559 $isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name,
560 ];
561
562 return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription());
563 }
564
565 /**
566 * Sets the aliases for the command.
567 *
568 * @param string[] $aliases An array of aliases for the command
569 *
570 * @return $this
571 *
572 * @throws InvalidArgumentException When an alias is invalid
573 */
574 public function setAliases(iterable $aliases): static
575 {
576 $list = [];
577
578 foreach ($aliases as $alias) {
579 $this->validateName($alias);
580 $list[] = $alias;
581 }
582
583 $this->aliases = \is_array($aliases) ? $aliases : $list;
584
585 return $this;
586 }
587
588 /**
589 * Returns the aliases for the command.
590 */
591 public function getAliases(): array
592 {
593 return $this->aliases;
594 }
595
596 /**
597 * Returns the synopsis for the command.
598 *
599 * @param bool $short Whether to show the short version of the synopsis (with options folded) or not
600 */
601 public function getSynopsis(bool $short = false): string
602 {
603 $key = $short ? 'short' : 'long';
604
605 if (!isset($this->synopsis[$key])) {
606 $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
607 }
608
609 return $this->synopsis[$key];
610 }
611
612 /**
613 * Add a command usage example, it'll be prefixed with the command name.
614 *
615 * @return $this
616 */
617 public function addUsage(string $usage): static
618 {
619 if (!str_starts_with($usage, $this->name)) {
620 $usage = sprintf('%s %s', $this->name, $usage);
621 }
622
623 $this->usages[] = $usage;
624
625 return $this;
626 }
627
628 /**
629 * Returns alternative usages of the command.
630 */
631 public function getUsages(): array
632 {
633 return $this->usages;
634 }
635
636 /**
637 * Gets a helper instance by name.
638 *
639 * @throws LogicException if no HelperSet is defined
640 * @throws InvalidArgumentException if the helper is not defined
641 */
642 public function getHelper(string $name): HelperInterface
643 {
644 if (null === $this->helperSet) {
645 throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name));
646 }
647
648 return $this->helperSet->get($name);
649 }
650
651 /**
652 * Validates a command name.
653 *
654 * It must be non-empty and parts can optionally be separated by ":".
655 *
656 * @throws InvalidArgumentException When the name is invalid
657 */
658 private function validateName(string $name): void
659 {
660 if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
661 throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
662 }
663 }
664}
diff --git a/vendor/symfony/console/Command/CompleteCommand.php b/vendor/symfony/console/Command/CompleteCommand.php
new file mode 100644
index 0000000..38aa737
--- /dev/null
+++ b/vendor/symfony/console/Command/CompleteCommand.php
@@ -0,0 +1,212 @@
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\Command;
13
14use Symfony\Component\Console\Attribute\AsCommand;
15use Symfony\Component\Console\Completion\CompletionInput;
16use Symfony\Component\Console\Completion\CompletionSuggestions;
17use Symfony\Component\Console\Completion\Output\BashCompletionOutput;
18use Symfony\Component\Console\Completion\Output\CompletionOutputInterface;
19use Symfony\Component\Console\Completion\Output\FishCompletionOutput;
20use Symfony\Component\Console\Completion\Output\ZshCompletionOutput;
21use Symfony\Component\Console\Exception\CommandNotFoundException;
22use Symfony\Component\Console\Exception\ExceptionInterface;
23use Symfony\Component\Console\Input\InputInterface;
24use Symfony\Component\Console\Input\InputOption;
25use Symfony\Component\Console\Output\OutputInterface;
26
27/**
28 * Responsible for providing the values to the shell completion.
29 *
30 * @author Wouter de Jong <wouter@wouterj.nl>
31 */
32#[AsCommand(name: '|_complete', description: 'Internal command to provide shell completion suggestions')]
33final class CompleteCommand extends Command
34{
35 public const COMPLETION_API_VERSION = '1';
36
37 private array $completionOutputs;
38 private bool $isDebug = false;
39
40 /**
41 * @param array<string, class-string<CompletionOutputInterface>> $completionOutputs A list of additional completion outputs, with shell name as key and FQCN as value
42 */
43 public function __construct(array $completionOutputs = [])
44 {
45 // must be set before the parent constructor, as the property value is used in configure()
46 $this->completionOutputs = $completionOutputs + [
47 'bash' => BashCompletionOutput::class,
48 'fish' => FishCompletionOutput::class,
49 'zsh' => ZshCompletionOutput::class,
50 ];
51
52 parent::__construct();
53 }
54
55 protected function configure(): void
56 {
57 $this
58 ->addOption('shell', 's', InputOption::VALUE_REQUIRED, 'The shell type ("'.implode('", "', array_keys($this->completionOutputs)).'")')
59 ->addOption('input', 'i', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'An array of input tokens (e.g. COMP_WORDS or argv)')
60 ->addOption('current', 'c', InputOption::VALUE_REQUIRED, 'The index of the "input" array that the cursor is in (e.g. COMP_CWORD)')
61 ->addOption('api-version', 'a', InputOption::VALUE_REQUIRED, 'The API version of the completion script')
62 ->addOption('symfony', 'S', InputOption::VALUE_REQUIRED, 'deprecated')
63 ;
64 }
65
66 protected function initialize(InputInterface $input, OutputInterface $output): void
67 {
68 $this->isDebug = filter_var(getenv('SYMFONY_COMPLETION_DEBUG'), \FILTER_VALIDATE_BOOL);
69 }
70
71 protected function execute(InputInterface $input, OutputInterface $output): int
72 {
73 try {
74 // "symfony" must be kept for compat with the shell scripts generated by Symfony Console 5.4 - 6.1
75 $version = $input->getOption('symfony') ? '1' : $input->getOption('api-version');
76 if ($version && version_compare($version, self::COMPLETION_API_VERSION, '<')) {
77 $message = sprintf('Completion script version is not supported ("%s" given, ">=%s" required).', $version, self::COMPLETION_API_VERSION);
78 $this->log($message);
79
80 $output->writeln($message.' Install the Symfony completion script again by using the "completion" command.');
81
82 return 126;
83 }
84
85 $shell = $input->getOption('shell');
86 if (!$shell) {
87 throw new \RuntimeException('The "--shell" option must be set.');
88 }
89
90 if (!$completionOutput = $this->completionOutputs[$shell] ?? false) {
91 throw new \RuntimeException(sprintf('Shell completion is not supported for your shell: "%s" (supported: "%s").', $shell, implode('", "', array_keys($this->completionOutputs))));
92 }
93
94 $completionInput = $this->createCompletionInput($input);
95 $suggestions = new CompletionSuggestions();
96
97 $this->log([
98 '',
99 '<comment>'.date('Y-m-d H:i:s').'</>',
100 '<info>Input:</> <comment>("|" indicates the cursor position)</>',
101 ' '.(string) $completionInput,
102 '<info>Command:</>',
103 ' '.(string) implode(' ', $_SERVER['argv']),
104 '<info>Messages:</>',
105 ]);
106
107 $command = $this->findCommand($completionInput, $output);
108 if (null === $command) {
109 $this->log(' No command found, completing using the Application class.');
110
111 $this->getApplication()->complete($completionInput, $suggestions);
112 } elseif (
113 $completionInput->mustSuggestArgumentValuesFor('command')
114 && $command->getName() !== $completionInput->getCompletionValue()
115 && !\in_array($completionInput->getCompletionValue(), $command->getAliases(), true)
116 ) {
117 $this->log(' No command found, completing using the Application class.');
118
119 // expand shortcut names ("cache:cl<TAB>") into their full name ("cache:clear")
120 $suggestions->suggestValues(array_filter(array_merge([$command->getName()], $command->getAliases())));
121 } else {
122 $command->mergeApplicationDefinition();
123 $completionInput->bind($command->getDefinition());
124
125 if (CompletionInput::TYPE_OPTION_NAME === $completionInput->getCompletionType()) {
126 $this->log(' Completing option names for the <comment>'.($command instanceof LazyCommand ? $command->getCommand() : $command)::class.'</> command.');
127
128 $suggestions->suggestOptions($command->getDefinition()->getOptions());
129 } else {
130 $this->log([
131 ' Completing using the <comment>'.($command instanceof LazyCommand ? $command->getCommand() : $command)::class.'</> class.',
132 ' Completing <comment>'.$completionInput->getCompletionType().'</> for <comment>'.$completionInput->getCompletionName().'</>',
133 ]);
134 if (null !== $compval = $completionInput->getCompletionValue()) {
135 $this->log(' Current value: <comment>'.$compval.'</>');
136 }
137
138 $command->complete($completionInput, $suggestions);
139 }
140 }
141
142 /** @var CompletionOutputInterface $completionOutput */
143 $completionOutput = new $completionOutput();
144
145 $this->log('<info>Suggestions:</>');
146 if ($options = $suggestions->getOptionSuggestions()) {
147 $this->log(' --'.implode(' --', array_map(fn ($o) => $o->getName(), $options)));
148 } elseif ($values = $suggestions->getValueSuggestions()) {
149 $this->log(' '.implode(' ', $values));
150 } else {
151 $this->log(' <comment>No suggestions were provided</>');
152 }
153
154 $completionOutput->write($suggestions, $output);
155 } catch (\Throwable $e) {
156 $this->log([
157 '<error>Error!</error>',
158 (string) $e,
159 ]);
160
161 if ($output->isDebug()) {
162 throw $e;
163 }
164
165 return 2;
166 }
167
168 return 0;
169 }
170
171 private function createCompletionInput(InputInterface $input): CompletionInput
172 {
173 $currentIndex = $input->getOption('current');
174 if (!$currentIndex || !ctype_digit($currentIndex)) {
175 throw new \RuntimeException('The "--current" option must be set and it must be an integer.');
176 }
177
178 $completionInput = CompletionInput::fromTokens($input->getOption('input'), (int) $currentIndex);
179
180 try {
181 $completionInput->bind($this->getApplication()->getDefinition());
182 } catch (ExceptionInterface) {
183 }
184
185 return $completionInput;
186 }
187
188 private function findCommand(CompletionInput $completionInput, OutputInterface $output): ?Command
189 {
190 try {
191 $inputName = $completionInput->getFirstArgument();
192 if (null === $inputName) {
193 return null;
194 }
195
196 return $this->getApplication()->find($inputName);
197 } catch (CommandNotFoundException) {
198 }
199
200 return null;
201 }
202
203 private function log($messages): void
204 {
205 if (!$this->isDebug) {
206 return;
207 }
208
209 $commandName = basename($_SERVER['argv'][0]);
210 file_put_contents(sys_get_temp_dir().'/sf_'.$commandName.'.log', implode(\PHP_EOL, (array) $messages).\PHP_EOL, \FILE_APPEND);
211 }
212}
diff --git a/vendor/symfony/console/Command/DumpCompletionCommand.php b/vendor/symfony/console/Command/DumpCompletionCommand.php
new file mode 100644
index 0000000..be6f545
--- /dev/null
+++ b/vendor/symfony/console/Command/DumpCompletionCommand.php
@@ -0,0 +1,151 @@
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\Command;
13
14use Symfony\Component\Console\Attribute\AsCommand;
15use Symfony\Component\Console\Input\InputArgument;
16use Symfony\Component\Console\Input\InputInterface;
17use Symfony\Component\Console\Input\InputOption;
18use Symfony\Component\Console\Output\ConsoleOutputInterface;
19use Symfony\Component\Console\Output\OutputInterface;
20use Symfony\Component\Process\Process;
21
22/**
23 * Dumps the completion script for the current shell.
24 *
25 * @author Wouter de Jong <wouter@wouterj.nl>
26 */
27#[AsCommand(name: 'completion', description: 'Dump the shell completion script')]
28final class DumpCompletionCommand extends Command
29{
30 private array $supportedShells;
31
32 protected function configure(): void
33 {
34 $fullCommand = $_SERVER['PHP_SELF'];
35 $commandName = basename($fullCommand);
36 $fullCommand = @realpath($fullCommand) ?: $fullCommand;
37
38 $shell = $this->guessShell();
39 [$rcFile, $completionFile] = match ($shell) {
40 'fish' => ['~/.config/fish/config.fish', "/etc/fish/completions/$commandName.fish"],
41 'zsh' => ['~/.zshrc', '$fpath[1]/_'.$commandName],
42 default => ['~/.bashrc', "/etc/bash_completion.d/$commandName"],
43 };
44
45 $supportedShells = implode(', ', $this->getSupportedShells());
46
47 $this
48 ->setHelp(<<<EOH
49The <info>%command.name%</> command dumps the shell completion script required
50to use shell autocompletion (currently, {$supportedShells} completion are supported).
51
52<comment>Static installation
53-------------------</>
54
55Dump the script to a global completion file and restart your shell:
56
57 <info>%command.full_name% {$shell} | sudo tee {$completionFile}</>
58
59Or dump the script to a local file and source it:
60
61 <info>%command.full_name% {$shell} > completion.sh</>
62
63 <comment># source the file whenever you use the project</>
64 <info>source completion.sh</>
65
66 <comment># or add this line at the end of your "{$rcFile}" file:</>
67 <info>source /path/to/completion.sh</>
68
69<comment>Dynamic installation
70--------------------</>
71
72Add this to the end of your shell configuration file (e.g. <info>"{$rcFile}"</>):
73
74 <info>eval "$({$fullCommand} completion {$shell})"</>
75EOH
76 )
77 ->addArgument('shell', InputArgument::OPTIONAL, 'The shell type (e.g. "bash"), the value of the "$SHELL" env var will be used if this is not given', null, $this->getSupportedShells(...))
78 ->addOption('debug', null, InputOption::VALUE_NONE, 'Tail the completion debug log')
79 ;
80 }
81
82 protected function execute(InputInterface $input, OutputInterface $output): int
83 {
84 $commandName = basename($_SERVER['argv'][0]);
85
86 if ($input->getOption('debug')) {
87 $this->tailDebugLog($commandName, $output);
88
89 return 0;
90 }
91
92 $shell = $input->getArgument('shell') ?? self::guessShell();
93 $completionFile = __DIR__.'/../Resources/completion.'.$shell;
94 if (!file_exists($completionFile)) {
95 $supportedShells = $this->getSupportedShells();
96
97 if ($output instanceof ConsoleOutputInterface) {
98 $output = $output->getErrorOutput();
99 }
100 if ($shell) {
101 $output->writeln(sprintf('<error>Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").</>', $shell, implode('", "', $supportedShells)));
102 } else {
103 $output->writeln(sprintf('<error>Shell not detected, Symfony shell completion only supports "%s").</>', implode('", "', $supportedShells)));
104 }
105
106 return 2;
107 }
108
109 $output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, CompleteCommand::COMPLETION_API_VERSION], file_get_contents($completionFile)));
110
111 return 0;
112 }
113
114 private static function guessShell(): string
115 {
116 return basename($_SERVER['SHELL'] ?? '');
117 }
118
119 private function tailDebugLog(string $commandName, OutputInterface $output): void
120 {
121 $debugFile = sys_get_temp_dir().'/sf_'.$commandName.'.log';
122 if (!file_exists($debugFile)) {
123 touch($debugFile);
124 }
125 $process = new Process(['tail', '-f', $debugFile], null, null, null, 0);
126 $process->run(function (string $type, string $line) use ($output): void {
127 $output->write($line);
128 });
129 }
130
131 /**
132 * @return string[]
133 */
134 private function getSupportedShells(): array
135 {
136 if (isset($this->supportedShells)) {
137 return $this->supportedShells;
138 }
139
140 $shells = [];
141
142 foreach (new \DirectoryIterator(__DIR__.'/../Resources/') as $file) {
143 if (str_starts_with($file->getBasename(), 'completion.') && $file->isFile()) {
144 $shells[] = $file->getExtension();
145 }
146 }
147 sort($shells);
148
149 return $this->supportedShells = $shells;
150 }
151}
diff --git a/vendor/symfony/console/Command/HelpCommand.php b/vendor/symfony/console/Command/HelpCommand.php
new file mode 100644
index 0000000..a2a72da
--- /dev/null
+++ b/vendor/symfony/console/Command/HelpCommand.php
@@ -0,0 +1,76 @@
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\Command;
13
14use Symfony\Component\Console\Descriptor\ApplicationDescription;
15use Symfony\Component\Console\Helper\DescriptorHelper;
16use Symfony\Component\Console\Input\InputArgument;
17use Symfony\Component\Console\Input\InputInterface;
18use Symfony\Component\Console\Input\InputOption;
19use Symfony\Component\Console\Output\OutputInterface;
20
21/**
22 * HelpCommand displays the help for a given command.
23 *
24 * @author Fabien Potencier <fabien@symfony.com>
25 */
26class HelpCommand extends Command
27{
28 private Command $command;
29
30 protected function configure(): void
31 {
32 $this->ignoreValidationErrors();
33
34 $this
35 ->setName('help')
36 ->setDefinition([
37 new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help', fn () => array_keys((new ApplicationDescription($this->getApplication()))->getCommands())),
38 new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', fn () => (new DescriptorHelper())->getFormats()),
39 new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
40 ])
41 ->setDescription('Display help for a command')
42 ->setHelp(<<<'EOF'
43The <info>%command.name%</info> command displays help for a given command:
44
45 <info>%command.full_name% list</info>
46
47You can also output the help in other formats by using the <comment>--format</comment> option:
48
49 <info>%command.full_name% --format=xml list</info>
50
51To display the list of available commands, please use the <info>list</info> command.
52EOF
53 )
54 ;
55 }
56
57 public function setCommand(Command $command): void
58 {
59 $this->command = $command;
60 }
61
62 protected function execute(InputInterface $input, OutputInterface $output): int
63 {
64 $this->command ??= $this->getApplication()->find($input->getArgument('command_name'));
65
66 $helper = new DescriptorHelper();
67 $helper->describe($output, $this->command, [
68 'format' => $input->getOption('format'),
69 'raw_text' => $input->getOption('raw'),
70 ]);
71
72 unset($this->command);
73
74 return 0;
75 }
76}
diff --git a/vendor/symfony/console/Command/LazyCommand.php b/vendor/symfony/console/Command/LazyCommand.php
new file mode 100644
index 0000000..fd2c300
--- /dev/null
+++ b/vendor/symfony/console/Command/LazyCommand.php
@@ -0,0 +1,206 @@
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\Command;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Completion\CompletionInput;
16use Symfony\Component\Console\Completion\CompletionSuggestions;
17use Symfony\Component\Console\Completion\Suggestion;
18use Symfony\Component\Console\Helper\HelperInterface;
19use Symfony\Component\Console\Helper\HelperSet;
20use Symfony\Component\Console\Input\InputDefinition;
21use Symfony\Component\Console\Input\InputInterface;
22use Symfony\Component\Console\Output\OutputInterface;
23
24/**
25 * @author Nicolas Grekas <p@tchwork.com>
26 */
27final class LazyCommand extends Command
28{
29 private \Closure|Command $command;
30
31 public function __construct(
32 string $name,
33 array $aliases,
34 string $description,
35 bool $isHidden,
36 \Closure $commandFactory,
37 private ?bool $isEnabled = true,
38 ) {
39 $this->setName($name)
40 ->setAliases($aliases)
41 ->setHidden($isHidden)
42 ->setDescription($description);
43
44 $this->command = $commandFactory;
45 }
46
47 public function ignoreValidationErrors(): void
48 {
49 $this->getCommand()->ignoreValidationErrors();
50 }
51
52 public function setApplication(?Application $application): void
53 {
54 if ($this->command instanceof parent) {
55 $this->command->setApplication($application);
56 }
57
58 parent::setApplication($application);
59 }
60
61 public function setHelperSet(HelperSet $helperSet): void
62 {
63 if ($this->command instanceof parent) {
64 $this->command->setHelperSet($helperSet);
65 }
66
67 parent::setHelperSet($helperSet);
68 }
69
70 public function isEnabled(): bool
71 {
72 return $this->isEnabled ?? $this->getCommand()->isEnabled();
73 }
74
75 public function run(InputInterface $input, OutputInterface $output): int
76 {
77 return $this->getCommand()->run($input, $output);
78 }
79
80 public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
81 {
82 $this->getCommand()->complete($input, $suggestions);
83 }
84
85 public function setCode(callable $code): static
86 {
87 $this->getCommand()->setCode($code);
88
89 return $this;
90 }
91
92 /**
93 * @internal
94 */
95 public function mergeApplicationDefinition(bool $mergeArgs = true): void
96 {
97 $this->getCommand()->mergeApplicationDefinition($mergeArgs);
98 }
99
100 public function setDefinition(array|InputDefinition $definition): static
101 {
102 $this->getCommand()->setDefinition($definition);
103
104 return $this;
105 }
106
107 public function getDefinition(): InputDefinition
108 {
109 return $this->getCommand()->getDefinition();
110 }
111
112 public function getNativeDefinition(): InputDefinition
113 {
114 return $this->getCommand()->getNativeDefinition();
115 }
116
117 /**
118 * @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
119 */
120 public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static
121 {
122 $this->getCommand()->addArgument($name, $mode, $description, $default, $suggestedValues);
123
124 return $this;
125 }
126
127 /**
128 * @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
129 */
130 public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static
131 {
132 $this->getCommand()->addOption($name, $shortcut, $mode, $description, $default, $suggestedValues);
133
134 return $this;
135 }
136
137 public function setProcessTitle(string $title): static
138 {
139 $this->getCommand()->setProcessTitle($title);
140
141 return $this;
142 }
143
144 public function setHelp(string $help): static
145 {
146 $this->getCommand()->setHelp($help);
147
148 return $this;
149 }
150
151 public function getHelp(): string
152 {
153 return $this->getCommand()->getHelp();
154 }
155
156 public function getProcessedHelp(): string
157 {
158 return $this->getCommand()->getProcessedHelp();
159 }
160
161 public function getSynopsis(bool $short = false): string
162 {
163 return $this->getCommand()->getSynopsis($short);
164 }
165
166 public function addUsage(string $usage): static
167 {
168 $this->getCommand()->addUsage($usage);
169
170 return $this;
171 }
172
173 public function getUsages(): array
174 {
175 return $this->getCommand()->getUsages();
176 }
177
178 public function getHelper(string $name): HelperInterface
179 {
180 return $this->getCommand()->getHelper($name);
181 }
182
183 public function getCommand(): parent
184 {
185 if (!$this->command instanceof \Closure) {
186 return $this->command;
187 }
188
189 $command = $this->command = ($this->command)();
190 $command->setApplication($this->getApplication());
191
192 if (null !== $this->getHelperSet()) {
193 $command->setHelperSet($this->getHelperSet());
194 }
195
196 $command->setName($this->getName())
197 ->setAliases($this->getAliases())
198 ->setHidden($this->isHidden())
199 ->setDescription($this->getDescription());
200
201 // Will throw if the command is not correctly initialized.
202 $command->getDefinition();
203
204 return $command;
205 }
206}
diff --git a/vendor/symfony/console/Command/ListCommand.php b/vendor/symfony/console/Command/ListCommand.php
new file mode 100644
index 0000000..61b4b1b
--- /dev/null
+++ b/vendor/symfony/console/Command/ListCommand.php
@@ -0,0 +1,72 @@
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\Command;
13
14use Symfony\Component\Console\Descriptor\ApplicationDescription;
15use Symfony\Component\Console\Helper\DescriptorHelper;
16use Symfony\Component\Console\Input\InputArgument;
17use Symfony\Component\Console\Input\InputInterface;
18use Symfony\Component\Console\Input\InputOption;
19use Symfony\Component\Console\Output\OutputInterface;
20
21/**
22 * ListCommand displays the list of all available commands for the application.
23 *
24 * @author Fabien Potencier <fabien@symfony.com>
25 */
26class ListCommand extends Command
27{
28 protected function configure(): void
29 {
30 $this
31 ->setName('list')
32 ->setDefinition([
33 new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name', null, fn () => array_keys((new ApplicationDescription($this->getApplication()))->getNamespaces())),
34 new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
35 new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', fn () => (new DescriptorHelper())->getFormats()),
36 new InputOption('short', null, InputOption::VALUE_NONE, 'To skip describing commands\' arguments'),
37 ])
38 ->setDescription('List commands')
39 ->setHelp(<<<'EOF'
40The <info>%command.name%</info> command lists all commands:
41
42 <info>%command.full_name%</info>
43
44You can also display the commands for a specific namespace:
45
46 <info>%command.full_name% test</info>
47
48You can also output the information in other formats by using the <comment>--format</comment> option:
49
50 <info>%command.full_name% --format=xml</info>
51
52It's also possible to get raw list of commands (useful for embedding command runner):
53
54 <info>%command.full_name% --raw</info>
55EOF
56 )
57 ;
58 }
59
60 protected function execute(InputInterface $input, OutputInterface $output): int
61 {
62 $helper = new DescriptorHelper();
63 $helper->describe($output, $this->getApplication(), [
64 'format' => $input->getOption('format'),
65 'raw_text' => $input->getOption('raw'),
66 'namespace' => $input->getArgument('namespace'),
67 'short' => $input->getOption('short'),
68 ]);
69
70 return 0;
71 }
72}
diff --git a/vendor/symfony/console/Command/LockableTrait.php b/vendor/symfony/console/Command/LockableTrait.php
new file mode 100644
index 0000000..f0001cc
--- /dev/null
+++ b/vendor/symfony/console/Command/LockableTrait.php
@@ -0,0 +1,74 @@
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\Command;
13
14use Symfony\Component\Console\Exception\LogicException;
15use Symfony\Component\Lock\LockFactory;
16use Symfony\Component\Lock\LockInterface;
17use Symfony\Component\Lock\Store\FlockStore;
18use Symfony\Component\Lock\Store\SemaphoreStore;
19
20/**
21 * Basic lock feature for commands.
22 *
23 * @author Geoffrey Brier <geoffrey.brier@gmail.com>
24 */
25trait LockableTrait
26{
27 private ?LockInterface $lock = null;
28
29 private ?LockFactory $lockFactory = null;
30
31 /**
32 * Locks a command.
33 */
34 private function lock(?string $name = null, bool $blocking = false): bool
35 {
36 if (!class_exists(SemaphoreStore::class)) {
37 throw new LogicException('To enable the locking feature you must install the symfony/lock component. Try running "composer require symfony/lock".');
38 }
39
40 if (null !== $this->lock) {
41 throw new LogicException('A lock is already in place.');
42 }
43
44 if (null === $this->lockFactory) {
45 if (SemaphoreStore::isSupported()) {
46 $store = new SemaphoreStore();
47 } else {
48 $store = new FlockStore();
49 }
50
51 $this->lockFactory = (new LockFactory($store));
52 }
53
54 $this->lock = $this->lockFactory->createLock($name ?: $this->getName());
55 if (!$this->lock->acquire($blocking)) {
56 $this->lock = null;
57
58 return false;
59 }
60
61 return true;
62 }
63
64 /**
65 * Releases the command lock if there is one.
66 */
67 private function release(): void
68 {
69 if ($this->lock) {
70 $this->lock->release();
71 $this->lock = null;
72 }
73 }
74}
diff --git a/vendor/symfony/console/Command/SignalableCommandInterface.php b/vendor/symfony/console/Command/SignalableCommandInterface.php
new file mode 100644
index 0000000..40b301d
--- /dev/null
+++ b/vendor/symfony/console/Command/SignalableCommandInterface.php
@@ -0,0 +1,32 @@
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\Command;
13
14/**
15 * Interface for command reacting to signal.
16 *
17 * @author Grégoire Pineau <lyrixx@lyrix.info>
18 */
19interface SignalableCommandInterface
20{
21 /**
22 * Returns the list of signals to subscribe.
23 */
24 public function getSubscribedSignals(): array;
25
26 /**
27 * The method will be called when the application is signaled.
28 *
29 * @return int|false The exit code to return or false to continue the normal execution
30 */
31 public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false;
32}
diff --git a/vendor/symfony/console/Command/TraceableCommand.php b/vendor/symfony/console/Command/TraceableCommand.php
new file mode 100644
index 0000000..9ffb68d
--- /dev/null
+++ b/vendor/symfony/console/Command/TraceableCommand.php
@@ -0,0 +1,356 @@
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\Command;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Completion\CompletionInput;
16use Symfony\Component\Console\Completion\CompletionSuggestions;
17use Symfony\Component\Console\Helper\HelperInterface;
18use Symfony\Component\Console\Helper\HelperSet;
19use Symfony\Component\Console\Input\InputDefinition;
20use Symfony\Component\Console\Input\InputInterface;
21use Symfony\Component\Console\Output\ConsoleOutputInterface;
22use Symfony\Component\Console\Output\OutputInterface;
23use Symfony\Component\Stopwatch\Stopwatch;
24
25/**
26 * @internal
27 *
28 * @author Jules Pietri <jules@heahprod.com>
29 */
30final class TraceableCommand extends Command implements SignalableCommandInterface
31{
32 public readonly Command $command;
33 public int $exitCode;
34 public ?int $interruptedBySignal = null;
35 public bool $ignoreValidation;
36 public bool $isInteractive = false;
37 public string $duration = 'n/a';
38 public string $maxMemoryUsage = 'n/a';
39 public InputInterface $input;
40 public OutputInterface $output;
41 /** @var array<string, mixed> */
42 public array $arguments;
43 /** @var array<string, mixed> */
44 public array $options;
45 /** @var array<string, mixed> */
46 public array $interactiveInputs = [];
47 public array $handledSignals = [];
48
49 public function __construct(
50 Command $command,
51 private readonly Stopwatch $stopwatch,
52 ) {
53 if ($command instanceof LazyCommand) {
54 $command = $command->getCommand();
55 }
56
57 $this->command = $command;
58
59 // prevent call to self::getDefaultDescription()
60 $this->setDescription($command->getDescription());
61
62 parent::__construct($command->getName());
63
64 // init below enables calling {@see parent::run()}
65 [$code, $processTitle, $ignoreValidationErrors] = \Closure::bind(function () {
66 return [$this->code, $this->processTitle, $this->ignoreValidationErrors];
67 }, $command, Command::class)();
68
69 if (\is_callable($code)) {
70 $this->setCode($code);
71 }
72
73 if ($processTitle) {
74 parent::setProcessTitle($processTitle);
75 }
76
77 if ($ignoreValidationErrors) {
78 parent::ignoreValidationErrors();
79 }
80
81 $this->ignoreValidation = $ignoreValidationErrors;
82 }
83
84 public function __call(string $name, array $arguments): mixed
85 {
86 return $this->command->{$name}(...$arguments);
87 }
88
89 public function getSubscribedSignals(): array
90 {
91 return $this->command instanceof SignalableCommandInterface ? $this->command->getSubscribedSignals() : [];
92 }
93
94 public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false
95 {
96 if (!$this->command instanceof SignalableCommandInterface) {
97 return false;
98 }
99
100 $event = $this->stopwatch->start($this->getName().'.handle_signal');
101
102 $exit = $this->command->handleSignal($signal, $previousExitCode);
103
104 $event->stop();
105
106 if (!isset($this->handledSignals[$signal])) {
107 $this->handledSignals[$signal] = [
108 'handled' => 0,
109 'duration' => 0,
110 'memory' => 0,
111 ];
112 }
113
114 ++$this->handledSignals[$signal]['handled'];
115 $this->handledSignals[$signal]['duration'] += $event->getDuration();
116 $this->handledSignals[$signal]['memory'] = max(
117 $this->handledSignals[$signal]['memory'],
118 $event->getMemory() >> 20
119 );
120
121 return $exit;
122 }
123
124 /**
125 * {@inheritdoc}
126 *
127 * Calling parent method is required to be used in {@see parent::run()}.
128 */
129 public function ignoreValidationErrors(): void
130 {
131 $this->ignoreValidation = true;
132 $this->command->ignoreValidationErrors();
133
134 parent::ignoreValidationErrors();
135 }
136
137 public function setApplication(?Application $application = null): void
138 {
139 $this->command->setApplication($application);
140 }
141
142 public function getApplication(): ?Application
143 {
144 return $this->command->getApplication();
145 }
146
147 public function setHelperSet(HelperSet $helperSet): void
148 {
149 $this->command->setHelperSet($helperSet);
150 }
151
152 public function getHelperSet(): ?HelperSet
153 {
154 return $this->command->getHelperSet();
155 }
156
157 public function isEnabled(): bool
158 {
159 return $this->command->isEnabled();
160 }
161
162 public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
163 {
164 $this->command->complete($input, $suggestions);
165 }
166
167 /**
168 * {@inheritdoc}
169 *
170 * Calling parent method is required to be used in {@see parent::run()}.
171 */
172 public function setCode(callable $code): static
173 {
174 $this->command->setCode($code);
175
176 return parent::setCode(function (InputInterface $input, OutputInterface $output) use ($code): int {
177 $event = $this->stopwatch->start($this->getName().'.code');
178
179 $this->exitCode = $code($input, $output);
180
181 $event->stop();
182
183 return $this->exitCode;
184 });
185 }
186
187 /**
188 * @internal
189 */
190 public function mergeApplicationDefinition(bool $mergeArgs = true): void
191 {
192 $this->command->mergeApplicationDefinition($mergeArgs);
193 }
194
195 public function setDefinition(array|InputDefinition $definition): static
196 {
197 $this->command->setDefinition($definition);
198
199 return $this;
200 }
201
202 public function getDefinition(): InputDefinition
203 {
204 return $this->command->getDefinition();
205 }
206
207 public function getNativeDefinition(): InputDefinition
208 {
209 return $this->command->getNativeDefinition();
210 }
211
212 public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static
213 {
214 $this->command->addArgument($name, $mode, $description, $default, $suggestedValues);
215
216 return $this;
217 }
218
219 public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static
220 {
221 $this->command->addOption($name, $shortcut, $mode, $description, $default, $suggestedValues);
222
223 return $this;
224 }
225
226 /**
227 * {@inheritdoc}
228 *
229 * Calling parent method is required to be used in {@see parent::run()}.
230 */
231 public function setProcessTitle(string $title): static
232 {
233 $this->command->setProcessTitle($title);
234
235 return parent::setProcessTitle($title);
236 }
237
238 public function setHelp(string $help): static
239 {
240 $this->command->setHelp($help);
241
242 return $this;
243 }
244
245 public function getHelp(): string
246 {
247 return $this->command->getHelp();
248 }
249
250 public function getProcessedHelp(): string
251 {
252 return $this->command->getProcessedHelp();
253 }
254
255 public function getSynopsis(bool $short = false): string
256 {
257 return $this->command->getSynopsis($short);
258 }
259
260 public function addUsage(string $usage): static
261 {
262 $this->command->addUsage($usage);
263
264 return $this;
265 }
266
267 public function getUsages(): array
268 {
269 return $this->command->getUsages();
270 }
271
272 public function getHelper(string $name): HelperInterface
273 {
274 return $this->command->getHelper($name);
275 }
276
277 public function run(InputInterface $input, OutputInterface $output): int
278 {
279 $this->input = $input;
280 $this->output = $output;
281 $this->arguments = $input->getArguments();
282 $this->options = $input->getOptions();
283 $event = $this->stopwatch->start($this->getName(), 'command');
284
285 try {
286 $this->exitCode = parent::run($input, $output);
287 } finally {
288 $event->stop();
289
290 if ($output instanceof ConsoleOutputInterface && $output->isDebug()) {
291 $output->getErrorOutput()->writeln((string) $event);
292 }
293
294 $this->duration = $event->getDuration().' ms';
295 $this->maxMemoryUsage = ($event->getMemory() >> 20).' MiB';
296
297 if ($this->isInteractive) {
298 $this->extractInteractiveInputs($input->getArguments(), $input->getOptions());
299 }
300 }
301
302 return $this->exitCode;
303 }
304
305 protected function initialize(InputInterface $input, OutputInterface $output): void
306 {
307 $event = $this->stopwatch->start($this->getName().'.init', 'command');
308
309 $this->command->initialize($input, $output);
310
311 $event->stop();
312 }
313
314 protected function interact(InputInterface $input, OutputInterface $output): void
315 {
316 if (!$this->isInteractive = Command::class !== (new \ReflectionMethod($this->command, 'interact'))->getDeclaringClass()->getName()) {
317 return;
318 }
319
320 $event = $this->stopwatch->start($this->getName().'.interact', 'command');
321
322 $this->command->interact($input, $output);
323
324 $event->stop();
325 }
326
327 protected function execute(InputInterface $input, OutputInterface $output): int
328 {
329 $event = $this->stopwatch->start($this->getName().'.execute', 'command');
330
331 $exitCode = $this->command->execute($input, $output);
332
333 $event->stop();
334
335 return $exitCode;
336 }
337
338 private function extractInteractiveInputs(array $arguments, array $options): void
339 {
340 foreach ($arguments as $argName => $argValue) {
341 if (\array_key_exists($argName, $this->arguments) && $this->arguments[$argName] === $argValue) {
342 continue;
343 }
344
345 $this->interactiveInputs[$argName] = $argValue;
346 }
347
348 foreach ($options as $optName => $optValue) {
349 if (\array_key_exists($optName, $this->options) && $this->options[$optName] === $optValue) {
350 continue;
351 }
352
353 $this->interactiveInputs['--'.$optName] = $optValue;
354 }
355 }
356}
diff --git a/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php b/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php
new file mode 100644
index 0000000..b6b637c
--- /dev/null
+++ b/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php
@@ -0,0 +1,38 @@
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\CommandLoader;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Exception\CommandNotFoundException;
16
17/**
18 * @author Robin Chalas <robin.chalas@gmail.com>
19 */
20interface CommandLoaderInterface
21{
22 /**
23 * Loads a command.
24 *
25 * @throws CommandNotFoundException
26 */
27 public function get(string $name): Command;
28
29 /**
30 * Checks if a command exists.
31 */
32 public function has(string $name): bool;
33
34 /**
35 * @return string[]
36 */
37 public function getNames(): array;
38}
diff --git a/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php b/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php
new file mode 100644
index 0000000..84e25be
--- /dev/null
+++ b/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php
@@ -0,0 +1,52 @@
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\CommandLoader;
13
14use Psr\Container\ContainerInterface;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Exception\CommandNotFoundException;
17
18/**
19 * Loads commands from a PSR-11 container.
20 *
21 * @author Robin Chalas <robin.chalas@gmail.com>
22 */
23class ContainerCommandLoader implements CommandLoaderInterface
24{
25 /**
26 * @param array $commandMap An array with command names as keys and service ids as values
27 */
28 public function __construct(
29 private ContainerInterface $container,
30 private array $commandMap,
31 ) {
32 }
33
34 public function get(string $name): Command
35 {
36 if (!$this->has($name)) {
37 throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
38 }
39
40 return $this->container->get($this->commandMap[$name]);
41 }
42
43 public function has(string $name): bool
44 {
45 return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]);
46 }
47
48 public function getNames(): array
49 {
50 return array_keys($this->commandMap);
51 }
52}
diff --git a/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php b/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php
new file mode 100644
index 0000000..ae16bf6
--- /dev/null
+++ b/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php
@@ -0,0 +1,52 @@
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\CommandLoader;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Exception\CommandNotFoundException;
16
17/**
18 * A simple command loader using factories to instantiate commands lazily.
19 *
20 * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
21 */
22class FactoryCommandLoader implements CommandLoaderInterface
23{
24 /**
25 * @param callable[] $factories Indexed by command names
26 */
27 public function __construct(
28 private array $factories,
29 ) {
30 }
31
32 public function has(string $name): bool
33 {
34 return isset($this->factories[$name]);
35 }
36
37 public function get(string $name): Command
38 {
39 if (!isset($this->factories[$name])) {
40 throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
41 }
42
43 $factory = $this->factories[$name];
44
45 return $factory();
46 }
47
48 public function getNames(): array
49 {
50 return array_keys($this->factories);
51 }
52}
diff --git a/vendor/symfony/console/Completion/CompletionInput.php b/vendor/symfony/console/Completion/CompletionInput.php
new file mode 100644
index 0000000..79c2f65
--- /dev/null
+++ b/vendor/symfony/console/Completion/CompletionInput.php
@@ -0,0 +1,248 @@
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\Completion;
13
14use Symfony\Component\Console\Exception\RuntimeException;
15use Symfony\Component\Console\Input\ArgvInput;
16use Symfony\Component\Console\Input\InputDefinition;
17use Symfony\Component\Console\Input\InputOption;
18
19/**
20 * An input specialized for shell completion.
21 *
22 * This input allows unfinished option names or values and exposes what kind of
23 * completion is expected.
24 *
25 * @author Wouter de Jong <wouter@wouterj.nl>
26 */
27final class CompletionInput extends ArgvInput
28{
29 public const TYPE_ARGUMENT_VALUE = 'argument_value';
30 public const TYPE_OPTION_VALUE = 'option_value';
31 public const TYPE_OPTION_NAME = 'option_name';
32 public const TYPE_NONE = 'none';
33
34 private array $tokens;
35 private int $currentIndex;
36 private string $completionType;
37 private ?string $completionName = null;
38 private string $completionValue = '';
39
40 /**
41 * Converts a terminal string into tokens.
42 *
43 * This is required for shell completions without COMP_WORDS support.
44 */
45 public static function fromString(string $inputStr, int $currentIndex): self
46 {
47 preg_match_all('/(?<=^|\s)([\'"]?)(.+?)(?<!\\\\)\1(?=$|\s)/', $inputStr, $tokens);
48
49 return self::fromTokens($tokens[0], $currentIndex);
50 }
51
52 /**
53 * Create an input based on an COMP_WORDS token list.
54 *
55 * @param string[] $tokens the set of split tokens (e.g. COMP_WORDS or argv)
56 * @param int $currentIndex the index of the cursor (e.g. COMP_CWORD)
57 */
58 public static function fromTokens(array $tokens, int $currentIndex): self
59 {
60 $input = new self($tokens);
61 $input->tokens = $tokens;
62 $input->currentIndex = $currentIndex;
63
64 return $input;
65 }
66
67 public function bind(InputDefinition $definition): void
68 {
69 parent::bind($definition);
70
71 $relevantToken = $this->getRelevantToken();
72 if ('-' === $relevantToken[0]) {
73 // the current token is an input option: complete either option name or option value
74 [$optionToken, $optionValue] = explode('=', $relevantToken, 2) + ['', ''];
75
76 $option = $this->getOptionFromToken($optionToken);
77 if (null === $option && !$this->isCursorFree()) {
78 $this->completionType = self::TYPE_OPTION_NAME;
79 $this->completionValue = $relevantToken;
80
81 return;
82 }
83
84 if ($option?->acceptValue()) {
85 $this->completionType = self::TYPE_OPTION_VALUE;
86 $this->completionName = $option->getName();
87 $this->completionValue = $optionValue ?: (!str_starts_with($optionToken, '--') ? substr($optionToken, 2) : '');
88
89 return;
90 }
91 }
92
93 $previousToken = $this->tokens[$this->currentIndex - 1];
94 if ('-' === $previousToken[0] && '' !== trim($previousToken, '-')) {
95 // check if previous option accepted a value
96 $previousOption = $this->getOptionFromToken($previousToken);
97 if ($previousOption?->acceptValue()) {
98 $this->completionType = self::TYPE_OPTION_VALUE;
99 $this->completionName = $previousOption->getName();
100 $this->completionValue = $relevantToken;
101
102 return;
103 }
104 }
105
106 // complete argument value
107 $this->completionType = self::TYPE_ARGUMENT_VALUE;
108
109 foreach ($this->definition->getArguments() as $argumentName => $argument) {
110 if (!isset($this->arguments[$argumentName])) {
111 break;
112 }
113
114 $argumentValue = $this->arguments[$argumentName];
115 $this->completionName = $argumentName;
116 if (\is_array($argumentValue)) {
117 $this->completionValue = $argumentValue ? $argumentValue[array_key_last($argumentValue)] : null;
118 } else {
119 $this->completionValue = $argumentValue;
120 }
121 }
122
123 if ($this->currentIndex >= \count($this->tokens)) {
124 if (!isset($this->arguments[$argumentName]) || $this->definition->getArgument($argumentName)->isArray()) {
125 $this->completionName = $argumentName;
126 $this->completionValue = '';
127 } else {
128 // we've reached the end
129 $this->completionType = self::TYPE_NONE;
130 $this->completionName = null;
131 $this->completionValue = '';
132 }
133 }
134 }
135
136 /**
137 * Returns the type of completion required.
138 *
139 * TYPE_ARGUMENT_VALUE when completing the value of an input argument
140 * TYPE_OPTION_VALUE when completing the value of an input option
141 * TYPE_OPTION_NAME when completing the name of an input option
142 * TYPE_NONE when nothing should be completed
143 *
144 * TYPE_OPTION_NAME and TYPE_NONE are already implemented by the Console component.
145 *
146 * @return self::TYPE_*
147 */
148 public function getCompletionType(): string
149 {
150 return $this->completionType;
151 }
152
153 /**
154 * The name of the input option or argument when completing a value.
155 *
156 * @return string|null returns null when completing an option name
157 */
158 public function getCompletionName(): ?string
159 {
160 return $this->completionName;
161 }
162
163 /**
164 * The value already typed by the user (or empty string).
165 */
166 public function getCompletionValue(): string
167 {
168 return $this->completionValue;
169 }
170
171 public function mustSuggestOptionValuesFor(string $optionName): bool
172 {
173 return self::TYPE_OPTION_VALUE === $this->getCompletionType() && $optionName === $this->getCompletionName();
174 }
175
176 public function mustSuggestArgumentValuesFor(string $argumentName): bool
177 {
178 return self::TYPE_ARGUMENT_VALUE === $this->getCompletionType() && $argumentName === $this->getCompletionName();
179 }
180
181 protected function parseToken(string $token, bool $parseOptions): bool
182 {
183 try {
184 return parent::parseToken($token, $parseOptions);
185 } catch (RuntimeException) {
186 // suppress errors, completed input is almost never valid
187 }
188
189 return $parseOptions;
190 }
191
192 private function getOptionFromToken(string $optionToken): ?InputOption
193 {
194 $optionName = ltrim($optionToken, '-');
195 if (!$optionName) {
196 return null;
197 }
198
199 if ('-' === ($optionToken[1] ?? ' ')) {
200 // long option name
201 return $this->definition->hasOption($optionName) ? $this->definition->getOption($optionName) : null;
202 }
203
204 // short option name
205 return $this->definition->hasShortcut($optionName[0]) ? $this->definition->getOptionForShortcut($optionName[0]) : null;
206 }
207
208 /**
209 * The token of the cursor, or the last token if the cursor is at the end of the input.
210 */
211 private function getRelevantToken(): string
212 {
213 return $this->tokens[$this->isCursorFree() ? $this->currentIndex - 1 : $this->currentIndex];
214 }
215
216 /**
217 * Whether the cursor is "free" (i.e. at the end of the input preceded by a space).
218 */
219 private function isCursorFree(): bool
220 {
221 $nrOfTokens = \count($this->tokens);
222 if ($this->currentIndex > $nrOfTokens) {
223 throw new \LogicException('Current index is invalid, it must be the number of input tokens or one more.');
224 }
225
226 return $this->currentIndex >= $nrOfTokens;
227 }
228
229 public function __toString()
230 {
231 $str = '';
232 foreach ($this->tokens as $i => $token) {
233 $str .= $token;
234
235 if ($this->currentIndex === $i) {
236 $str .= '|';
237 }
238
239 $str .= ' ';
240 }
241
242 if ($this->currentIndex > $i) {
243 $str .= '|';
244 }
245
246 return rtrim($str);
247 }
248}
diff --git a/vendor/symfony/console/Completion/CompletionSuggestions.php b/vendor/symfony/console/Completion/CompletionSuggestions.php
new file mode 100644
index 0000000..549bbaf
--- /dev/null
+++ b/vendor/symfony/console/Completion/CompletionSuggestions.php
@@ -0,0 +1,97 @@
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\Completion;
13
14use Symfony\Component\Console\Input\InputOption;
15
16/**
17 * Stores all completion suggestions for the current input.
18 *
19 * @author Wouter de Jong <wouter@wouterj.nl>
20 */
21final class CompletionSuggestions
22{
23 private array $valueSuggestions = [];
24 private array $optionSuggestions = [];
25
26 /**
27 * Add a suggested value for an input option or argument.
28 *
29 * @return $this
30 */
31 public function suggestValue(string|Suggestion $value): static
32 {
33 $this->valueSuggestions[] = !$value instanceof Suggestion ? new Suggestion($value) : $value;
34
35 return $this;
36 }
37
38 /**
39 * Add multiple suggested values at once for an input option or argument.
40 *
41 * @param list<string|Suggestion> $values
42 *
43 * @return $this
44 */
45 public function suggestValues(array $values): static
46 {
47 foreach ($values as $value) {
48 $this->suggestValue($value);
49 }
50
51 return $this;
52 }
53
54 /**
55 * Add a suggestion for an input option name.
56 *
57 * @return $this
58 */
59 public function suggestOption(InputOption $option): static
60 {
61 $this->optionSuggestions[] = $option;
62
63 return $this;
64 }
65
66 /**
67 * Add multiple suggestions for input option names at once.
68 *
69 * @param InputOption[] $options
70 *
71 * @return $this
72 */
73 public function suggestOptions(array $options): static
74 {
75 foreach ($options as $option) {
76 $this->suggestOption($option);
77 }
78
79 return $this;
80 }
81
82 /**
83 * @return InputOption[]
84 */
85 public function getOptionSuggestions(): array
86 {
87 return $this->optionSuggestions;
88 }
89
90 /**
91 * @return Suggestion[]
92 */
93 public function getValueSuggestions(): array
94 {
95 return $this->valueSuggestions;
96 }
97}
diff --git a/vendor/symfony/console/Completion/Output/BashCompletionOutput.php b/vendor/symfony/console/Completion/Output/BashCompletionOutput.php
new file mode 100644
index 0000000..c6f76eb
--- /dev/null
+++ b/vendor/symfony/console/Completion/Output/BashCompletionOutput.php
@@ -0,0 +1,33 @@
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\Completion\Output;
13
14use Symfony\Component\Console\Completion\CompletionSuggestions;
15use Symfony\Component\Console\Output\OutputInterface;
16
17/**
18 * @author Wouter de Jong <wouter@wouterj.nl>
19 */
20class BashCompletionOutput implements CompletionOutputInterface
21{
22 public function write(CompletionSuggestions $suggestions, OutputInterface $output): void
23 {
24 $values = $suggestions->getValueSuggestions();
25 foreach ($suggestions->getOptionSuggestions() as $option) {
26 $values[] = '--'.$option->getName();
27 if ($option->isNegatable()) {
28 $values[] = '--no-'.$option->getName();
29 }
30 }
31 $output->writeln(implode("\n", $values));
32 }
33}
diff --git a/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php b/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php
new file mode 100644
index 0000000..659e596
--- /dev/null
+++ b/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php
@@ -0,0 +1,25 @@
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\Completion\Output;
13
14use Symfony\Component\Console\Completion\CompletionSuggestions;
15use Symfony\Component\Console\Output\OutputInterface;
16
17/**
18 * Transforms the {@see CompletionSuggestions} object into output readable by the shell completion.
19 *
20 * @author Wouter de Jong <wouter@wouterj.nl>
21 */
22interface CompletionOutputInterface
23{
24 public function write(CompletionSuggestions $suggestions, OutputInterface $output): void;
25}
diff --git a/vendor/symfony/console/Completion/Output/FishCompletionOutput.php b/vendor/symfony/console/Completion/Output/FishCompletionOutput.php
new file mode 100644
index 0000000..356a974
--- /dev/null
+++ b/vendor/symfony/console/Completion/Output/FishCompletionOutput.php
@@ -0,0 +1,36 @@
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\Completion\Output;
13
14use Symfony\Component\Console\Completion\CompletionSuggestions;
15use Symfony\Component\Console\Output\OutputInterface;
16
17/**
18 * @author Guillaume Aveline <guillaume.aveline@pm.me>
19 */
20class FishCompletionOutput implements CompletionOutputInterface
21{
22 public function write(CompletionSuggestions $suggestions, OutputInterface $output): void
23 {
24 $values = [];
25 foreach ($suggestions->getValueSuggestions() as $value) {
26 $values[] = $value->getValue().($value->getDescription() ? "\t".$value->getDescription() : '');
27 }
28 foreach ($suggestions->getOptionSuggestions() as $option) {
29 $values[] = '--'.$option->getName().($option->getDescription() ? "\t".$option->getDescription() : '');
30 if ($option->isNegatable()) {
31 $values[] = '--no-'.$option->getName().($option->getDescription() ? "\t".$option->getDescription() : '');
32 }
33 }
34 $output->write(implode("\n", $values));
35 }
36}
diff --git a/vendor/symfony/console/Completion/Output/ZshCompletionOutput.php b/vendor/symfony/console/Completion/Output/ZshCompletionOutput.php
new file mode 100644
index 0000000..bb4ce70
--- /dev/null
+++ b/vendor/symfony/console/Completion/Output/ZshCompletionOutput.php
@@ -0,0 +1,36 @@
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\Completion\Output;
13
14use Symfony\Component\Console\Completion\CompletionSuggestions;
15use Symfony\Component\Console\Output\OutputInterface;
16
17/**
18 * @author Jitendra A <adhocore@gmail.com>
19 */
20class ZshCompletionOutput implements CompletionOutputInterface
21{
22 public function write(CompletionSuggestions $suggestions, OutputInterface $output): void
23 {
24 $values = [];
25 foreach ($suggestions->getValueSuggestions() as $value) {
26 $values[] = $value->getValue().($value->getDescription() ? "\t".$value->getDescription() : '');
27 }
28 foreach ($suggestions->getOptionSuggestions() as $option) {
29 $values[] = '--'.$option->getName().($option->getDescription() ? "\t".$option->getDescription() : '');
30 if ($option->isNegatable()) {
31 $values[] = '--no-'.$option->getName().($option->getDescription() ? "\t".$option->getDescription() : '');
32 }
33 }
34 $output->write(implode("\n", $values)."\n");
35 }
36}
diff --git a/vendor/symfony/console/Completion/Suggestion.php b/vendor/symfony/console/Completion/Suggestion.php
new file mode 100644
index 0000000..3251b07
--- /dev/null
+++ b/vendor/symfony/console/Completion/Suggestion.php
@@ -0,0 +1,41 @@
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\Completion;
13
14/**
15 * Represents a single suggested value.
16 *
17 * @author Wouter de Jong <wouter@wouterj.nl>
18 */
19class Suggestion implements \Stringable
20{
21 public function __construct(
22 private readonly string $value,
23 private readonly string $description = '',
24 ) {
25 }
26
27 public function getValue(): string
28 {
29 return $this->value;
30 }
31
32 public function getDescription(): string
33 {
34 return $this->description;
35 }
36
37 public function __toString(): string
38 {
39 return $this->getValue();
40 }
41}
diff --git a/vendor/symfony/console/ConsoleEvents.php b/vendor/symfony/console/ConsoleEvents.php
new file mode 100644
index 0000000..6ae8f32
--- /dev/null
+++ b/vendor/symfony/console/ConsoleEvents.php
@@ -0,0 +1,72 @@
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;
13
14use Symfony\Component\Console\Event\ConsoleCommandEvent;
15use Symfony\Component\Console\Event\ConsoleErrorEvent;
16use Symfony\Component\Console\Event\ConsoleSignalEvent;
17use Symfony\Component\Console\Event\ConsoleTerminateEvent;
18
19/**
20 * Contains all events dispatched by an Application.
21 *
22 * @author Francesco Levorato <git@flevour.net>
23 */
24final class ConsoleEvents
25{
26 /**
27 * The COMMAND event allows you to attach listeners before any command is
28 * executed by the console. It also allows you to modify the command, input and output
29 * before they are handed to the command.
30 *
31 * @Event("Symfony\Component\Console\Event\ConsoleCommandEvent")
32 */
33 public const COMMAND = 'console.command';
34
35 /**
36 * The SIGNAL event allows you to perform some actions
37 * after the command execution was interrupted.
38 *
39 * @Event("Symfony\Component\Console\Event\ConsoleSignalEvent")
40 */
41 public const SIGNAL = 'console.signal';
42
43 /**
44 * The TERMINATE event allows you to attach listeners after a command is
45 * executed by the console.
46 *
47 * @Event("Symfony\Component\Console\Event\ConsoleTerminateEvent")
48 */
49 public const TERMINATE = 'console.terminate';
50
51 /**
52 * The ERROR event occurs when an uncaught exception or error appears.
53 *
54 * This event allows you to deal with the exception/error or
55 * to modify the thrown exception.
56 *
57 * @Event("Symfony\Component\Console\Event\ConsoleErrorEvent")
58 */
59 public const ERROR = 'console.error';
60
61 /**
62 * Event aliases.
63 *
64 * These aliases can be consumed by RegisterListenersPass.
65 */
66 public const ALIASES = [
67 ConsoleCommandEvent::class => self::COMMAND,
68 ConsoleErrorEvent::class => self::ERROR,
69 ConsoleSignalEvent::class => self::SIGNAL,
70 ConsoleTerminateEvent::class => self::TERMINATE,
71 ];
72}
diff --git a/vendor/symfony/console/Cursor.php b/vendor/symfony/console/Cursor.php
new file mode 100644
index 0000000..965f996
--- /dev/null
+++ b/vendor/symfony/console/Cursor.php
@@ -0,0 +1,204 @@
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;
13
14use Symfony\Component\Console\Output\OutputInterface;
15
16/**
17 * @author Pierre du Plessis <pdples@gmail.com>
18 */
19final class Cursor
20{
21 /** @var resource */
22 private $input;
23
24 /**
25 * @param resource|null $input
26 */
27 public function __construct(
28 private OutputInterface $output,
29 $input = null,
30 ) {
31 $this->input = $input ?? (\defined('STDIN') ? \STDIN : fopen('php://input', 'r+'));
32 }
33
34 /**
35 * @return $this
36 */
37 public function moveUp(int $lines = 1): static
38 {
39 $this->output->write(sprintf("\x1b[%dA", $lines));
40
41 return $this;
42 }
43
44 /**
45 * @return $this
46 */
47 public function moveDown(int $lines = 1): static
48 {
49 $this->output->write(sprintf("\x1b[%dB", $lines));
50
51 return $this;
52 }
53
54 /**
55 * @return $this
56 */
57 public function moveRight(int $columns = 1): static
58 {
59 $this->output->write(sprintf("\x1b[%dC", $columns));
60
61 return $this;
62 }
63
64 /**
65 * @return $this
66 */
67 public function moveLeft(int $columns = 1): static
68 {
69 $this->output->write(sprintf("\x1b[%dD", $columns));
70
71 return $this;
72 }
73
74 /**
75 * @return $this
76 */
77 public function moveToColumn(int $column): static
78 {
79 $this->output->write(sprintf("\x1b[%dG", $column));
80
81 return $this;
82 }
83
84 /**
85 * @return $this
86 */
87 public function moveToPosition(int $column, int $row): static
88 {
89 $this->output->write(sprintf("\x1b[%d;%dH", $row + 1, $column));
90
91 return $this;
92 }
93
94 /**
95 * @return $this
96 */
97 public function savePosition(): static
98 {
99 $this->output->write("\x1b7");
100
101 return $this;
102 }
103
104 /**
105 * @return $this
106 */
107 public function restorePosition(): static
108 {
109 $this->output->write("\x1b8");
110
111 return $this;
112 }
113
114 /**
115 * @return $this
116 */
117 public function hide(): static
118 {
119 $this->output->write("\x1b[?25l");
120
121 return $this;
122 }
123
124 /**
125 * @return $this
126 */
127 public function show(): static
128 {
129 $this->output->write("\x1b[?25h\x1b[?0c");
130
131 return $this;
132 }
133
134 /**
135 * Clears all the output from the current line.
136 *
137 * @return $this
138 */
139 public function clearLine(): static
140 {
141 $this->output->write("\x1b[2K");
142
143 return $this;
144 }
145
146 /**
147 * Clears all the output from the current line after the current position.
148 */
149 public function clearLineAfter(): self
150 {
151 $this->output->write("\x1b[K");
152
153 return $this;
154 }
155
156 /**
157 * Clears all the output from the cursors' current position to the end of the screen.
158 *
159 * @return $this
160 */
161 public function clearOutput(): static
162 {
163 $this->output->write("\x1b[0J");
164
165 return $this;
166 }
167
168 /**
169 * Clears the entire screen.
170 *
171 * @return $this
172 */
173 public function clearScreen(): static
174 {
175 $this->output->write("\x1b[2J");
176
177 return $this;
178 }
179
180 /**
181 * Returns the current cursor position as x,y coordinates.
182 */
183 public function getCurrentPosition(): array
184 {
185 static $isTtySupported;
186
187 if (!$isTtySupported ??= '/' === \DIRECTORY_SEPARATOR && stream_isatty(\STDOUT)) {
188 return [1, 1];
189 }
190
191 $sttyMode = shell_exec('stty -g');
192 shell_exec('stty -icanon -echo');
193
194 @fwrite($this->input, "\033[6n");
195
196 $code = trim(fread($this->input, 1024));
197
198 shell_exec(sprintf('stty %s', $sttyMode));
199
200 sscanf($code, "\033[%d;%dR", $row, $col);
201
202 return [$col, $row];
203 }
204}
diff --git a/vendor/symfony/console/DataCollector/CommandDataCollector.php b/vendor/symfony/console/DataCollector/CommandDataCollector.php
new file mode 100644
index 0000000..45138c7
--- /dev/null
+++ b/vendor/symfony/console/DataCollector/CommandDataCollector.php
@@ -0,0 +1,234 @@
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\DataCollector;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Debug\CliRequest;
16use Symfony\Component\Console\Output\OutputInterface;
17use Symfony\Component\Console\SignalRegistry\SignalMap;
18use Symfony\Component\HttpFoundation\Request;
19use Symfony\Component\HttpFoundation\Response;
20use Symfony\Component\HttpKernel\DataCollector\DataCollector;
21use Symfony\Component\VarDumper\Cloner\Data;
22
23/**
24 * @internal
25 *
26 * @author Jules Pietri <jules@heahprod.com>
27 */
28final class CommandDataCollector extends DataCollector
29{
30 public function collect(Request $request, Response $response, ?\Throwable $exception = null): void
31 {
32 if (!$request instanceof CliRequest) {
33 return;
34 }
35
36 $command = $request->command;
37 $application = $command->getApplication();
38
39 $this->data = [
40 'command' => $this->cloneVar($command->command),
41 'exit_code' => $command->exitCode,
42 'interrupted_by_signal' => $command->interruptedBySignal,
43 'duration' => $command->duration,
44 'max_memory_usage' => $command->maxMemoryUsage,
45 'verbosity_level' => match ($command->output->getVerbosity()) {
46 OutputInterface::VERBOSITY_QUIET => 'quiet',
47 OutputInterface::VERBOSITY_NORMAL => 'normal',
48 OutputInterface::VERBOSITY_VERBOSE => 'verbose',
49 OutputInterface::VERBOSITY_VERY_VERBOSE => 'very verbose',
50 OutputInterface::VERBOSITY_DEBUG => 'debug',
51 },
52 'interactive' => $command->isInteractive,
53 'validate_input' => !$command->ignoreValidation,
54 'enabled' => $command->isEnabled(),
55 'visible' => !$command->isHidden(),
56 'input' => $this->cloneVar($command->input),
57 'output' => $this->cloneVar($command->output),
58 'interactive_inputs' => array_map($this->cloneVar(...), $command->interactiveInputs),
59 'signalable' => $command->getSubscribedSignals(),
60 'handled_signals' => $command->handledSignals,
61 'helper_set' => array_map($this->cloneVar(...), iterator_to_array($command->getHelperSet())),
62 ];
63
64 $baseDefinition = $application->getDefinition();
65
66 foreach ($command->arguments as $argName => $argValue) {
67 if ($baseDefinition->hasArgument($argName)) {
68 $this->data['application_inputs'][$argName] = $this->cloneVar($argValue);
69 } else {
70 $this->data['arguments'][$argName] = $this->cloneVar($argValue);
71 }
72 }
73
74 foreach ($command->options as $optName => $optValue) {
75 if ($baseDefinition->hasOption($optName)) {
76 $this->data['application_inputs']['--'.$optName] = $this->cloneVar($optValue);
77 } else {
78 $this->data['options'][$optName] = $this->cloneVar($optValue);
79 }
80 }
81 }
82
83 public function getName(): string
84 {
85 return 'command';
86 }
87
88 /**
89 * @return array{
90 * class?: class-string,
91 * executor?: string,
92 * file: string,
93 * line: int,
94 * }
95 */
96 public function getCommand(): array
97 {
98 $class = $this->data['command']->getType();
99 $r = new \ReflectionMethod($class, 'execute');
100
101 if (Command::class !== $r->getDeclaringClass()) {
102 return [
103 'executor' => $class.'::'.$r->name,
104 'file' => $r->getFileName(),
105 'line' => $r->getStartLine(),
106 ];
107 }
108
109 $r = new \ReflectionClass($class);
110
111 return [
112 'class' => $class,
113 'file' => $r->getFileName(),
114 'line' => $r->getStartLine(),
115 ];
116 }
117
118 public function getInterruptedBySignal(): ?string
119 {
120 if (isset($this->data['interrupted_by_signal'])) {
121 return sprintf('%s (%d)', SignalMap::getSignalName($this->data['interrupted_by_signal']), $this->data['interrupted_by_signal']);
122 }
123
124 return null;
125 }
126
127 public function getDuration(): string
128 {
129 return $this->data['duration'];
130 }
131
132 public function getMaxMemoryUsage(): string
133 {
134 return $this->data['max_memory_usage'];
135 }
136
137 public function getVerbosityLevel(): string
138 {
139 return $this->data['verbosity_level'];
140 }
141
142 public function getInteractive(): bool
143 {
144 return $this->data['interactive'];
145 }
146
147 public function getValidateInput(): bool
148 {
149 return $this->data['validate_input'];
150 }
151
152 public function getEnabled(): bool
153 {
154 return $this->data['enabled'];
155 }
156
157 public function getVisible(): bool
158 {
159 return $this->data['visible'];
160 }
161
162 public function getInput(): Data
163 {
164 return $this->data['input'];
165 }
166
167 public function getOutput(): Data
168 {
169 return $this->data['output'];
170 }
171
172 /**
173 * @return Data[]
174 */
175 public function getArguments(): array
176 {
177 return $this->data['arguments'] ?? [];
178 }
179
180 /**
181 * @return Data[]
182 */
183 public function getOptions(): array
184 {
185 return $this->data['options'] ?? [];
186 }
187
188 /**
189 * @return Data[]
190 */
191 public function getApplicationInputs(): array
192 {
193 return $this->data['application_inputs'] ?? [];
194 }
195
196 /**
197 * @return Data[]
198 */
199 public function getInteractiveInputs(): array
200 {
201 return $this->data['interactive_inputs'] ?? [];
202 }
203
204 public function getSignalable(): array
205 {
206 return array_map(
207 static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal),
208 $this->data['signalable']
209 );
210 }
211
212 public function getHandledSignals(): array
213 {
214 $keys = array_map(
215 static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal),
216 array_keys($this->data['handled_signals'])
217 );
218
219 return array_combine($keys, array_values($this->data['handled_signals']));
220 }
221
222 /**
223 * @return Data[]
224 */
225 public function getHelperSet(): array
226 {
227 return $this->data['helper_set'] ?? [];
228 }
229
230 public function reset(): void
231 {
232 $this->data = [];
233 }
234}
diff --git a/vendor/symfony/console/Debug/CliRequest.php b/vendor/symfony/console/Debug/CliRequest.php
new file mode 100644
index 0000000..b023db0
--- /dev/null
+++ b/vendor/symfony/console/Debug/CliRequest.php
@@ -0,0 +1,70 @@
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\Debug;
13
14use Symfony\Component\Console\Command\TraceableCommand;
15use Symfony\Component\HttpFoundation\Request;
16use Symfony\Component\HttpFoundation\Response;
17
18/**
19 * @internal
20 */
21final class CliRequest extends Request
22{
23 public function __construct(
24 public readonly TraceableCommand $command,
25 ) {
26 parent::__construct(
27 attributes: ['_controller' => \get_class($command->command), '_virtual_type' => 'command'],
28 server: $_SERVER,
29 );
30 }
31
32 // Methods below allow to populate a profile, thus enable search and filtering
33 public function getUri(): string
34 {
35 if ($this->server->has('SYMFONY_CLI_BINARY_NAME')) {
36 $binary = $this->server->get('SYMFONY_CLI_BINARY_NAME').' console';
37 } else {
38 $binary = $this->server->get('argv')[0];
39 }
40
41 return $binary.' '.$this->command->input;
42 }
43
44 public function getMethod(): string
45 {
46 return $this->command->isInteractive ? 'INTERACTIVE' : 'BATCH';
47 }
48
49 public function getResponse(): Response
50 {
51 return new class($this->command->exitCode) extends Response {
52 public function __construct(private readonly int $exitCode)
53 {
54 parent::__construct();
55 }
56
57 public function getStatusCode(): int
58 {
59 return $this->exitCode;
60 }
61 };
62 }
63
64 public function getClientIp(): string
65 {
66 $application = $this->command->getApplication();
67
68 return $application->getName().' '.$application->getVersion();
69 }
70}
diff --git a/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php b/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php
new file mode 100644
index 0000000..f712c61
--- /dev/null
+++ b/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php
@@ -0,0 +1,131 @@
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\DependencyInjection;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Command\LazyCommand;
16use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
17use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
18use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
19use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
20use Symfony\Component\DependencyInjection\ContainerBuilder;
21use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
22use Symfony\Component\DependencyInjection\Reference;
23use Symfony\Component\DependencyInjection\TypedReference;
24
25/**
26 * Registers console commands.
27 *
28 * @author Grégoire Pineau <lyrixx@lyrixx.info>
29 */
30class AddConsoleCommandPass implements CompilerPassInterface
31{
32 public function process(ContainerBuilder $container): void
33 {
34 $commandServices = $container->findTaggedServiceIds('console.command', true);
35 $lazyCommandMap = [];
36 $lazyCommandRefs = [];
37 $serviceIds = [];
38
39 foreach ($commandServices as $id => $tags) {
40 $definition = $container->getDefinition($id);
41 $definition->addTag('container.no_preload');
42 $class = $container->getParameterBag()->resolveValue($definition->getClass());
43
44 if (isset($tags[0]['command'])) {
45 $aliases = $tags[0]['command'];
46 } else {
47 if (!$r = $container->getReflectionClass($class)) {
48 throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
49 }
50 if (!$r->isSubclassOf(Command::class)) {
51 throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class));
52 }
53 $aliases = str_replace('%', '%%', $class::getDefaultName() ?? '');
54 }
55
56 $aliases = explode('|', $aliases ?? '');
57 $commandName = array_shift($aliases);
58
59 if ($isHidden = '' === $commandName) {
60 $commandName = array_shift($aliases);
61 }
62
63 if (null === $commandName) {
64 if (!$definition->isPublic() || $definition->isPrivate() || $definition->hasTag('container.private')) {
65 $commandId = 'console.command.public_alias.'.$id;
66 $container->setAlias($commandId, $id)->setPublic(true);
67 $id = $commandId;
68 }
69 $serviceIds[] = $id;
70
71 continue;
72 }
73
74 $description = $tags[0]['description'] ?? null;
75
76 unset($tags[0]);
77 $lazyCommandMap[$commandName] = $id;
78 $lazyCommandRefs[$id] = new TypedReference($id, $class);
79
80 foreach ($aliases as $alias) {
81 $lazyCommandMap[$alias] = $id;
82 }
83
84 foreach ($tags as $tag) {
85 if (isset($tag['command'])) {
86 $aliases[] = $tag['command'];
87 $lazyCommandMap[$tag['command']] = $id;
88 }
89
90 $description ??= $tag['description'] ?? null;
91 }
92
93 $definition->addMethodCall('setName', [$commandName]);
94
95 if ($aliases) {
96 $definition->addMethodCall('setAliases', [$aliases]);
97 }
98
99 if ($isHidden) {
100 $definition->addMethodCall('setHidden', [true]);
101 }
102
103 if (!$description) {
104 if (!$r = $container->getReflectionClass($class)) {
105 throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
106 }
107 if (!$r->isSubclassOf(Command::class)) {
108 throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class));
109 }
110 $description = str_replace('%', '%%', $class::getDefaultDescription() ?? '');
111 }
112
113 if ($description) {
114 $definition->addMethodCall('setDescription', [$description]);
115
116 $container->register('.'.$id.'.lazy', LazyCommand::class)
117 ->setArguments([$commandName, $aliases, $description, $isHidden, new ServiceClosureArgument($lazyCommandRefs[$id])]);
118
119 $lazyCommandRefs[$id] = new Reference('.'.$id.'.lazy');
120 }
121 }
122
123 $container
124 ->register('console.command_loader', ContainerCommandLoader::class)
125 ->setPublic(true)
126 ->addTag('container.no_preload')
127 ->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]);
128
129 $container->setParameter('console.command.ids', $serviceIds);
130 }
131}
diff --git a/vendor/symfony/console/Descriptor/ApplicationDescription.php b/vendor/symfony/console/Descriptor/ApplicationDescription.php
new file mode 100644
index 0000000..5149fde
--- /dev/null
+++ b/vendor/symfony/console/Descriptor/ApplicationDescription.php
@@ -0,0 +1,136 @@
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\Descriptor;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Exception\CommandNotFoundException;
17
18/**
19 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
20 *
21 * @internal
22 */
23class ApplicationDescription
24{
25 public const GLOBAL_NAMESPACE = '_global';
26
27 private array $namespaces;
28
29 /**
30 * @var array<string, Command>
31 */
32 private array $commands;
33
34 /**
35 * @var array<string, Command>
36 */
37 private array $aliases = [];
38
39 public function __construct(
40 private Application $application,
41 private ?string $namespace = null,
42 private bool $showHidden = false,
43 ) {
44 }
45
46 public function getNamespaces(): array
47 {
48 if (!isset($this->namespaces)) {
49 $this->inspectApplication();
50 }
51
52 return $this->namespaces;
53 }
54
55 /**
56 * @return Command[]
57 */
58 public function getCommands(): array
59 {
60 if (!isset($this->commands)) {
61 $this->inspectApplication();
62 }
63
64 return $this->commands;
65 }
66
67 /**
68 * @throws CommandNotFoundException
69 */
70 public function getCommand(string $name): Command
71 {
72 if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
73 throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
74 }
75
76 return $this->commands[$name] ?? $this->aliases[$name];
77 }
78
79 private function inspectApplication(): void
80 {
81 $this->commands = [];
82 $this->namespaces = [];
83
84 $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null);
85 foreach ($this->sortCommands($all) as $namespace => $commands) {
86 $names = [];
87
88 /** @var Command $command */
89 foreach ($commands as $name => $command) {
90 if (!$command->getName() || (!$this->showHidden && $command->isHidden())) {
91 continue;
92 }
93
94 if ($command->getName() === $name) {
95 $this->commands[$name] = $command;
96 } else {
97 $this->aliases[$name] = $command;
98 }
99
100 $names[] = $name;
101 }
102
103 $this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names];
104 }
105 }
106
107 private function sortCommands(array $commands): array
108 {
109 $namespacedCommands = [];
110 $globalCommands = [];
111 $sortedCommands = [];
112 foreach ($commands as $name => $command) {
113 $key = $this->application->extractNamespace($name, 1);
114 if (\in_array($key, ['', self::GLOBAL_NAMESPACE], true)) {
115 $globalCommands[$name] = $command;
116 } else {
117 $namespacedCommands[$key][$name] = $command;
118 }
119 }
120
121 if ($globalCommands) {
122 ksort($globalCommands);
123 $sortedCommands[self::GLOBAL_NAMESPACE] = $globalCommands;
124 }
125
126 if ($namespacedCommands) {
127 ksort($namespacedCommands, \SORT_STRING);
128 foreach ($namespacedCommands as $key => $commandsSet) {
129 ksort($commandsSet);
130 $sortedCommands[$key] = $commandsSet;
131 }
132 }
133
134 return $sortedCommands;
135 }
136}
diff --git a/vendor/symfony/console/Descriptor/Descriptor.php b/vendor/symfony/console/Descriptor/Descriptor.php
new file mode 100644
index 0000000..7b2509c
--- /dev/null
+++ b/vendor/symfony/console/Descriptor/Descriptor.php
@@ -0,0 +1,74 @@
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\Descriptor;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Exception\InvalidArgumentException;
17use Symfony\Component\Console\Input\InputArgument;
18use Symfony\Component\Console\Input\InputDefinition;
19use Symfony\Component\Console\Input\InputOption;
20use Symfony\Component\Console\Output\OutputInterface;
21
22/**
23 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
24 *
25 * @internal
26 */
27abstract class Descriptor implements DescriptorInterface
28{
29 protected OutputInterface $output;
30
31 public function describe(OutputInterface $output, object $object, array $options = []): void
32 {
33 $this->output = $output;
34
35 match (true) {
36 $object instanceof InputArgument => $this->describeInputArgument($object, $options),
37 $object instanceof InputOption => $this->describeInputOption($object, $options),
38 $object instanceof InputDefinition => $this->describeInputDefinition($object, $options),
39 $object instanceof Command => $this->describeCommand($object, $options),
40 $object instanceof Application => $this->describeApplication($object, $options),
41 default => throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object))),
42 };
43 }
44
45 protected function write(string $content, bool $decorated = false): void
46 {
47 $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
48 }
49
50 /**
51 * Describes an InputArgument instance.
52 */
53 abstract protected function describeInputArgument(InputArgument $argument, array $options = []): void;
54
55 /**
56 * Describes an InputOption instance.
57 */
58 abstract protected function describeInputOption(InputOption $option, array $options = []): void;
59
60 /**
61 * Describes an InputDefinition instance.
62 */
63 abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []): void;
64
65 /**
66 * Describes a Command instance.
67 */
68 abstract protected function describeCommand(Command $command, array $options = []): void;
69
70 /**
71 * Describes an Application instance.
72 */
73 abstract protected function describeApplication(Application $application, array $options = []): void;
74}
diff --git a/vendor/symfony/console/Descriptor/DescriptorInterface.php b/vendor/symfony/console/Descriptor/DescriptorInterface.php
new file mode 100644
index 0000000..04e5a7c
--- /dev/null
+++ b/vendor/symfony/console/Descriptor/DescriptorInterface.php
@@ -0,0 +1,24 @@
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\Descriptor;
13
14use Symfony\Component\Console\Output\OutputInterface;
15
16/**
17 * Descriptor interface.
18 *
19 * @author Jean-François Simon <contact@jfsimon.fr>
20 */
21interface DescriptorInterface
22{
23 public function describe(OutputInterface $output, object $object, array $options = []): void;
24}
diff --git a/vendor/symfony/console/Descriptor/JsonDescriptor.php b/vendor/symfony/console/Descriptor/JsonDescriptor.php
new file mode 100644
index 0000000..9563037
--- /dev/null
+++ b/vendor/symfony/console/Descriptor/JsonDescriptor.php
@@ -0,0 +1,166 @@
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\Descriptor;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Input\InputArgument;
17use Symfony\Component\Console\Input\InputDefinition;
18use Symfony\Component\Console\Input\InputOption;
19
20/**
21 * JSON descriptor.
22 *
23 * @author Jean-François Simon <contact@jfsimon.fr>
24 *
25 * @internal
26 */
27class JsonDescriptor extends Descriptor
28{
29 protected function describeInputArgument(InputArgument $argument, array $options = []): void
30 {
31 $this->writeData($this->getInputArgumentData($argument), $options);
32 }
33
34 protected function describeInputOption(InputOption $option, array $options = []): void
35 {
36 $this->writeData($this->getInputOptionData($option), $options);
37 if ($option->isNegatable()) {
38 $this->writeData($this->getInputOptionData($option, true), $options);
39 }
40 }
41
42 protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
43 {
44 $this->writeData($this->getInputDefinitionData($definition), $options);
45 }
46
47 protected function describeCommand(Command $command, array $options = []): void
48 {
49 $this->writeData($this->getCommandData($command, $options['short'] ?? false), $options);
50 }
51
52 protected function describeApplication(Application $application, array $options = []): void
53 {
54 $describedNamespace = $options['namespace'] ?? null;
55 $description = new ApplicationDescription($application, $describedNamespace, true);
56 $commands = [];
57
58 foreach ($description->getCommands() as $command) {
59 $commands[] = $this->getCommandData($command, $options['short'] ?? false);
60 }
61
62 $data = [];
63 if ('UNKNOWN' !== $application->getName()) {
64 $data['application']['name'] = $application->getName();
65 if ('UNKNOWN' !== $application->getVersion()) {
66 $data['application']['version'] = $application->getVersion();
67 }
68 }
69
70 $data['commands'] = $commands;
71
72 if ($describedNamespace) {
73 $data['namespace'] = $describedNamespace;
74 } else {
75 $data['namespaces'] = array_values($description->getNamespaces());
76 }
77
78 $this->writeData($data, $options);
79 }
80
81 /**
82 * Writes data as json.
83 */
84 private function writeData(array $data, array $options): void
85 {
86 $flags = $options['json_encoding'] ?? 0;
87
88 $this->write(json_encode($data, $flags));
89 }
90
91 private function getInputArgumentData(InputArgument $argument): array
92 {
93 return [
94 'name' => $argument->getName(),
95 'is_required' => $argument->isRequired(),
96 'is_array' => $argument->isArray(),
97 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()),
98 'default' => \INF === $argument->getDefault() ? 'INF' : $argument->getDefault(),
99 ];
100 }
101
102 private function getInputOptionData(InputOption $option, bool $negated = false): array
103 {
104 return $negated ? [
105 'name' => '--no-'.$option->getName(),
106 'shortcut' => '',
107 'accept_value' => false,
108 'is_value_required' => false,
109 'is_multiple' => false,
110 'description' => 'Negate the "--'.$option->getName().'" option',
111 'default' => false,
112 ] : [
113 'name' => '--'.$option->getName(),
114 'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $option->getShortcut()) : '',
115 'accept_value' => $option->acceptValue(),
116 'is_value_required' => $option->isValueRequired(),
117 'is_multiple' => $option->isArray(),
118 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()),
119 'default' => \INF === $option->getDefault() ? 'INF' : $option->getDefault(),
120 ];
121 }
122
123 private function getInputDefinitionData(InputDefinition $definition): array
124 {
125 $inputArguments = [];
126 foreach ($definition->getArguments() as $name => $argument) {
127 $inputArguments[$name] = $this->getInputArgumentData($argument);
128 }
129
130 $inputOptions = [];
131 foreach ($definition->getOptions() as $name => $option) {
132 $inputOptions[$name] = $this->getInputOptionData($option);
133 if ($option->isNegatable()) {
134 $inputOptions['no-'.$name] = $this->getInputOptionData($option, true);
135 }
136 }
137
138 return ['arguments' => $inputArguments, 'options' => $inputOptions];
139 }
140
141 private function getCommandData(Command $command, bool $short = false): array
142 {
143 $data = [
144 'name' => $command->getName(),
145 'description' => $command->getDescription(),
146 ];
147
148 if ($short) {
149 $data += [
150 'usage' => $command->getAliases(),
151 ];
152 } else {
153 $command->mergeApplicationDefinition(false);
154
155 $data += [
156 'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()),
157 'help' => $command->getProcessedHelp(),
158 'definition' => $this->getInputDefinitionData($command->getDefinition()),
159 ];
160 }
161
162 $data['hidden'] = $command->isHidden();
163
164 return $data;
165 }
166}
diff --git a/vendor/symfony/console/Descriptor/MarkdownDescriptor.php b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php
new file mode 100644
index 0000000..b3f16ee
--- /dev/null
+++ b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php
@@ -0,0 +1,173 @@
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\Descriptor;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Helper\Helper;
17use Symfony\Component\Console\Input\InputArgument;
18use Symfony\Component\Console\Input\InputDefinition;
19use Symfony\Component\Console\Input\InputOption;
20use Symfony\Component\Console\Output\OutputInterface;
21
22/**
23 * Markdown descriptor.
24 *
25 * @author Jean-François Simon <contact@jfsimon.fr>
26 *
27 * @internal
28 */
29class MarkdownDescriptor extends Descriptor
30{
31 public function describe(OutputInterface $output, object $object, array $options = []): void
32 {
33 $decorated = $output->isDecorated();
34 $output->setDecorated(false);
35
36 parent::describe($output, $object, $options);
37
38 $output->setDecorated($decorated);
39 }
40
41 protected function write(string $content, bool $decorated = true): void
42 {
43 parent::write($content, $decorated);
44 }
45
46 protected function describeInputArgument(InputArgument $argument, array $options = []): void
47 {
48 $this->write(
49 '#### `'.($argument->getName() ?: '<none>')."`\n\n"
50 .($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
51 .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
52 .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
53 .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'
54 );
55 }
56
57 protected function describeInputOption(InputOption $option, array $options = []): void
58 {
59 $name = '--'.$option->getName();
60 if ($option->isNegatable()) {
61 $name .= '|--no-'.$option->getName();
62 }
63 if ($option->getShortcut()) {
64 $name .= '|-'.str_replace('|', '|-', $option->getShortcut()).'';
65 }
66
67 $this->write(
68 '#### `'.$name.'`'."\n\n"
69 .($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '')
70 .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
71 .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
72 .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
73 .'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n"
74 .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
75 );
76 }
77
78 protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
79 {
80 if ($showArguments = \count($definition->getArguments()) > 0) {
81 $this->write('### Arguments');
82 foreach ($definition->getArguments() as $argument) {
83 $this->write("\n\n");
84 $this->describeInputArgument($argument);
85 }
86 }
87
88 if (\count($definition->getOptions()) > 0) {
89 if ($showArguments) {
90 $this->write("\n\n");
91 }
92
93 $this->write('### Options');
94 foreach ($definition->getOptions() as $option) {
95 $this->write("\n\n");
96 $this->describeInputOption($option);
97 }
98 }
99 }
100
101 protected function describeCommand(Command $command, array $options = []): void
102 {
103 if ($options['short'] ?? false) {
104 $this->write(
105 '`'.$command->getName()."`\n"
106 .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
107 .($command->getDescription() ? $command->getDescription()."\n\n" : '')
108 .'### Usage'."\n\n"
109 .array_reduce($command->getAliases(), fn ($carry, $usage) => $carry.'* `'.$usage.'`'."\n")
110 );
111
112 return;
113 }
114
115 $command->mergeApplicationDefinition(false);
116
117 $this->write(
118 '`'.$command->getName()."`\n"
119 .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
120 .($command->getDescription() ? $command->getDescription()."\n\n" : '')
121 .'### Usage'."\n\n"
122 .array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), fn ($carry, $usage) => $carry.'* `'.$usage.'`'."\n")
123 );
124
125 if ($help = $command->getProcessedHelp()) {
126 $this->write("\n");
127 $this->write($help);
128 }
129
130 $definition = $command->getDefinition();
131 if ($definition->getOptions() || $definition->getArguments()) {
132 $this->write("\n\n");
133 $this->describeInputDefinition($definition);
134 }
135 }
136
137 protected function describeApplication(Application $application, array $options = []): void
138 {
139 $describedNamespace = $options['namespace'] ?? null;
140 $description = new ApplicationDescription($application, $describedNamespace);
141 $title = $this->getApplicationTitle($application);
142
143 $this->write($title."\n".str_repeat('=', Helper::width($title)));
144
145 foreach ($description->getNamespaces() as $namespace) {
146 if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
147 $this->write("\n\n");
148 $this->write('**'.$namespace['id'].':**');
149 }
150
151 $this->write("\n\n");
152 $this->write(implode("\n", array_map(fn ($commandName) => sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName())), $namespace['commands'])));
153 }
154
155 foreach ($description->getCommands() as $command) {
156 $this->write("\n\n");
157 $this->describeCommand($command, $options);
158 }
159 }
160
161 private function getApplicationTitle(Application $application): string
162 {
163 if ('UNKNOWN' !== $application->getName()) {
164 if ('UNKNOWN' !== $application->getVersion()) {
165 return sprintf('%s %s', $application->getName(), $application->getVersion());
166 }
167
168 return $application->getName();
169 }
170
171 return 'Console Tool';
172 }
173}
diff --git a/vendor/symfony/console/Descriptor/ReStructuredTextDescriptor.php b/vendor/symfony/console/Descriptor/ReStructuredTextDescriptor.php
new file mode 100644
index 0000000..f12fecb
--- /dev/null
+++ b/vendor/symfony/console/Descriptor/ReStructuredTextDescriptor.php
@@ -0,0 +1,272 @@
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\Descriptor;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Helper\Helper;
17use Symfony\Component\Console\Input\InputArgument;
18use Symfony\Component\Console\Input\InputDefinition;
19use Symfony\Component\Console\Input\InputOption;
20use Symfony\Component\Console\Output\OutputInterface;
21use Symfony\Component\String\UnicodeString;
22
23class ReStructuredTextDescriptor extends Descriptor
24{
25 // <h1>
26 private string $partChar = '=';
27 // <h2>
28 private string $chapterChar = '-';
29 // <h3>
30 private string $sectionChar = '~';
31 // <h4>
32 private string $subsectionChar = '.';
33 // <h5>
34 private string $subsubsectionChar = '^';
35 // <h6>
36 private string $paragraphsChar = '"';
37
38 private array $visibleNamespaces = [];
39
40 public function describe(OutputInterface $output, object $object, array $options = []): void
41 {
42 $decorated = $output->isDecorated();
43 $output->setDecorated(false);
44
45 parent::describe($output, $object, $options);
46
47 $output->setDecorated($decorated);
48 }
49
50 /**
51 * Override parent method to set $decorated = true.
52 */
53 protected function write(string $content, bool $decorated = true): void
54 {
55 parent::write($content, $decorated);
56 }
57
58 protected function describeInputArgument(InputArgument $argument, array $options = []): void
59 {
60 $this->write(
61 $argument->getName() ?: '<none>'."\n".str_repeat($this->paragraphsChar, Helper::width($argument->getName()))."\n\n"
62 .($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
63 .'- **Is required**: '.($argument->isRequired() ? 'yes' : 'no')."\n"
64 .'- **Is array**: '.($argument->isArray() ? 'yes' : 'no')."\n"
65 .'- **Default**: ``'.str_replace("\n", '', var_export($argument->getDefault(), true)).'``'
66 );
67 }
68
69 protected function describeInputOption(InputOption $option, array $options = []): void
70 {
71 $name = '\-\-'.$option->getName();
72 if ($option->isNegatable()) {
73 $name .= '|\-\-no-'.$option->getName();
74 }
75 if ($option->getShortcut()) {
76 $name .= '|-'.str_replace('|', '|-', $option->getShortcut());
77 }
78
79 $optionDescription = $option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n\n", $option->getDescription())."\n\n" : '';
80 $optionDescription = (new UnicodeString($optionDescription))->ascii();
81 $this->write(
82 $name."\n".str_repeat($this->paragraphsChar, Helper::width($name))."\n\n"
83 .$optionDescription
84 .'- **Accept value**: '.($option->acceptValue() ? 'yes' : 'no')."\n"
85 .'- **Is value required**: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
86 .'- **Is multiple**: '.($option->isArray() ? 'yes' : 'no')."\n"
87 .'- **Is negatable**: '.($option->isNegatable() ? 'yes' : 'no')."\n"
88 .'- **Default**: ``'.str_replace("\n", '', var_export($option->getDefault(), true)).'``'."\n"
89 );
90 }
91
92 protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
93 {
94 if ($showArguments = ((bool) $definition->getArguments())) {
95 $this->write("Arguments\n".str_repeat($this->subsubsectionChar, 9))."\n\n";
96 foreach ($definition->getArguments() as $argument) {
97 $this->write("\n\n");
98 $this->describeInputArgument($argument);
99 }
100 }
101
102 if ($nonDefaultOptions = $this->getNonDefaultOptions($definition)) {
103 if ($showArguments) {
104 $this->write("\n\n");
105 }
106
107 $this->write("Options\n".str_repeat($this->subsubsectionChar, 7)."\n\n");
108 foreach ($nonDefaultOptions as $option) {
109 $this->describeInputOption($option);
110 $this->write("\n");
111 }
112 }
113 }
114
115 protected function describeCommand(Command $command, array $options = []): void
116 {
117 if ($options['short'] ?? false) {
118 $this->write(
119 '``'.$command->getName()."``\n"
120 .str_repeat($this->subsectionChar, Helper::width($command->getName()))."\n\n"
121 .($command->getDescription() ? $command->getDescription()."\n\n" : '')
122 ."Usage\n".str_repeat($this->paragraphsChar, 5)."\n\n"
123 .array_reduce($command->getAliases(), static fn ($carry, $usage) => $carry.'- ``'.$usage.'``'."\n")
124 );
125
126 return;
127 }
128
129 $command->mergeApplicationDefinition(false);
130
131 foreach ($command->getAliases() as $alias) {
132 $this->write('.. _'.$alias.":\n\n");
133 }
134 $this->write(
135 $command->getName()."\n"
136 .str_repeat($this->subsectionChar, Helper::width($command->getName()))."\n\n"
137 .($command->getDescription() ? $command->getDescription()."\n\n" : '')
138 ."Usage\n".str_repeat($this->subsubsectionChar, 5)."\n\n"
139 .array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), static fn ($carry, $usage) => $carry.'- ``'.$usage.'``'."\n")
140 );
141
142 if ($help = $command->getProcessedHelp()) {
143 $this->write("\n");
144 $this->write($help);
145 }
146
147 $definition = $command->getDefinition();
148 if ($definition->getOptions() || $definition->getArguments()) {
149 $this->write("\n\n");
150 $this->describeInputDefinition($definition);
151 }
152 }
153
154 protected function describeApplication(Application $application, array $options = []): void
155 {
156 $description = new ApplicationDescription($application, $options['namespace'] ?? null);
157 $title = $this->getApplicationTitle($application);
158
159 $this->write($title."\n".str_repeat($this->partChar, Helper::width($title)));
160 $this->createTableOfContents($description, $application);
161 $this->describeCommands($application, $options);
162 }
163
164 private function getApplicationTitle(Application $application): string
165 {
166 if ('UNKNOWN' === $application->getName()) {
167 return 'Console Tool';
168 }
169 if ('UNKNOWN' !== $application->getVersion()) {
170 return sprintf('%s %s', $application->getName(), $application->getVersion());
171 }
172
173 return $application->getName();
174 }
175
176 private function describeCommands($application, array $options): void
177 {
178 $title = 'Commands';
179 $this->write("\n\n$title\n".str_repeat($this->chapterChar, Helper::width($title))."\n\n");
180 foreach ($this->visibleNamespaces as $namespace) {
181 if ('_global' === $namespace) {
182 $commands = $application->all('');
183 $this->write('Global'."\n".str_repeat($this->sectionChar, Helper::width('Global'))."\n\n");
184 } else {
185 $commands = $application->all($namespace);
186 $this->write($namespace."\n".str_repeat($this->sectionChar, Helper::width($namespace))."\n\n");
187 }
188
189 foreach ($this->removeAliasesAndHiddenCommands($commands) as $command) {
190 $this->describeCommand($command, $options);
191 $this->write("\n\n");
192 }
193 }
194 }
195
196 private function createTableOfContents(ApplicationDescription $description, Application $application): void
197 {
198 $this->setVisibleNamespaces($description);
199 $chapterTitle = 'Table of Contents';
200 $this->write("\n\n$chapterTitle\n".str_repeat($this->chapterChar, Helper::width($chapterTitle))."\n\n");
201 foreach ($this->visibleNamespaces as $namespace) {
202 if ('_global' === $namespace) {
203 $commands = $application->all('');
204 } else {
205 $commands = $application->all($namespace);
206 $this->write("\n\n");
207 $this->write($namespace."\n".str_repeat($this->sectionChar, Helper::width($namespace))."\n\n");
208 }
209 $commands = $this->removeAliasesAndHiddenCommands($commands);
210
211 $this->write("\n\n");
212 $this->write(implode("\n", array_map(static fn ($commandName) => sprintf('- `%s`_', $commandName), array_keys($commands))));
213 }
214 }
215
216 private function getNonDefaultOptions(InputDefinition $definition): array
217 {
218 $globalOptions = [
219 'help',
220 'quiet',
221 'verbose',
222 'version',
223 'ansi',
224 'no-interaction',
225 ];
226 $nonDefaultOptions = [];
227 foreach ($definition->getOptions() as $option) {
228 // Skip global options.
229 if (!\in_array($option->getName(), $globalOptions, true)) {
230 $nonDefaultOptions[] = $option;
231 }
232 }
233
234 return $nonDefaultOptions;
235 }
236
237 private function setVisibleNamespaces(ApplicationDescription $description): void
238 {
239 $commands = $description->getCommands();
240 foreach ($description->getNamespaces() as $namespace) {
241 try {
242 $namespaceCommands = $namespace['commands'];
243 foreach ($namespaceCommands as $key => $commandName) {
244 if (!\array_key_exists($commandName, $commands)) {
245 // If the array key does not exist, then this is an alias.
246 unset($namespaceCommands[$key]);
247 } elseif ($commands[$commandName]->isHidden()) {
248 unset($namespaceCommands[$key]);
249 }
250 }
251 if (!$namespaceCommands) {
252 // If the namespace contained only aliases or hidden commands, skip the namespace.
253 continue;
254 }
255 } catch (\Exception) {
256 }
257 $this->visibleNamespaces[] = $namespace['id'];
258 }
259 }
260
261 private function removeAliasesAndHiddenCommands(array $commands): array
262 {
263 foreach ($commands as $key => $command) {
264 if ($command->isHidden() || \in_array($key, $command->getAliases(), true)) {
265 unset($commands[$key]);
266 }
267 }
268 unset($commands['completion']);
269
270 return $commands;
271 }
272}
diff --git a/vendor/symfony/console/Descriptor/TextDescriptor.php b/vendor/symfony/console/Descriptor/TextDescriptor.php
new file mode 100644
index 0000000..d04d102
--- /dev/null
+++ b/vendor/symfony/console/Descriptor/TextDescriptor.php
@@ -0,0 +1,317 @@
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\Descriptor;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Formatter\OutputFormatter;
17use Symfony\Component\Console\Helper\Helper;
18use Symfony\Component\Console\Input\InputArgument;
19use Symfony\Component\Console\Input\InputDefinition;
20use Symfony\Component\Console\Input\InputOption;
21
22/**
23 * Text descriptor.
24 *
25 * @author Jean-François Simon <contact@jfsimon.fr>
26 *
27 * @internal
28 */
29class TextDescriptor extends Descriptor
30{
31 protected function describeInputArgument(InputArgument $argument, array $options = []): void
32 {
33 if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) {
34 $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
35 } else {
36 $default = '';
37 }
38
39 $totalWidth = $options['total_width'] ?? Helper::width($argument->getName());
40 $spacingWidth = $totalWidth - \strlen($argument->getName());
41
42 $this->writeText(sprintf(' <info>%s</info> %s%s%s',
43 $argument->getName(),
44 str_repeat(' ', $spacingWidth),
45 // + 4 = 2 spaces before <info>, 2 spaces after </info>
46 preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $argument->getDescription()),
47 $default
48 ), $options);
49 }
50
51 protected function describeInputOption(InputOption $option, array $options = []): void
52 {
53 if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) {
54 $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
55 } else {
56 $default = '';
57 }
58
59 $value = '';
60 if ($option->acceptValue()) {
61 $value = '='.strtoupper($option->getName());
62
63 if ($option->isValueOptional()) {
64 $value = '['.$value.']';
65 }
66 }
67
68 $totalWidth = $options['total_width'] ?? $this->calculateTotalWidthForOptions([$option]);
69 $synopsis = sprintf('%s%s',
70 $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ',
71 sprintf($option->isNegatable() ? '--%1$s|--no-%1$s' : '--%1$s%2$s', $option->getName(), $value)
72 );
73
74 $spacingWidth = $totalWidth - Helper::width($synopsis);
75
76 $this->writeText(sprintf(' <info>%s</info> %s%s%s%s',
77 $synopsis,
78 str_repeat(' ', $spacingWidth),
79 // + 4 = 2 spaces before <info>, 2 spaces after </info>
80 preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $option->getDescription()),
81 $default,
82 $option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
83 ), $options);
84 }
85
86 protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
87 {
88 $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
89 foreach ($definition->getArguments() as $argument) {
90 $totalWidth = max($totalWidth, Helper::width($argument->getName()));
91 }
92
93 if ($definition->getArguments()) {
94 $this->writeText('<comment>Arguments:</comment>', $options);
95 $this->writeText("\n");
96 foreach ($definition->getArguments() as $argument) {
97 $this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth]));
98 $this->writeText("\n");
99 }
100 }
101
102 if ($definition->getArguments() && $definition->getOptions()) {
103 $this->writeText("\n");
104 }
105
106 if ($definition->getOptions()) {
107 $laterOptions = [];
108
109 $this->writeText('<comment>Options:</comment>', $options);
110 foreach ($definition->getOptions() as $option) {
111 if (\strlen($option->getShortcut() ?? '') > 1) {
112 $laterOptions[] = $option;
113 continue;
114 }
115 $this->writeText("\n");
116 $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
117 }
118 foreach ($laterOptions as $option) {
119 $this->writeText("\n");
120 $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
121 }
122 }
123 }
124
125 protected function describeCommand(Command $command, array $options = []): void
126 {
127 $command->mergeApplicationDefinition(false);
128
129 if ($description = $command->getDescription()) {
130 $this->writeText('<comment>Description:</comment>', $options);
131 $this->writeText("\n");
132 $this->writeText(' '.$description);
133 $this->writeText("\n\n");
134 }
135
136 $this->writeText('<comment>Usage:</comment>', $options);
137 foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) {
138 $this->writeText("\n");
139 $this->writeText(' '.OutputFormatter::escape($usage), $options);
140 }
141 $this->writeText("\n");
142
143 $definition = $command->getDefinition();
144 if ($definition->getOptions() || $definition->getArguments()) {
145 $this->writeText("\n");
146 $this->describeInputDefinition($definition, $options);
147 $this->writeText("\n");
148 }
149
150 $help = $command->getProcessedHelp();
151 if ($help && $help !== $description) {
152 $this->writeText("\n");
153 $this->writeText('<comment>Help:</comment>', $options);
154 $this->writeText("\n");
155 $this->writeText(' '.str_replace("\n", "\n ", $help), $options);
156 $this->writeText("\n");
157 }
158 }
159
160 protected function describeApplication(Application $application, array $options = []): void
161 {
162 $describedNamespace = $options['namespace'] ?? null;
163 $description = new ApplicationDescription($application, $describedNamespace);
164
165 if (isset($options['raw_text']) && $options['raw_text']) {
166 $width = $this->getColumnWidth($description->getCommands());
167
168 foreach ($description->getCommands() as $command) {
169 $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options);
170 $this->writeText("\n");
171 }
172 } else {
173 if ('' != $help = $application->getHelp()) {
174 $this->writeText("$help\n\n", $options);
175 }
176
177 $this->writeText("<comment>Usage:</comment>\n", $options);
178 $this->writeText(" command [options] [arguments]\n\n", $options);
179
180 $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options);
181
182 $this->writeText("\n");
183 $this->writeText("\n");
184
185 $commands = $description->getCommands();
186 $namespaces = $description->getNamespaces();
187 if ($describedNamespace && $namespaces) {
188 // make sure all alias commands are included when describing a specific namespace
189 $describedNamespaceInfo = reset($namespaces);
190 foreach ($describedNamespaceInfo['commands'] as $name) {
191 $commands[$name] = $description->getCommand($name);
192 }
193 }
194
195 // calculate max. width based on available commands per namespace
196 $width = $this->getColumnWidth(array_merge(...array_values(array_map(fn ($namespace) => array_intersect($namespace['commands'], array_keys($commands)), array_values($namespaces)))));
197
198 if ($describedNamespace) {
199 $this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
200 } else {
201 $this->writeText('<comment>Available commands:</comment>', $options);
202 }
203
204 foreach ($namespaces as $namespace) {
205 $namespace['commands'] = array_filter($namespace['commands'], fn ($name) => isset($commands[$name]));
206
207 if (!$namespace['commands']) {
208 continue;
209 }
210
211 if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
212 $this->writeText("\n");
213 $this->writeText(' <comment>'.$namespace['id'].'</comment>', $options);
214 }
215
216 foreach ($namespace['commands'] as $name) {
217 $this->writeText("\n");
218 $spacingWidth = $width - Helper::width($name);
219 $command = $commands[$name];
220 $commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : '';
221 $this->writeText(sprintf(' <info>%s</info>%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options);
222 }
223 }
224
225 $this->writeText("\n");
226 }
227 }
228
229 private function writeText(string $content, array $options = []): void
230 {
231 $this->write(
232 isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
233 isset($options['raw_output']) ? !$options['raw_output'] : true
234 );
235 }
236
237 /**
238 * Formats command aliases to show them in the command description.
239 */
240 private function getCommandAliasesText(Command $command): string
241 {
242 $text = '';
243 $aliases = $command->getAliases();
244
245 if ($aliases) {
246 $text = '['.implode('|', $aliases).'] ';
247 }
248
249 return $text;
250 }
251
252 /**
253 * Formats input option/argument default value.
254 */
255 private function formatDefaultValue(mixed $default): string
256 {
257 if (\INF === $default) {
258 return 'INF';
259 }
260
261 if (\is_string($default)) {
262 $default = OutputFormatter::escape($default);
263 } elseif (\is_array($default)) {
264 foreach ($default as $key => $value) {
265 if (\is_string($value)) {
266 $default[$key] = OutputFormatter::escape($value);
267 }
268 }
269 }
270
271 return str_replace('\\\\', '\\', json_encode($default, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE));
272 }
273
274 /**
275 * @param array<Command|string> $commands
276 */
277 private function getColumnWidth(array $commands): int
278 {
279 $widths = [];
280
281 foreach ($commands as $command) {
282 if ($command instanceof Command) {
283 $widths[] = Helper::width($command->getName());
284 foreach ($command->getAliases() as $alias) {
285 $widths[] = Helper::width($alias);
286 }
287 } else {
288 $widths[] = Helper::width($command);
289 }
290 }
291
292 return $widths ? max($widths) + 2 : 0;
293 }
294
295 /**
296 * @param InputOption[] $options
297 */
298 private function calculateTotalWidthForOptions(array $options): int
299 {
300 $totalWidth = 0;
301 foreach ($options as $option) {
302 // "-" + shortcut + ", --" + name
303 $nameLength = 1 + max(Helper::width($option->getShortcut()), 1) + 4 + Helper::width($option->getName());
304 if ($option->isNegatable()) {
305 $nameLength += 6 + Helper::width($option->getName()); // |--no- + name
306 } elseif ($option->acceptValue()) {
307 $valueLength = 1 + Helper::width($option->getName()); // = + value
308 $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ]
309
310 $nameLength += $valueLength;
311 }
312 $totalWidth = max($totalWidth, $nameLength);
313 }
314
315 return $totalWidth;
316 }
317}
diff --git a/vendor/symfony/console/Descriptor/XmlDescriptor.php b/vendor/symfony/console/Descriptor/XmlDescriptor.php
new file mode 100644
index 0000000..8e44c88
--- /dev/null
+++ b/vendor/symfony/console/Descriptor/XmlDescriptor.php
@@ -0,0 +1,232 @@
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\Descriptor;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Input\InputArgument;
17use Symfony\Component\Console\Input\InputDefinition;
18use Symfony\Component\Console\Input\InputOption;
19
20/**
21 * XML descriptor.
22 *
23 * @author Jean-François Simon <contact@jfsimon.fr>
24 *
25 * @internal
26 */
27class XmlDescriptor extends Descriptor
28{
29 public function getInputDefinitionDocument(InputDefinition $definition): \DOMDocument
30 {
31 $dom = new \DOMDocument('1.0', 'UTF-8');
32 $dom->appendChild($definitionXML = $dom->createElement('definition'));
33
34 $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
35 foreach ($definition->getArguments() as $argument) {
36 $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument));
37 }
38
39 $definitionXML->appendChild($optionsXML = $dom->createElement('options'));
40 foreach ($definition->getOptions() as $option) {
41 $this->appendDocument($optionsXML, $this->getInputOptionDocument($option));
42 }
43
44 return $dom;
45 }
46
47 public function getCommandDocument(Command $command, bool $short = false): \DOMDocument
48 {
49 $dom = new \DOMDocument('1.0', 'UTF-8');
50 $dom->appendChild($commandXML = $dom->createElement('command'));
51
52 $commandXML->setAttribute('id', $command->getName());
53 $commandXML->setAttribute('name', $command->getName());
54 $commandXML->setAttribute('hidden', $command->isHidden() ? 1 : 0);
55
56 $commandXML->appendChild($usagesXML = $dom->createElement('usages'));
57
58 $commandXML->appendChild($descriptionXML = $dom->createElement('description'));
59 $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription())));
60
61 if ($short) {
62 foreach ($command->getAliases() as $usage) {
63 $usagesXML->appendChild($dom->createElement('usage', $usage));
64 }
65 } else {
66 $command->mergeApplicationDefinition(false);
67
68 foreach (array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()) as $usage) {
69 $usagesXML->appendChild($dom->createElement('usage', $usage));
70 }
71
72 $commandXML->appendChild($helpXML = $dom->createElement('help'));
73 $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp())));
74
75 $definitionXML = $this->getInputDefinitionDocument($command->getDefinition());
76 $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0));
77 }
78
79 return $dom;
80 }
81
82 public function getApplicationDocument(Application $application, ?string $namespace = null, bool $short = false): \DOMDocument
83 {
84 $dom = new \DOMDocument('1.0', 'UTF-8');
85 $dom->appendChild($rootXml = $dom->createElement('symfony'));
86
87 if ('UNKNOWN' !== $application->getName()) {
88 $rootXml->setAttribute('name', $application->getName());
89 if ('UNKNOWN' !== $application->getVersion()) {
90 $rootXml->setAttribute('version', $application->getVersion());
91 }
92 }
93
94 $rootXml->appendChild($commandsXML = $dom->createElement('commands'));
95
96 $description = new ApplicationDescription($application, $namespace, true);
97
98 if ($namespace) {
99 $commandsXML->setAttribute('namespace', $namespace);
100 }
101
102 foreach ($description->getCommands() as $command) {
103 $this->appendDocument($commandsXML, $this->getCommandDocument($command, $short));
104 }
105
106 if (!$namespace) {
107 $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces'));
108
109 foreach ($description->getNamespaces() as $namespaceDescription) {
110 $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace'));
111 $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']);
112
113 foreach ($namespaceDescription['commands'] as $name) {
114 $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command'));
115 $commandXML->appendChild($dom->createTextNode($name));
116 }
117 }
118 }
119
120 return $dom;
121 }
122
123 protected function describeInputArgument(InputArgument $argument, array $options = []): void
124 {
125 $this->writeDocument($this->getInputArgumentDocument($argument));
126 }
127
128 protected function describeInputOption(InputOption $option, array $options = []): void
129 {
130 $this->writeDocument($this->getInputOptionDocument($option));
131 }
132
133 protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
134 {
135 $this->writeDocument($this->getInputDefinitionDocument($definition));
136 }
137
138 protected function describeCommand(Command $command, array $options = []): void
139 {
140 $this->writeDocument($this->getCommandDocument($command, $options['short'] ?? false));
141 }
142
143 protected function describeApplication(Application $application, array $options = []): void
144 {
145 $this->writeDocument($this->getApplicationDocument($application, $options['namespace'] ?? null, $options['short'] ?? false));
146 }
147
148 /**
149 * Appends document children to parent node.
150 */
151 private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent): void
152 {
153 foreach ($importedParent->childNodes as $childNode) {
154 $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true));
155 }
156 }
157
158 /**
159 * Writes DOM document.
160 */
161 private function writeDocument(\DOMDocument $dom): void
162 {
163 $dom->formatOutput = true;
164 $this->write($dom->saveXML());
165 }
166
167 private function getInputArgumentDocument(InputArgument $argument): \DOMDocument
168 {
169 $dom = new \DOMDocument('1.0', 'UTF-8');
170
171 $dom->appendChild($objectXML = $dom->createElement('argument'));
172 $objectXML->setAttribute('name', $argument->getName());
173 $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
174 $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
175 $objectXML->appendChild($descriptionXML = $dom->createElement('description'));
176 $descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));
177
178 $objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
179 $defaults = \is_array($argument->getDefault()) ? $argument->getDefault() : (\is_bool($argument->getDefault()) ? [var_export($argument->getDefault(), true)] : ($argument->getDefault() ? [$argument->getDefault()] : []));
180 foreach ($defaults as $default) {
181 $defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
182 $defaultXML->appendChild($dom->createTextNode($default));
183 }
184
185 return $dom;
186 }
187
188 private function getInputOptionDocument(InputOption $option): \DOMDocument
189 {
190 $dom = new \DOMDocument('1.0', 'UTF-8');
191
192 $dom->appendChild($objectXML = $dom->createElement('option'));
193 $objectXML->setAttribute('name', '--'.$option->getName());
194 $pos = strpos($option->getShortcut() ?? '', '|');
195 if (false !== $pos) {
196 $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos));
197 $objectXML->setAttribute('shortcuts', '-'.str_replace('|', '|-', $option->getShortcut()));
198 } else {
199 $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
200 }
201 $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
202 $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
203 $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
204 $objectXML->appendChild($descriptionXML = $dom->createElement('description'));
205 $descriptionXML->appendChild($dom->createTextNode($option->getDescription()));
206
207 if ($option->acceptValue()) {
208 $defaults = \is_array($option->getDefault()) ? $option->getDefault() : (\is_bool($option->getDefault()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : []));
209 $objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
210
211 if ($defaults) {
212 foreach ($defaults as $default) {
213 $defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
214 $defaultXML->appendChild($dom->createTextNode($default));
215 }
216 }
217 }
218
219 if ($option->isNegatable()) {
220 $dom->appendChild($objectXML = $dom->createElement('option'));
221 $objectXML->setAttribute('name', '--no-'.$option->getName());
222 $objectXML->setAttribute('shortcut', '');
223 $objectXML->setAttribute('accept_value', 0);
224 $objectXML->setAttribute('is_value_required', 0);
225 $objectXML->setAttribute('is_multiple', 0);
226 $objectXML->appendChild($descriptionXML = $dom->createElement('description'));
227 $descriptionXML->appendChild($dom->createTextNode('Negate the "--'.$option->getName().'" option'));
228 }
229
230 return $dom;
231 }
232}
diff --git a/vendor/symfony/console/Event/ConsoleCommandEvent.php b/vendor/symfony/console/Event/ConsoleCommandEvent.php
new file mode 100644
index 0000000..0757a23
--- /dev/null
+++ b/vendor/symfony/console/Event/ConsoleCommandEvent.php
@@ -0,0 +1,54 @@
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\Event;
13
14/**
15 * Allows to do things before the command is executed, like skipping the command or executing code before the command is
16 * going to be executed.
17 *
18 * Changing the input arguments will have no effect.
19 *
20 * @author Fabien Potencier <fabien@symfony.com>
21 */
22final class ConsoleCommandEvent extends ConsoleEvent
23{
24 /**
25 * The return code for skipped commands, this will also be passed into the terminate event.
26 */
27 public const RETURN_CODE_DISABLED = 113;
28
29 /**
30 * Indicates if the command should be run or skipped.
31 */
32 private bool $commandShouldRun = true;
33
34 /**
35 * Disables the command, so it won't be run.
36 */
37 public function disableCommand(): bool
38 {
39 return $this->commandShouldRun = false;
40 }
41
42 public function enableCommand(): bool
43 {
44 return $this->commandShouldRun = true;
45 }
46
47 /**
48 * Returns true if the command is runnable, false otherwise.
49 */
50 public function commandShouldRun(): bool
51 {
52 return $this->commandShouldRun;
53 }
54}
diff --git a/vendor/symfony/console/Event/ConsoleErrorEvent.php b/vendor/symfony/console/Event/ConsoleErrorEvent.php
new file mode 100644
index 0000000..1c0d626
--- /dev/null
+++ b/vendor/symfony/console/Event/ConsoleErrorEvent.php
@@ -0,0 +1,58 @@
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\Event;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Input\InputInterface;
16use Symfony\Component\Console\Output\OutputInterface;
17
18/**
19 * Allows to handle throwables thrown while running a command.
20 *
21 * @author Wouter de Jong <wouter@wouterj.nl>
22 */
23final class ConsoleErrorEvent extends ConsoleEvent
24{
25 private int $exitCode;
26
27 public function __construct(
28 InputInterface $input,
29 OutputInterface $output,
30 private \Throwable $error,
31 ?Command $command = null,
32 ) {
33 parent::__construct($command, $input, $output);
34 }
35
36 public function getError(): \Throwable
37 {
38 return $this->error;
39 }
40
41 public function setError(\Throwable $error): void
42 {
43 $this->error = $error;
44 }
45
46 public function setExitCode(int $exitCode): void
47 {
48 $this->exitCode = $exitCode;
49
50 $r = new \ReflectionProperty($this->error, 'code');
51 $r->setValue($this->error, $this->exitCode);
52 }
53
54 public function getExitCode(): int
55 {
56 return $this->exitCode ?? (\is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 1);
57 }
58}
diff --git a/vendor/symfony/console/Event/ConsoleEvent.php b/vendor/symfony/console/Event/ConsoleEvent.php
new file mode 100644
index 0000000..2f9f077
--- /dev/null
+++ b/vendor/symfony/console/Event/ConsoleEvent.php
@@ -0,0 +1,56 @@
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\Event;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Input\InputInterface;
16use Symfony\Component\Console\Output\OutputInterface;
17use Symfony\Contracts\EventDispatcher\Event;
18
19/**
20 * Allows to inspect input and output of a command.
21 *
22 * @author Francesco Levorato <git@flevour.net>
23 */
24class ConsoleEvent extends Event
25{
26 public function __construct(
27 protected ?Command $command,
28 private InputInterface $input,
29 private OutputInterface $output,
30 ) {
31 }
32
33 /**
34 * Gets the command that is executed.
35 */
36 public function getCommand(): ?Command
37 {
38 return $this->command;
39 }
40
41 /**
42 * Gets the input instance.
43 */
44 public function getInput(): InputInterface
45 {
46 return $this->input;
47 }
48
49 /**
50 * Gets the output instance.
51 */
52 public function getOutput(): OutputInterface
53 {
54 return $this->output;
55 }
56}
diff --git a/vendor/symfony/console/Event/ConsoleSignalEvent.php b/vendor/symfony/console/Event/ConsoleSignalEvent.php
new file mode 100644
index 0000000..b27f08a
--- /dev/null
+++ b/vendor/symfony/console/Event/ConsoleSignalEvent.php
@@ -0,0 +1,56 @@
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\Event;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Input\InputInterface;
16use Symfony\Component\Console\Output\OutputInterface;
17
18/**
19 * @author marie <marie@users.noreply.github.com>
20 */
21final class ConsoleSignalEvent extends ConsoleEvent
22{
23 public function __construct(
24 Command $command,
25 InputInterface $input,
26 OutputInterface $output,
27 private int $handlingSignal,
28 private int|false $exitCode = 0,
29 ) {
30 parent::__construct($command, $input, $output);
31 }
32
33 public function getHandlingSignal(): int
34 {
35 return $this->handlingSignal;
36 }
37
38 public function setExitCode(int $exitCode): void
39 {
40 if ($exitCode < 0 || $exitCode > 255) {
41 throw new \InvalidArgumentException('Exit code must be between 0 and 255.');
42 }
43
44 $this->exitCode = $exitCode;
45 }
46
47 public function abortExit(): void
48 {
49 $this->exitCode = false;
50 }
51
52 public function getExitCode(): int|false
53 {
54 return $this->exitCode;
55 }
56}
diff --git a/vendor/symfony/console/Event/ConsoleTerminateEvent.php b/vendor/symfony/console/Event/ConsoleTerminateEvent.php
new file mode 100644
index 0000000..38f7253
--- /dev/null
+++ b/vendor/symfony/console/Event/ConsoleTerminateEvent.php
@@ -0,0 +1,50 @@
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\Event;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Input\InputInterface;
16use Symfony\Component\Console\Output\OutputInterface;
17
18/**
19 * Allows to manipulate the exit code of a command after its execution.
20 *
21 * @author Francesco Levorato <git@flevour.net>
22 * @author Jules Pietri <jules@heahprod.com>
23 */
24final class ConsoleTerminateEvent extends ConsoleEvent
25{
26 public function __construct(
27 Command $command,
28 InputInterface $input,
29 OutputInterface $output,
30 private int $exitCode,
31 private readonly ?int $interruptingSignal = null,
32 ) {
33 parent::__construct($command, $input, $output);
34 }
35
36 public function setExitCode(int $exitCode): void
37 {
38 $this->exitCode = $exitCode;
39 }
40
41 public function getExitCode(): int
42 {
43 return $this->exitCode;
44 }
45
46 public function getInterruptingSignal(): ?int
47 {
48 return $this->interruptingSignal;
49 }
50}
diff --git a/vendor/symfony/console/EventListener/ErrorListener.php b/vendor/symfony/console/EventListener/ErrorListener.php
new file mode 100644
index 0000000..49915a4
--- /dev/null
+++ b/vendor/symfony/console/EventListener/ErrorListener.php
@@ -0,0 +1,93 @@
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\EventListener;
13
14use Psr\Log\LoggerInterface;
15use Symfony\Component\Console\ConsoleEvents;
16use Symfony\Component\Console\Event\ConsoleErrorEvent;
17use Symfony\Component\Console\Event\ConsoleEvent;
18use Symfony\Component\Console\Event\ConsoleTerminateEvent;
19use Symfony\Component\EventDispatcher\EventSubscriberInterface;
20
21/**
22 * @author James Halsall <james.t.halsall@googlemail.com>
23 * @author Robin Chalas <robin.chalas@gmail.com>
24 */
25class ErrorListener implements EventSubscriberInterface
26{
27 public function __construct(
28 private ?LoggerInterface $logger = null,
29 ) {
30 }
31
32 public function onConsoleError(ConsoleErrorEvent $event): void
33 {
34 if (null === $this->logger) {
35 return;
36 }
37
38 $error = $event->getError();
39
40 if (!$inputString = $this->getInputString($event)) {
41 $this->logger->critical('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => $error->getMessage()]);
42
43 return;
44 }
45
46 $this->logger->critical('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $error, 'command' => $inputString, 'message' => $error->getMessage()]);
47 }
48
49 public function onConsoleTerminate(ConsoleTerminateEvent $event): void
50 {
51 if (null === $this->logger) {
52 return;
53 }
54
55 $exitCode = $event->getExitCode();
56
57 if (0 === $exitCode) {
58 return;
59 }
60
61 if (!$inputString = $this->getInputString($event)) {
62 $this->logger->debug('The console exited with code "{code}"', ['code' => $exitCode]);
63
64 return;
65 }
66
67 $this->logger->debug('Command "{command}" exited with code "{code}"', ['command' => $inputString, 'code' => $exitCode]);
68 }
69
70 public static function getSubscribedEvents(): array
71 {
72 return [
73 ConsoleEvents::ERROR => ['onConsoleError', -128],
74 ConsoleEvents::TERMINATE => ['onConsoleTerminate', -128],
75 ];
76 }
77
78 private static function getInputString(ConsoleEvent $event): ?string
79 {
80 $commandName = $event->getCommand()?->getName();
81 $input = $event->getInput();
82
83 if ($input instanceof \Stringable) {
84 if ($commandName) {
85 return str_replace(["'$commandName'", "\"$commandName\""], $commandName, (string) $input);
86 }
87
88 return (string) $input;
89 }
90
91 return $commandName;
92 }
93}
diff --git a/vendor/symfony/console/Exception/CommandNotFoundException.php b/vendor/symfony/console/Exception/CommandNotFoundException.php
new file mode 100644
index 0000000..246f04f
--- /dev/null
+++ b/vendor/symfony/console/Exception/CommandNotFoundException.php
@@ -0,0 +1,43 @@
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\Exception;
13
14/**
15 * Represents an incorrect command name typed in the console.
16 *
17 * @author Jérôme Tamarelle <jerome@tamarelle.net>
18 */
19class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface
20{
21 /**
22 * @param string $message Exception message to throw
23 * @param string[] $alternatives List of similar defined names
24 * @param int $code Exception code
25 * @param \Throwable|null $previous Previous exception used for the exception chaining
26 */
27 public function __construct(
28 string $message,
29 private array $alternatives = [],
30 int $code = 0,
31 ?\Throwable $previous = null,
32 ) {
33 parent::__construct($message, $code, $previous);
34 }
35
36 /**
37 * @return string[]
38 */
39 public function getAlternatives(): array
40 {
41 return $this->alternatives;
42 }
43}
diff --git a/vendor/symfony/console/Exception/ExceptionInterface.php b/vendor/symfony/console/Exception/ExceptionInterface.php
new file mode 100644
index 0000000..1624e13
--- /dev/null
+++ b/vendor/symfony/console/Exception/ExceptionInterface.php
@@ -0,0 +1,21 @@
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\Exception;
13
14/**
15 * ExceptionInterface.
16 *
17 * @author Jérôme Tamarelle <jerome@tamarelle.net>
18 */
19interface ExceptionInterface extends \Throwable
20{
21}
diff --git a/vendor/symfony/console/Exception/InvalidArgumentException.php b/vendor/symfony/console/Exception/InvalidArgumentException.php
new file mode 100644
index 0000000..07cc0b6
--- /dev/null
+++ b/vendor/symfony/console/Exception/InvalidArgumentException.php
@@ -0,0 +1,19 @@
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\Exception;
13
14/**
15 * @author Jérôme Tamarelle <jerome@tamarelle.net>
16 */
17class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
18{
19}
diff --git a/vendor/symfony/console/Exception/InvalidOptionException.php b/vendor/symfony/console/Exception/InvalidOptionException.php
new file mode 100644
index 0000000..5cf6279
--- /dev/null
+++ b/vendor/symfony/console/Exception/InvalidOptionException.php
@@ -0,0 +1,21 @@
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\Exception;
13
14/**
15 * Represents an incorrect option name or value typed in the console.
16 *
17 * @author Jérôme Tamarelle <jerome@tamarelle.net>
18 */
19class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface
20{
21}
diff --git a/vendor/symfony/console/Exception/LogicException.php b/vendor/symfony/console/Exception/LogicException.php
new file mode 100644
index 0000000..fc37b8d
--- /dev/null
+++ b/vendor/symfony/console/Exception/LogicException.php
@@ -0,0 +1,19 @@
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\Exception;
13
14/**
15 * @author Jérôme Tamarelle <jerome@tamarelle.net>
16 */
17class LogicException extends \LogicException implements ExceptionInterface
18{
19}
diff --git a/vendor/symfony/console/Exception/MissingInputException.php b/vendor/symfony/console/Exception/MissingInputException.php
new file mode 100644
index 0000000..04f02ad
--- /dev/null
+++ b/vendor/symfony/console/Exception/MissingInputException.php
@@ -0,0 +1,21 @@
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\Exception;
13
14/**
15 * Represents failure to read input from stdin.
16 *
17 * @author Gabriel Ostrolucký <gabriel.ostrolucky@gmail.com>
18 */
19class MissingInputException extends RuntimeException implements ExceptionInterface
20{
21}
diff --git a/vendor/symfony/console/Exception/NamespaceNotFoundException.php b/vendor/symfony/console/Exception/NamespaceNotFoundException.php
new file mode 100644
index 0000000..dd16e45
--- /dev/null
+++ b/vendor/symfony/console/Exception/NamespaceNotFoundException.php
@@ -0,0 +1,21 @@
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\Exception;
13
14/**
15 * Represents an incorrect namespace typed in the console.
16 *
17 * @author Pierre du Plessis <pdples@gmail.com>
18 */
19class NamespaceNotFoundException extends CommandNotFoundException
20{
21}
diff --git a/vendor/symfony/console/Exception/RunCommandFailedException.php b/vendor/symfony/console/Exception/RunCommandFailedException.php
new file mode 100644
index 0000000..5d87ec9
--- /dev/null
+++ b/vendor/symfony/console/Exception/RunCommandFailedException.php
@@ -0,0 +1,29 @@
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\Exception;
13
14use Symfony\Component\Console\Messenger\RunCommandContext;
15
16/**
17 * @author Kevin Bond <kevinbond@gmail.com>
18 */
19final class RunCommandFailedException extends RuntimeException
20{
21 public function __construct(\Throwable|string $exception, public readonly RunCommandContext $context)
22 {
23 parent::__construct(
24 $exception instanceof \Throwable ? $exception->getMessage() : $exception,
25 $exception instanceof \Throwable ? $exception->getCode() : 0,
26 $exception instanceof \Throwable ? $exception : null,
27 );
28 }
29}
diff --git a/vendor/symfony/console/Exception/RuntimeException.php b/vendor/symfony/console/Exception/RuntimeException.php
new file mode 100644
index 0000000..51d7d80
--- /dev/null
+++ b/vendor/symfony/console/Exception/RuntimeException.php
@@ -0,0 +1,19 @@
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\Exception;
13
14/**
15 * @author Jérôme Tamarelle <jerome@tamarelle.net>
16 */
17class RuntimeException extends \RuntimeException implements ExceptionInterface
18{
19}
diff --git a/vendor/symfony/console/Formatter/NullOutputFormatter.php b/vendor/symfony/console/Formatter/NullOutputFormatter.php
new file mode 100644
index 0000000..5c11c76
--- /dev/null
+++ b/vendor/symfony/console/Formatter/NullOutputFormatter.php
@@ -0,0 +1,51 @@
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\Formatter;
13
14/**
15 * @author Tien Xuan Vo <tien.xuan.vo@gmail.com>
16 */
17final class NullOutputFormatter implements OutputFormatterInterface
18{
19 private NullOutputFormatterStyle $style;
20
21 public function format(?string $message): ?string
22 {
23 return null;
24 }
25
26 public function getStyle(string $name): OutputFormatterStyleInterface
27 {
28 // to comply with the interface we must return a OutputFormatterStyleInterface
29 return $this->style ??= new NullOutputFormatterStyle();
30 }
31
32 public function hasStyle(string $name): bool
33 {
34 return false;
35 }
36
37 public function isDecorated(): bool
38 {
39 return false;
40 }
41
42 public function setDecorated(bool $decorated): void
43 {
44 // do nothing
45 }
46
47 public function setStyle(string $name, OutputFormatterStyleInterface $style): void
48 {
49 // do nothing
50 }
51}
diff --git a/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php b/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php
new file mode 100644
index 0000000..06fa6e4
--- /dev/null
+++ b/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php
@@ -0,0 +1,48 @@
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\Formatter;
13
14/**
15 * @author Tien Xuan Vo <tien.xuan.vo@gmail.com>
16 */
17final class NullOutputFormatterStyle implements OutputFormatterStyleInterface
18{
19 public function apply(string $text): string
20 {
21 return $text;
22 }
23
24 public function setBackground(?string $color): void
25 {
26 // do nothing
27 }
28
29 public function setForeground(?string $color): void
30 {
31 // do nothing
32 }
33
34 public function setOption(string $option): void
35 {
36 // do nothing
37 }
38
39 public function setOptions(array $options): void
40 {
41 // do nothing
42 }
43
44 public function unsetOption(string $option): void
45 {
46 // do nothing
47 }
48}
diff --git a/vendor/symfony/console/Formatter/OutputFormatter.php b/vendor/symfony/console/Formatter/OutputFormatter.php
new file mode 100644
index 0000000..8e81e59
--- /dev/null
+++ b/vendor/symfony/console/Formatter/OutputFormatter.php
@@ -0,0 +1,268 @@
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\Formatter;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15
16use function Symfony\Component\String\b;
17
18/**
19 * Formatter class for console output.
20 *
21 * @author Konstantin Kudryashov <ever.zet@gmail.com>
22 * @author Roland Franssen <franssen.roland@gmail.com>
23 */
24class OutputFormatter implements WrappableOutputFormatterInterface
25{
26 private bool $decorated;
27 private array $styles = [];
28 private OutputFormatterStyleStack $styleStack;
29
30 public function __clone()
31 {
32 $this->styleStack = clone $this->styleStack;
33 foreach ($this->styles as $key => $value) {
34 $this->styles[$key] = clone $value;
35 }
36 }
37
38 /**
39 * Escapes "<" and ">" special chars in given text.
40 */
41 public static function escape(string $text): string
42 {
43 $text = preg_replace('/([^\\\\]|^)([<>])/', '$1\\\\$2', $text);
44
45 return self::escapeTrailingBackslash($text);
46 }
47
48 /**
49 * Escapes trailing "\" in given text.
50 *
51 * @internal
52 */
53 public static function escapeTrailingBackslash(string $text): string
54 {
55 if (str_ends_with($text, '\\')) {
56 $len = \strlen($text);
57 $text = rtrim($text, '\\');
58 $text = str_replace("\0", '', $text);
59 $text .= str_repeat("\0", $len - \strlen($text));
60 }
61
62 return $text;
63 }
64
65 /**
66 * Initializes console output formatter.
67 *
68 * @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances
69 */
70 public function __construct(bool $decorated = false, array $styles = [])
71 {
72 $this->decorated = $decorated;
73
74 $this->setStyle('error', new OutputFormatterStyle('white', 'red'));
75 $this->setStyle('info', new OutputFormatterStyle('green'));
76 $this->setStyle('comment', new OutputFormatterStyle('yellow'));
77 $this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
78
79 foreach ($styles as $name => $style) {
80 $this->setStyle($name, $style);
81 }
82
83 $this->styleStack = new OutputFormatterStyleStack();
84 }
85
86 public function setDecorated(bool $decorated): void
87 {
88 $this->decorated = $decorated;
89 }
90
91 public function isDecorated(): bool
92 {
93 return $this->decorated;
94 }
95
96 public function setStyle(string $name, OutputFormatterStyleInterface $style): void
97 {
98 $this->styles[strtolower($name)] = $style;
99 }
100
101 public function hasStyle(string $name): bool
102 {
103 return isset($this->styles[strtolower($name)]);
104 }
105
106 public function getStyle(string $name): OutputFormatterStyleInterface
107 {
108 if (!$this->hasStyle($name)) {
109 throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name));
110 }
111
112 return $this->styles[strtolower($name)];
113 }
114
115 public function format(?string $message): ?string
116 {
117 return $this->formatAndWrap($message, 0);
118 }
119
120 public function formatAndWrap(?string $message, int $width): string
121 {
122 if (null === $message) {
123 return '';
124 }
125
126 $offset = 0;
127 $output = '';
128 $openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*';
129 $closeTagRegex = '[a-z][^<>]*+';
130 $currentLineLength = 0;
131 preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE);
132 foreach ($matches[0] as $i => $match) {
133 $pos = $match[1];
134 $text = $match[0];
135
136 if (0 != $pos && '\\' == $message[$pos - 1]) {
137 continue;
138 }
139
140 // add the text up to the next tag
141 $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
142 $offset = $pos + \strlen($text);
143
144 // opening tag?
145 if ($open = '/' !== $text[1]) {
146 $tag = $matches[1][$i][0];
147 } else {
148 $tag = $matches[3][$i][0] ?? '';
149 }
150
151 if (!$open && !$tag) {
152 // </>
153 $this->styleStack->pop();
154 } elseif (null === $style = $this->createStyleFromString($tag)) {
155 $output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength);
156 } elseif ($open) {
157 $this->styleStack->push($style);
158 } else {
159 $this->styleStack->pop($style);
160 }
161 }
162
163 $output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);
164
165 return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']);
166 }
167
168 public function getStyleStack(): OutputFormatterStyleStack
169 {
170 return $this->styleStack;
171 }
172
173 /**
174 * Tries to create new style instance from string.
175 */
176 private function createStyleFromString(string $string): ?OutputFormatterStyleInterface
177 {
178 if (isset($this->styles[$string])) {
179 return $this->styles[$string];
180 }
181
182 if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, \PREG_SET_ORDER)) {
183 return null;
184 }
185
186 $style = new OutputFormatterStyle();
187 foreach ($matches as $match) {
188 array_shift($match);
189 $match[0] = strtolower($match[0]);
190
191 if ('fg' == $match[0]) {
192 $style->setForeground(strtolower($match[1]));
193 } elseif ('bg' == $match[0]) {
194 $style->setBackground(strtolower($match[1]));
195 } elseif ('href' === $match[0]) {
196 $url = preg_replace('{\\\\([<>])}', '$1', $match[1]);
197 $style->setHref($url);
198 } elseif ('options' === $match[0]) {
199 preg_match_all('([^,;]+)', strtolower($match[1]), $options);
200 $options = array_shift($options);
201 foreach ($options as $option) {
202 $style->setOption($option);
203 }
204 } else {
205 return null;
206 }
207 }
208
209 return $style;
210 }
211
212 /**
213 * Applies current style from stack to text, if must be applied.
214 */
215 private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string
216 {
217 if ('' === $text) {
218 return '';
219 }
220
221 if (!$width) {
222 return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text;
223 }
224
225 if (!$currentLineLength && '' !== $current) {
226 $text = ltrim($text);
227 }
228
229 if ($currentLineLength) {
230 $prefix = substr($text, 0, $i = $width - $currentLineLength)."\n";
231 $text = substr($text, $i);
232 } else {
233 $prefix = '';
234 }
235
236 preg_match('~(\\n)$~', $text, $matches);
237 $text = $prefix.$this->addLineBreaks($text, $width);
238 $text = rtrim($text, "\n").($matches[1] ?? '');
239
240 if (!$currentLineLength && '' !== $current && !str_ends_with($current, "\n")) {
241 $text = "\n".$text;
242 }
243
244 $lines = explode("\n", $text);
245
246 foreach ($lines as $line) {
247 $currentLineLength += \strlen($line);
248 if ($width <= $currentLineLength) {
249 $currentLineLength = 0;
250 }
251 }
252
253 if ($this->isDecorated()) {
254 foreach ($lines as $i => $line) {
255 $lines[$i] = $this->styleStack->getCurrent()->apply($line);
256 }
257 }
258
259 return implode("\n", $lines);
260 }
261
262 private function addLineBreaks(string $text, int $width): string
263 {
264 $encoding = mb_detect_encoding($text, null, true) ?: 'UTF-8';
265
266 return b($text)->toCodePointString($encoding)->wordwrap($width, "\n", true)->toByteString($encoding);
267 }
268}
diff --git a/vendor/symfony/console/Formatter/OutputFormatterInterface.php b/vendor/symfony/console/Formatter/OutputFormatterInterface.php
new file mode 100644
index 0000000..947347f
--- /dev/null
+++ b/vendor/symfony/console/Formatter/OutputFormatterInterface.php
@@ -0,0 +1,52 @@
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\Formatter;
13
14/**
15 * Formatter interface for console output.
16 *
17 * @author Konstantin Kudryashov <ever.zet@gmail.com>
18 */
19interface OutputFormatterInterface
20{
21 /**
22 * Sets the decorated flag.
23 */
24 public function setDecorated(bool $decorated): void;
25
26 /**
27 * Whether the output will decorate messages.
28 */
29 public function isDecorated(): bool;
30
31 /**
32 * Sets a new style.
33 */
34 public function setStyle(string $name, OutputFormatterStyleInterface $style): void;
35
36 /**
37 * Checks if output formatter has style with specified name.
38 */
39 public function hasStyle(string $name): bool;
40
41 /**
42 * Gets style options from style with specified name.
43 *
44 * @throws \InvalidArgumentException When style isn't defined
45 */
46 public function getStyle(string $name): OutputFormatterStyleInterface;
47
48 /**
49 * Formats a message according to the given styles.
50 */
51 public function format(?string $message): ?string;
52}
diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyle.php b/vendor/symfony/console/Formatter/OutputFormatterStyle.php
new file mode 100644
index 0000000..20a65b5
--- /dev/null
+++ b/vendor/symfony/console/Formatter/OutputFormatterStyle.php
@@ -0,0 +1,89 @@
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\Formatter;
13
14use Symfony\Component\Console\Color;
15
16/**
17 * Formatter style class for defining styles.
18 *
19 * @author Konstantin Kudryashov <ever.zet@gmail.com>
20 */
21class OutputFormatterStyle implements OutputFormatterStyleInterface
22{
23 private Color $color;
24 private string $foreground;
25 private string $background;
26 private array $options;
27 private ?string $href = null;
28 private bool $handlesHrefGracefully;
29
30 /**
31 * Initializes output formatter style.
32 *
33 * @param string|null $foreground The style foreground color name
34 * @param string|null $background The style background color name
35 */
36 public function __construct(?string $foreground = null, ?string $background = null, array $options = [])
37 {
38 $this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options);
39 }
40
41 public function setForeground(?string $color): void
42 {
43 $this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options);
44 }
45
46 public function setBackground(?string $color): void
47 {
48 $this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options);
49 }
50
51 public function setHref(string $url): void
52 {
53 $this->href = $url;
54 }
55
56 public function setOption(string $option): void
57 {
58 $this->options[] = $option;
59 $this->color = new Color($this->foreground, $this->background, $this->options);
60 }
61
62 public function unsetOption(string $option): void
63 {
64 $pos = array_search($option, $this->options);
65 if (false !== $pos) {
66 unset($this->options[$pos]);
67 }
68
69 $this->color = new Color($this->foreground, $this->background, $this->options);
70 }
71
72 public function setOptions(array $options): void
73 {
74 $this->color = new Color($this->foreground, $this->background, $this->options = $options);
75 }
76
77 public function apply(string $text): string
78 {
79 $this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR')
80 && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100)
81 && !isset($_SERVER['IDEA_INITIAL_DIRECTORY']);
82
83 if (null !== $this->href && $this->handlesHrefGracefully) {
84 $text = "\033]8;;$this->href\033\\$text\033]8;;\033\\";
85 }
86
87 return $this->color->apply($text);
88 }
89}
diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php
new file mode 100644
index 0000000..0374192
--- /dev/null
+++ b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php
@@ -0,0 +1,50 @@
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\Formatter;
13
14/**
15 * Formatter style interface for defining styles.
16 *
17 * @author Konstantin Kudryashov <ever.zet@gmail.com>
18 */
19interface OutputFormatterStyleInterface
20{
21 /**
22 * Sets style foreground color.
23 */
24 public function setForeground(?string $color): void;
25
26 /**
27 * Sets style background color.
28 */
29 public function setBackground(?string $color): void;
30
31 /**
32 * Sets some specific style option.
33 */
34 public function setOption(string $option): void;
35
36 /**
37 * Unsets some specific style option.
38 */
39 public function unsetOption(string $option): void;
40
41 /**
42 * Sets multiple style options at once.
43 */
44 public function setOptions(array $options): void;
45
46 /**
47 * Applies the style to a given text.
48 */
49 public function apply(string $text): string;
50}
diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php
new file mode 100644
index 0000000..4985213
--- /dev/null
+++ b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php
@@ -0,0 +1,103 @@
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\Formatter;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Contracts\Service\ResetInterface;
16
17/**
18 * @author Jean-François Simon <contact@jfsimon.fr>
19 */
20class OutputFormatterStyleStack implements ResetInterface
21{
22 /**
23 * @var OutputFormatterStyleInterface[]
24 */
25 private array $styles = [];
26
27 private OutputFormatterStyleInterface $emptyStyle;
28
29 public function __construct(?OutputFormatterStyleInterface $emptyStyle = null)
30 {
31 $this->emptyStyle = $emptyStyle ?? new OutputFormatterStyle();
32 $this->reset();
33 }
34
35 /**
36 * Resets stack (ie. empty internal arrays).
37 */
38 public function reset(): void
39 {
40 $this->styles = [];
41 }
42
43 /**
44 * Pushes a style in the stack.
45 */
46 public function push(OutputFormatterStyleInterface $style): void
47 {
48 $this->styles[] = $style;
49 }
50
51 /**
52 * Pops a style from the stack.
53 *
54 * @throws InvalidArgumentException When style tags incorrectly nested
55 */
56 public function pop(?OutputFormatterStyleInterface $style = null): OutputFormatterStyleInterface
57 {
58 if (!$this->styles) {
59 return $this->emptyStyle;
60 }
61
62 if (null === $style) {
63 return array_pop($this->styles);
64 }
65
66 foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
67 if ($style->apply('') === $stackedStyle->apply('')) {
68 $this->styles = \array_slice($this->styles, 0, $index);
69
70 return $stackedStyle;
71 }
72 }
73
74 throw new InvalidArgumentException('Incorrectly nested style tag found.');
75 }
76
77 /**
78 * Computes current style with stacks top codes.
79 */
80 public function getCurrent(): OutputFormatterStyleInterface
81 {
82 if (!$this->styles) {
83 return $this->emptyStyle;
84 }
85
86 return $this->styles[\count($this->styles) - 1];
87 }
88
89 /**
90 * @return $this
91 */
92 public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle): static
93 {
94 $this->emptyStyle = $emptyStyle;
95
96 return $this;
97 }
98
99 public function getEmptyStyle(): OutputFormatterStyleInterface
100 {
101 return $this->emptyStyle;
102 }
103}
diff --git a/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php b/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php
new file mode 100644
index 0000000..412d997
--- /dev/null
+++ b/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php
@@ -0,0 +1,25 @@
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\Formatter;
13
14/**
15 * Formatter interface for console output that supports word wrapping.
16 *
17 * @author Roland Franssen <franssen.roland@gmail.com>
18 */
19interface WrappableOutputFormatterInterface extends OutputFormatterInterface
20{
21 /**
22 * Formats a message according to the given styles, wrapping at `$width` (0 means no wrapping).
23 */
24 public function formatAndWrap(?string $message, int $width): string;
25}
diff --git a/vendor/symfony/console/Helper/DebugFormatterHelper.php b/vendor/symfony/console/Helper/DebugFormatterHelper.php
new file mode 100644
index 0000000..9ea7fb9
--- /dev/null
+++ b/vendor/symfony/console/Helper/DebugFormatterHelper.php
@@ -0,0 +1,98 @@
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
14/**
15 * Helps outputting debug information when running an external program from a command.
16 *
17 * An external program can be a Process, an HTTP request, or anything else.
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 */
21class DebugFormatterHelper extends Helper
22{
23 private const COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'];
24 private array $started = [];
25 private int $count = -1;
26
27 /**
28 * Starts a debug formatting session.
29 */
30 public function start(string $id, string $message, string $prefix = 'RUN'): string
31 {
32 $this->started[$id] = ['border' => ++$this->count % \count(self::COLORS)];
33
34 return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message);
35 }
36
37 /**
38 * Adds progress to a formatting session.
39 */
40 public function progress(string $id, string $buffer, bool $error = false, string $prefix = 'OUT', string $errorPrefix = 'ERR'): string
41 {
42 $message = '';
43
44 if ($error) {
45 if (isset($this->started[$id]['out'])) {
46 $message .= "\n";
47 unset($this->started[$id]['out']);
48 }
49 if (!isset($this->started[$id]['err'])) {
50 $message .= sprintf('%s<bg=red;fg=white> %s </> ', $this->getBorder($id), $errorPrefix);
51 $this->started[$id]['err'] = true;
52 }
53
54 $message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix), $buffer);
55 } else {
56 if (isset($this->started[$id]['err'])) {
57 $message .= "\n";
58 unset($this->started[$id]['err']);
59 }
60 if (!isset($this->started[$id]['out'])) {
61 $message .= sprintf('%s<bg=green;fg=white> %s </> ', $this->getBorder($id), $prefix);
62 $this->started[$id]['out'] = true;
63 }
64
65 $message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix), $buffer);
66 }
67
68 return $message;
69 }
70
71 /**
72 * Stops a formatting session.
73 */
74 public function stop(string $id, string $message, bool $successful, string $prefix = 'RES'): string
75 {
76 $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : '';
77
78 if ($successful) {
79 return sprintf("%s%s<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
80 }
81
82 $message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
83
84 unset($this->started[$id]['out'], $this->started[$id]['err']);
85
86 return $message;
87 }
88
89 private function getBorder(string $id): string
90 {
91 return sprintf('<bg=%s> </>', self::COLORS[$this->started[$id]['border']]);
92 }
93
94 public function getName(): string
95 {
96 return 'debug_formatter';
97 }
98}
diff --git a/vendor/symfony/console/Helper/DescriptorHelper.php b/vendor/symfony/console/Helper/DescriptorHelper.php
new file mode 100644
index 0000000..300c7b1
--- /dev/null
+++ b/vendor/symfony/console/Helper/DescriptorHelper.php
@@ -0,0 +1,91 @@
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\Descriptor\DescriptorInterface;
15use Symfony\Component\Console\Descriptor\JsonDescriptor;
16use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
17use Symfony\Component\Console\Descriptor\ReStructuredTextDescriptor;
18use Symfony\Component\Console\Descriptor\TextDescriptor;
19use Symfony\Component\Console\Descriptor\XmlDescriptor;
20use Symfony\Component\Console\Exception\InvalidArgumentException;
21use Symfony\Component\Console\Output\OutputInterface;
22
23/**
24 * This class adds helper method to describe objects in various formats.
25 *
26 * @author Jean-François Simon <contact@jfsimon.fr>
27 */
28class DescriptorHelper extends Helper
29{
30 /**
31 * @var DescriptorInterface[]
32 */
33 private array $descriptors = [];
34
35 public function __construct()
36 {
37 $this
38 ->register('txt', new TextDescriptor())
39 ->register('xml', new XmlDescriptor())
40 ->register('json', new JsonDescriptor())
41 ->register('md', new MarkdownDescriptor())
42 ->register('rst', new ReStructuredTextDescriptor())
43 ;
44 }
45
46 /**
47 * Describes an object if supported.
48 *
49 * Available options are:
50 * * format: string, the output format name
51 * * raw_text: boolean, sets output type as raw
52 *
53 * @throws InvalidArgumentException when the given format is not supported
54 */
55 public function describe(OutputInterface $output, ?object $object, array $options = []): void
56 {
57 $options = array_merge([
58 'raw_text' => false,
59 'format' => 'txt',
60 ], $options);
61
62 if (!isset($this->descriptors[$options['format']])) {
63 throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
64 }
65
66 $descriptor = $this->descriptors[$options['format']];
67 $descriptor->describe($output, $object, $options);
68 }
69
70 /**
71 * Registers a descriptor.
72 *
73 * @return $this
74 */
75 public function register(string $format, DescriptorInterface $descriptor): static
76 {
77 $this->descriptors[$format] = $descriptor;
78
79 return $this;
80 }
81
82 public function getName(): string
83 {
84 return 'descriptor';
85 }
86
87 public function getFormats(): array
88 {
89 return array_keys($this->descriptors);
90 }
91}
diff --git a/vendor/symfony/console/Helper/Dumper.php b/vendor/symfony/console/Helper/Dumper.php
new file mode 100644
index 0000000..0cd01e6
--- /dev/null
+++ b/vendor/symfony/console/Helper/Dumper.php
@@ -0,0 +1,53 @@
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\Output\OutputInterface;
15use Symfony\Component\VarDumper\Cloner\ClonerInterface;
16use Symfony\Component\VarDumper\Cloner\VarCloner;
17use Symfony\Component\VarDumper\Dumper\CliDumper;
18
19/**
20 * @author Roland Franssen <franssen.roland@gmail.com>
21 */
22final class Dumper
23{
24 private \Closure $handler;
25
26 public function __construct(
27 private OutputInterface $output,
28 private ?CliDumper $dumper = null,
29 private ?ClonerInterface $cloner = null,
30 ) {
31 if (class_exists(CliDumper::class)) {
32 $this->handler = function ($var): string {
33 $dumper = $this->dumper ??= new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
34 $dumper->setColors($this->output->isDecorated());
35
36 return rtrim($dumper->dump(($this->cloner ??= new VarCloner())->cloneVar($var)->withRefHandles(false), true));
37 };
38 } else {
39 $this->handler = fn ($var): string => match (true) {
40 null === $var => 'null',
41 true === $var => 'true',
42 false === $var => 'false',
43 \is_string($var) => '"'.$var.'"',
44 default => rtrim(print_r($var, true)),
45 };
46 }
47 }
48
49 public function __invoke(mixed $var): string
50 {
51 return ($this->handler)($var);
52 }
53}
diff --git a/vendor/symfony/console/Helper/FormatterHelper.php b/vendor/symfony/console/Helper/FormatterHelper.php
new file mode 100644
index 0000000..279e4c7
--- /dev/null
+++ b/vendor/symfony/console/Helper/FormatterHelper.php
@@ -0,0 +1,81 @@
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\Formatter\OutputFormatter;
15
16/**
17 * The Formatter class provides helpers to format messages.
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 */
21class FormatterHelper extends Helper
22{
23 /**
24 * Formats a message within a section.
25 */
26 public function formatSection(string $section, string $message, string $style = 'info'): string
27 {
28 return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message);
29 }
30
31 /**
32 * Formats a message as a block of text.
33 */
34 public function formatBlock(string|array $messages, string $style, bool $large = false): string
35 {
36 if (!\is_array($messages)) {
37 $messages = [$messages];
38 }
39
40 $len = 0;
41 $lines = [];
42 foreach ($messages as $message) {
43 $message = OutputFormatter::escape($message);
44 $lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
45 $len = max(self::width($message) + ($large ? 4 : 2), $len);
46 }
47
48 $messages = $large ? [str_repeat(' ', $len)] : [];
49 for ($i = 0; isset($lines[$i]); ++$i) {
50 $messages[] = $lines[$i].str_repeat(' ', $len - self::width($lines[$i]));
51 }
52 if ($large) {
53 $messages[] = str_repeat(' ', $len);
54 }
55
56 for ($i = 0; isset($messages[$i]); ++$i) {
57 $messages[$i] = sprintf('<%s>%s</%s>', $style, $messages[$i], $style);
58 }
59
60 return implode("\n", $messages);
61 }
62
63 /**
64 * Truncates a message to the given length.
65 */
66 public function truncate(string $message, int $length, string $suffix = '...'): string
67 {
68 $computedLength = $length - self::width($suffix);
69
70 if ($computedLength > self::width($message)) {
71 return $message;
72 }
73
74 return self::substr($message, 0, $length).$suffix;
75 }
76
77 public function getName(): string
78 {
79 return 'formatter';
80 }
81}
diff --git a/vendor/symfony/console/Helper/Helper.php b/vendor/symfony/console/Helper/Helper.php
new file mode 100644
index 0000000..de09006
--- /dev/null
+++ b/vendor/symfony/console/Helper/Helper.php
@@ -0,0 +1,159 @@
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\Formatter\OutputFormatterInterface;
15use Symfony\Component\String\UnicodeString;
16
17/**
18 * Helper is the base class for all helper classes.
19 *
20 * @author Fabien Potencier <fabien@symfony.com>
21 */
22abstract class Helper implements HelperInterface
23{
24 protected ?HelperSet $helperSet = null;
25
26 public function setHelperSet(?HelperSet $helperSet): void
27 {
28 $this->helperSet = $helperSet;
29 }
30
31 public function getHelperSet(): ?HelperSet
32 {
33 return $this->helperSet;
34 }
35
36 /**
37 * Returns the width of a string, using mb_strwidth if it is available.
38 * The width is how many characters positions the string will use.
39 */
40 public static function width(?string $string): int
41 {
42 $string ??= '';
43
44 if (preg_match('//u', $string)) {
45 return (new UnicodeString($string))->width(false);
46 }
47
48 if (false === $encoding = mb_detect_encoding($string, null, true)) {
49 return \strlen($string);
50 }
51
52 return mb_strwidth($string, $encoding);
53 }
54
55 /**
56 * Returns the length of a string, using mb_strlen if it is available.
57 * The length is related to how many bytes the string will use.
58 */
59 public static function length(?string $string): int
60 {
61 $string ??= '';
62
63 if (preg_match('//u', $string)) {
64 return (new UnicodeString($string))->length();
65 }
66
67 if (false === $encoding = mb_detect_encoding($string, null, true)) {
68 return \strlen($string);
69 }
70
71 return mb_strlen($string, $encoding);
72 }
73
74 /**
75 * Returns the subset of a string, using mb_substr if it is available.
76 */
77 public static function substr(?string $string, int $from, ?int $length = null): string
78 {
79 $string ??= '';
80
81 if (false === $encoding = mb_detect_encoding($string, null, true)) {
82 return substr($string, $from, $length);
83 }
84
85 return mb_substr($string, $from, $length, $encoding);
86 }
87
88 public static function formatTime(int|float $secs, int $precision = 1): string
89 {
90 $secs = (int) floor($secs);
91
92 if (0 === $secs) {
93 return '< 1 sec';
94 }
95
96 static $timeFormats = [
97 [1, '1 sec', 'secs'],
98 [60, '1 min', 'mins'],
99 [3600, '1 hr', 'hrs'],
100 [86400, '1 day', 'days'],
101 ];
102
103 $times = [];
104 foreach ($timeFormats as $index => $format) {
105 $seconds = isset($timeFormats[$index + 1]) ? $secs % $timeFormats[$index + 1][0] : $secs;
106
107 if (isset($times[$index - $precision])) {
108 unset($times[$index - $precision]);
109 }
110
111 if (0 === $seconds) {
112 continue;
113 }
114
115 $unitCount = ($seconds / $format[0]);
116 $times[$index] = 1 === $unitCount ? $format[1] : $unitCount.' '.$format[2];
117
118 if ($secs === $seconds) {
119 break;
120 }
121
122 $secs -= $seconds;
123 }
124
125 return implode(', ', array_reverse($times));
126 }
127
128 public static function formatMemory(int $memory): string
129 {
130 if ($memory >= 1024 * 1024 * 1024) {
131 return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
132 }
133
134 if ($memory >= 1024 * 1024) {
135 return sprintf('%.1f MiB', $memory / 1024 / 1024);
136 }
137
138 if ($memory >= 1024) {
139 return sprintf('%d KiB', $memory / 1024);
140 }
141
142 return sprintf('%d B', $memory);
143 }
144
145 public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string): string
146 {
147 $isDecorated = $formatter->isDecorated();
148 $formatter->setDecorated(false);
149 // remove <...> formatting
150 $string = $formatter->format($string ?? '');
151 // remove already formatted characters
152 $string = preg_replace("/\033\[[^m]*m/", '', $string ?? '');
153 // remove terminal hyperlinks
154 $string = preg_replace('/\\033]8;[^;]*;[^\\033]*\\033\\\\/', '', $string ?? '');
155 $formatter->setDecorated($isDecorated);
156
157 return $string;
158 }
159}
diff --git a/vendor/symfony/console/Helper/HelperInterface.php b/vendor/symfony/console/Helper/HelperInterface.php
new file mode 100644
index 0000000..8c4da3c
--- /dev/null
+++ b/vendor/symfony/console/Helper/HelperInterface.php
@@ -0,0 +1,35 @@
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
14/**
15 * HelperInterface is the interface all helpers must implement.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 */
19interface HelperInterface
20{
21 /**
22 * Sets the helper set associated with this helper.
23 */
24 public function setHelperSet(?HelperSet $helperSet): void;
25
26 /**
27 * Gets the helper set associated with this helper.
28 */
29 public function getHelperSet(): ?HelperSet;
30
31 /**
32 * Returns the canonical name of this helper.
33 */
34 public function getName(): string;
35}
diff --git a/vendor/symfony/console/Helper/HelperSet.php b/vendor/symfony/console/Helper/HelperSet.php
new file mode 100644
index 0000000..30df9f9
--- /dev/null
+++ b/vendor/symfony/console/Helper/HelperSet.php
@@ -0,0 +1,74 @@
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;
15
16/**
17 * HelperSet represents a set of helpers to be used with a command.
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 *
21 * @implements \IteratorAggregate<string, HelperInterface>
22 */
23class HelperSet implements \IteratorAggregate
24{
25 /** @var array<string, HelperInterface> */
26 private array $helpers = [];
27
28 /**
29 * @param HelperInterface[] $helpers
30 */
31 public function __construct(array $helpers = [])
32 {
33 foreach ($helpers as $alias => $helper) {
34 $this->set($helper, \is_int($alias) ? null : $alias);
35 }
36 }
37
38 public function set(HelperInterface $helper, ?string $alias = null): void
39 {
40 $this->helpers[$helper->getName()] = $helper;
41 if (null !== $alias) {
42 $this->helpers[$alias] = $helper;
43 }
44
45 $helper->setHelperSet($this);
46 }
47
48 /**
49 * Returns true if the helper if defined.
50 */
51 public function has(string $name): bool
52 {
53 return isset($this->helpers[$name]);
54 }
55
56 /**
57 * Gets a helper value.
58 *
59 * @throws InvalidArgumentException if the helper is not defined
60 */
61 public function get(string $name): HelperInterface
62 {
63 if (!$this->has($name)) {
64 throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
65 }
66
67 return $this->helpers[$name];
68 }
69
70 public function getIterator(): \Traversable
71 {
72 return new \ArrayIterator($this->helpers);
73 }
74}
diff --git a/vendor/symfony/console/Helper/InputAwareHelper.php b/vendor/symfony/console/Helper/InputAwareHelper.php
new file mode 100644
index 0000000..47126bd
--- /dev/null
+++ b/vendor/symfony/console/Helper/InputAwareHelper.php
@@ -0,0 +1,30 @@
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\Input\InputAwareInterface;
15use Symfony\Component\Console\Input\InputInterface;
16
17/**
18 * An implementation of InputAwareInterface for Helpers.
19 *
20 * @author Wouter J <waldio.webdesign@gmail.com>
21 */
22abstract class InputAwareHelper extends Helper implements InputAwareInterface
23{
24 protected InputInterface $input;
25
26 public function setInput(InputInterface $input): void
27 {
28 $this->input = $input;
29 }
30}
diff --git a/vendor/symfony/console/Helper/OutputWrapper.php b/vendor/symfony/console/Helper/OutputWrapper.php
new file mode 100644
index 0000000..0ea2b70
--- /dev/null
+++ b/vendor/symfony/console/Helper/OutputWrapper.php
@@ -0,0 +1,76 @@
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
14/**
15 * Simple output wrapper for "tagged outputs" instead of wordwrap(). This solution is based on a StackOverflow
16 * answer: https://stackoverflow.com/a/20434776/1476819 from user557597 (alias SLN).
17 *
18 * (?:
19 * # -- Words/Characters
20 * ( # (1 start)
21 * (?> # Atomic Group - Match words with valid breaks
22 * .{1,16} # 1-N characters
23 * # Followed by one of 4 prioritized, non-linebreak whitespace
24 * (?: # break types:
25 * (?<= [^\S\r\n] ) # 1. - Behind a non-linebreak whitespace
26 * [^\S\r\n]? # ( optionally accept an extra non-linebreak whitespace )
27 * | (?= \r? \n ) # 2. - Ahead a linebreak
28 * | $ # 3. - EOS
29 * | [^\S\r\n] # 4. - Accept an extra non-linebreak whitespace
30 * )
31 * ) # End atomic group
32 * |
33 * .{1,16} # No valid word breaks, just break on the N'th character
34 * ) # (1 end)
35 * (?: \r? \n )? # Optional linebreak after Words/Characters
36 * |
37 * # -- Or, Linebreak
38 * (?: \r? \n | $ ) # Stand alone linebreak or at EOS
39 * )
40 *
41 * @author Krisztián Ferenczi <ferenczi.krisztian@gmail.com>
42 *
43 * @see https://stackoverflow.com/a/20434776/1476819
44 */
45final class OutputWrapper
46{
47 private const TAG_OPEN_REGEX_SEGMENT = '[a-z](?:[^\\\\<>]*+ | \\\\.)*';
48 private const TAG_CLOSE_REGEX_SEGMENT = '[a-z][^<>]*+';
49 private const URL_PATTERN = 'https?://\S+';
50
51 public function __construct(
52 private bool $allowCutUrls = false,
53 ) {
54 }
55
56 public function wrap(string $text, int $width, string $break = "\n"): string
57 {
58 if (!$width) {
59 return $text;
60 }
61
62 $tagPattern = sprintf('<(?:(?:%s)|/(?:%s)?)>', self::TAG_OPEN_REGEX_SEGMENT, self::TAG_CLOSE_REGEX_SEGMENT);
63 $limitPattern = "{1,$width}";
64 $patternBlocks = [$tagPattern];
65 if (!$this->allowCutUrls) {
66 $patternBlocks[] = self::URL_PATTERN;
67 }
68 $patternBlocks[] = '.';
69 $blocks = implode('|', $patternBlocks);
70 $rowPattern = "(?:$blocks)$limitPattern";
71 $pattern = sprintf('#(?:((?>(%1$s)((?<=[^\S\r\n])[^\S\r\n]?|(?=\r?\n)|$|[^\S\r\n]))|(%1$s))(?:\r?\n)?|(?:\r?\n|$))#imux', $rowPattern);
72 $output = rtrim(preg_replace($pattern, '\\1'.$break, $text), $break);
73
74 return str_replace(' '.$break, $break, $output);
75 }
76}
diff --git a/vendor/symfony/console/Helper/ProcessHelper.php b/vendor/symfony/console/Helper/ProcessHelper.php
new file mode 100644
index 0000000..3ef6f71
--- /dev/null
+++ b/vendor/symfony/console/Helper/ProcessHelper.php
@@ -0,0 +1,137 @@
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\Output\ConsoleOutputInterface;
15use Symfony\Component\Console\Output\OutputInterface;
16use Symfony\Component\Process\Exception\ProcessFailedException;
17use Symfony\Component\Process\Process;
18
19/**
20 * The ProcessHelper class provides helpers to run external processes.
21 *
22 * @author Fabien Potencier <fabien@symfony.com>
23 *
24 * @final
25 */
26class ProcessHelper extends Helper
27{
28 /**
29 * Runs an external process.
30 *
31 * @param array|Process $cmd An instance of Process or an array of the command and arguments
32 * @param callable|null $callback A PHP callback to run whenever there is some
33 * output available on STDOUT or STDERR
34 */
35 public function run(OutputInterface $output, array|Process $cmd, ?string $error = null, ?callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process
36 {
37 if (!class_exists(Process::class)) {
38 throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".');
39 }
40
41 if ($output instanceof ConsoleOutputInterface) {
42 $output = $output->getErrorOutput();
43 }
44
45 $formatter = $this->getHelperSet()->get('debug_formatter');
46
47 if ($cmd instanceof Process) {
48 $cmd = [$cmd];
49 }
50
51 if (\is_string($cmd[0] ?? null)) {
52 $process = new Process($cmd);
53 $cmd = [];
54 } elseif (($cmd[0] ?? null) instanceof Process) {
55 $process = $cmd[0];
56 unset($cmd[0]);
57 } else {
58 throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__));
59 }
60
61 if ($verbosity <= $output->getVerbosity()) {
62 $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
63 }
64
65 if ($output->isDebug()) {
66 $callback = $this->wrapCallback($output, $process, $callback);
67 }
68
69 $process->run($callback, $cmd);
70
71 if ($verbosity <= $output->getVerbosity()) {
72 $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
73 $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
74 }
75
76 if (!$process->isSuccessful() && null !== $error) {
77 $output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
78 }
79
80 return $process;
81 }
82
83 /**
84 * Runs the process.
85 *
86 * This is identical to run() except that an exception is thrown if the process
87 * exits with a non-zero exit code.
88 *
89 * @param array|Process $cmd An instance of Process or a command to run
90 * @param callable|null $callback A PHP callback to run whenever there is some
91 * output available on STDOUT or STDERR
92 *
93 * @throws ProcessFailedException
94 *
95 * @see run()
96 */
97 public function mustRun(OutputInterface $output, array|Process $cmd, ?string $error = null, ?callable $callback = null): Process
98 {
99 $process = $this->run($output, $cmd, $error, $callback);
100
101 if (!$process->isSuccessful()) {
102 throw new ProcessFailedException($process);
103 }
104
105 return $process;
106 }
107
108 /**
109 * Wraps a Process callback to add debugging output.
110 */
111 public function wrapCallback(OutputInterface $output, Process $process, ?callable $callback = null): callable
112 {
113 if ($output instanceof ConsoleOutputInterface) {
114 $output = $output->getErrorOutput();
115 }
116
117 $formatter = $this->getHelperSet()->get('debug_formatter');
118
119 return function ($type, $buffer) use ($output, $process, $callback, $formatter) {
120 $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));
121
122 if (null !== $callback) {
123 $callback($type, $buffer);
124 }
125 };
126 }
127
128 private function escapeString(string $str): string
129 {
130 return str_replace('<', '\\<', $str);
131 }
132
133 public function getName(): string
134 {
135 return 'process';
136 }
137}
diff --git a/vendor/symfony/console/Helper/ProgressBar.php b/vendor/symfony/console/Helper/ProgressBar.php
new file mode 100644
index 0000000..7c22b7d
--- /dev/null
+++ b/vendor/symfony/console/Helper/ProgressBar.php
@@ -0,0 +1,645 @@
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\Cursor;
15use Symfony\Component\Console\Exception\LogicException;
16use Symfony\Component\Console\Output\ConsoleOutputInterface;
17use Symfony\Component\Console\Output\ConsoleSectionOutput;
18use Symfony\Component\Console\Output\OutputInterface;
19use 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 */
27final class ProgressBar
28{
29 public const FORMAT_VERBOSE = 'verbose';
30 public const FORMAT_VERY_VERBOSE = 'very_verbose';
31 public const FORMAT_DEBUG = 'debug';
32 public const FORMAT_NORMAL = 'normal';
33
34 private const FORMAT_VERBOSE_NOMAX = 'verbose_nomax';
35 private const FORMAT_VERY_VERBOSE_NOMAX = 'very_verbose_nomax';
36 private const FORMAT_DEBUG_NOMAX = 'debug_nomax';
37 private const FORMAT_NORMAL_NOMAX = 'normal_nomax';
38
39 private int $barWidth = 28;
40 private string $barChar;
41 private string $emptyBarChar = '-';
42 private string $progressChar = '>';
43 private ?string $format = null;
44 private ?string $internalFormat = null;
45 private ?int $redrawFreq = 1;
46 private int $writeCount = 0;
47 private float $lastWriteTime = 0;
48 private float $minSecondsBetweenRedraws = 0;
49 private float $maxSecondsBetweenRedraws = 1;
50 private OutputInterface $output;
51 private int $step = 0;
52 private int $startingStep = 0;
53 private ?int $max = null;
54 private int $startTime;
55 private int $stepWidth;
56 private float $percent = 0.0;
57 private array $messages = [];
58 private bool $overwrite = true;
59 private Terminal $terminal;
60 private ?string $previousMessage = null;
61 private Cursor $cursor;
62 private array $placeholders = [];
63
64 private static array $formatters;
65 private static array $formats;
66
67 /**
68 * @param int $max Maximum steps (0 if unknown)
69 */
70 public function __construct(OutputInterface $output, int $max = 0, float $minSecondsBetweenRedraws = 1 / 25)
71 {
72 if ($output instanceof ConsoleOutputInterface) {
73 $output = $output->getErrorOutput();
74 }
75
76 $this->output = $output;
77 $this->setMaxSteps($max);
78 $this->terminal = new Terminal();
79
80 if (0 < $minSecondsBetweenRedraws) {
81 $this->redrawFreq = null;
82 $this->minSecondsBetweenRedraws = $minSecondsBetweenRedraws;
83 }
84
85 if (!$this->output->isDecorated()) {
86 // disable overwrite when output does not support ANSI codes.
87 $this->overwrite = false;
88
89 // set a reasonable redraw frequency so output isn't flooded
90 $this->redrawFreq = null;
91 }
92
93 $this->startTime = time();
94 $this->cursor = new Cursor($output);
95 }
96
97 /**
98 * Sets a placeholder formatter for a given name, globally for all instances of ProgressBar.
99 *
100 * This method also allow you to override an existing placeholder.
101 *
102 * @param string $name The placeholder name (including the delimiter char like %)
103 * @param callable(ProgressBar):string $callable A PHP callable
104 */
105 public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void
106 {
107 self::$formatters ??= self::initPlaceholderFormatters();
108
109 self::$formatters[$name] = $callable;
110 }
111
112 /**
113 * Gets the placeholder formatter for a given name.
114 *
115 * @param string $name The placeholder name (including the delimiter char like %)
116 */
117 public static function getPlaceholderFormatterDefinition(string $name): ?callable
118 {
119 self::$formatters ??= self::initPlaceholderFormatters();
120
121 return self::$formatters[$name] ?? null;
122 }
123
124 /**
125 * Sets a placeholder formatter for a given name, for this instance only.
126 *
127 * @param callable(ProgressBar):string $callable A PHP callable
128 */
129 public function setPlaceholderFormatter(string $name, callable $callable): void
130 {
131 $this->placeholders[$name] = $callable;
132 }
133
134 /**
135 * Gets the placeholder formatter for a given name.
136 *
137 * @param string $name The placeholder name (including the delimiter char like %)
138 */
139 public function getPlaceholderFormatter(string $name): ?callable
140 {
141 return $this->placeholders[$name] ?? $this::getPlaceholderFormatterDefinition($name);
142 }
143
144 /**
145 * Sets a format for a given name.
146 *
147 * This method also allow you to override an existing format.
148 *
149 * @param string $name The format name
150 * @param string $format A format string
151 */
152 public static function setFormatDefinition(string $name, string $format): void
153 {
154 self::$formats ??= self::initFormats();
155
156 self::$formats[$name] = $format;
157 }
158
159 /**
160 * Gets the format for a given name.
161 *
162 * @param string $name The format name
163 */
164 public static function getFormatDefinition(string $name): ?string
165 {
166 self::$formats ??= self::initFormats();
167
168 return self::$formats[$name] ?? null;
169 }
170
171 /**
172 * Associates a text with a named placeholder.
173 *
174 * The text is displayed when the progress bar is rendered but only
175 * when the corresponding placeholder is part of the custom format line
176 * (by wrapping the name with %).
177 *
178 * @param string $message The text to associate with the placeholder
179 * @param string $name The name of the placeholder
180 */
181 public function setMessage(string $message, string $name = 'message'): void
182 {
183 $this->messages[$name] = $message;
184 }
185
186 public function getMessage(string $name = 'message'): ?string
187 {
188 return $this->messages[$name] ?? null;
189 }
190
191 public function getStartTime(): int
192 {
193 return $this->startTime;
194 }
195
196 public function getMaxSteps(): int
197 {
198 return $this->max ?? 0;
199 }
200
201 public function getProgress(): int
202 {
203 return $this->step;
204 }
205
206 private function getStepWidth(): int
207 {
208 return $this->stepWidth;
209 }
210
211 public function getProgressPercent(): float
212 {
213 return $this->percent;
214 }
215
216 public function getBarOffset(): float
217 {
218 return floor(null !== $this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth);
219 }
220
221 public function getEstimated(): float
222 {
223 if (0 === $this->step || $this->step === $this->startingStep) {
224 return 0;
225 }
226
227 return round((time() - $this->startTime) / ($this->step - $this->startingStep) * $this->max);
228 }
229
230 public function getRemaining(): float
231 {
232 if (!$this->step) {
233 return 0;
234 }
235
236 return round((time() - $this->startTime) / ($this->step - $this->startingStep) * ($this->max - $this->step));
237 }
238
239 public function setBarWidth(int $size): void
240 {
241 $this->barWidth = max(1, $size);
242 }
243
244 public function getBarWidth(): int
245 {
246 return $this->barWidth;
247 }
248
249 public function setBarCharacter(string $char): void
250 {
251 $this->barChar = $char;
252 }
253
254 public function getBarCharacter(): string
255 {
256 return $this->barChar ?? (null !== $this->max ? '=' : $this->emptyBarChar);
257 }
258
259 public function setEmptyBarCharacter(string $char): void
260 {
261 $this->emptyBarChar = $char;
262 }
263
264 public function getEmptyBarCharacter(): string
265 {
266 return $this->emptyBarChar;
267 }
268
269 public function setProgressCharacter(string $char): void
270 {
271 $this->progressChar = $char;
272 }
273
274 public function getProgressCharacter(): string
275 {
276 return $this->progressChar;
277 }
278
279 public function setFormat(string $format): void
280 {
281 $this->format = null;
282 $this->internalFormat = $format;
283 }
284
285 /**
286 * Sets the redraw frequency.
287 *
288 * @param int|null $freq The frequency in steps
289 */
290 public function setRedrawFrequency(?int $freq): void
291 {
292 $this->redrawFreq = null !== $freq ? max(1, $freq) : null;
293 }
294
295 public function minSecondsBetweenRedraws(float $seconds): void
296 {
297 $this->minSecondsBetweenRedraws = $seconds;
298 }
299
300 public function maxSecondsBetweenRedraws(float $seconds): void
301 {
302 $this->maxSecondsBetweenRedraws = $seconds;
303 }
304
305 /**
306 * Returns an iterator that will automatically update the progress bar when iterated.
307 *
308 * @template TKey
309 * @template TValue
310 *
311 * @param iterable<TKey, TValue> $iterable
312 * @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable
313 *
314 * @return iterable<TKey, TValue>
315 */
316 public function iterate(iterable $iterable, ?int $max = null): iterable
317 {
318 if (0 === $max) {
319 $max = null;
320 }
321
322 $max ??= is_countable($iterable) ? \count($iterable) : null;
323
324 if (0 === $max) {
325 $this->max = 0;
326 $this->stepWidth = 2;
327 $this->finish();
328
329 return;
330 }
331
332 $this->start($max);
333
334 foreach ($iterable as $key => $value) {
335 yield $key => $value;
336
337 $this->advance();
338 }
339
340 $this->finish();
341 }
342
343 /**
344 * Starts the progress output.
345 *
346 * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
347 * @param int $startAt The starting point of the bar (useful e.g. when resuming a previously started bar)
348 */
349 public function start(?int $max = null, int $startAt = 0): void
350 {
351 $this->startTime = time();
352 $this->step = $startAt;
353 $this->startingStep = $startAt;
354
355 $startAt > 0 ? $this->setProgress($startAt) : $this->percent = 0.0;
356
357 if (null !== $max) {
358 $this->setMaxSteps($max);
359 }
360
361 $this->display();
362 }
363
364 /**
365 * Advances the progress output X steps.
366 *
367 * @param int $step Number of steps to advance
368 */
369 public function advance(int $step = 1): void
370 {
371 $this->setProgress($this->step + $step);
372 }
373
374 /**
375 * Sets whether to overwrite the progressbar, false for new line.
376 */
377 public function setOverwrite(bool $overwrite): void
378 {
379 $this->overwrite = $overwrite;
380 }
381
382 public function setProgress(int $step): void
383 {
384 if ($this->max && $step > $this->max) {
385 $this->max = $step;
386 } elseif ($step < 0) {
387 $step = 0;
388 }
389
390 $redrawFreq = $this->redrawFreq ?? (($this->max ?? 10) / 10);
391 $prevPeriod = $redrawFreq ? (int) ($this->step / $redrawFreq) : 0;
392 $currPeriod = $redrawFreq ? (int) ($step / $redrawFreq) : 0;
393 $this->step = $step;
394 $this->percent = match ($this->max) {
395 null => 0,
396 0 => 1,
397 default => (float) $this->step / $this->max,
398 };
399 $timeInterval = microtime(true) - $this->lastWriteTime;
400
401 // Draw regardless of other limits
402 if ($this->max === $step) {
403 $this->display();
404
405 return;
406 }
407
408 // Throttling
409 if ($timeInterval < $this->minSecondsBetweenRedraws) {
410 return;
411 }
412
413 // Draw each step period, but not too late
414 if ($prevPeriod !== $currPeriod || $timeInterval >= $this->maxSecondsBetweenRedraws) {
415 $this->display();
416 }
417 }
418
419 public function setMaxSteps(?int $max): void
420 {
421 if (0 === $max) {
422 $max = null;
423 }
424
425 $this->format = null;
426 if (null === $max) {
427 $this->max = null;
428 $this->stepWidth = 4;
429 } else {
430 $this->max = max(0, $max);
431 $this->stepWidth = Helper::width((string) $this->max);
432 }
433 }
434
435 /**
436 * Finishes the progress output.
437 */
438 public function finish(): void
439 {
440 if (null === $this->max) {
441 $this->max = $this->step;
442 }
443
444 if (($this->step === $this->max || null === $this->max) && !$this->overwrite) {
445 // prevent double 100% output
446 return;
447 }
448
449 $this->setProgress($this->max ?? $this->step);
450 }
451
452 /**
453 * Outputs the current progress string.
454 */
455 public function display(): void
456 {
457 if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
458 return;
459 }
460
461 if (null === $this->format) {
462 $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
463 }
464
465 $this->overwrite($this->buildLine());
466 }
467
468 /**
469 * Removes the progress bar from the current line.
470 *
471 * This is useful if you wish to write some output
472 * while a progress bar is running.
473 * Call display() to show the progress bar again.
474 */
475 public function clear(): void
476 {
477 if (!$this->overwrite) {
478 return;
479 }
480
481 if (null === $this->format) {
482 $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
483 }
484
485 $this->overwrite('');
486 }
487
488 private function setRealFormat(string $format): void
489 {
490 // try to use the _nomax variant if available
491 if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
492 $this->format = self::getFormatDefinition($format.'_nomax');
493 } elseif (null !== self::getFormatDefinition($format)) {
494 $this->format = self::getFormatDefinition($format);
495 } else {
496 $this->format = $format;
497 }
498 }
499
500 /**
501 * Overwrites a previous message to the output.
502 */
503 private function overwrite(string $message): void
504 {
505 if ($this->previousMessage === $message) {
506 return;
507 }
508
509 $originalMessage = $message;
510
511 if ($this->overwrite) {
512 if (null !== $this->previousMessage) {
513 if ($this->output instanceof ConsoleSectionOutput) {
514 $messageLines = explode("\n", $this->previousMessage);
515 $lineCount = \count($messageLines);
516 foreach ($messageLines as $messageLine) {
517 $messageLineLength = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $messageLine));
518 if ($messageLineLength > $this->terminal->getWidth()) {
519 $lineCount += floor($messageLineLength / $this->terminal->getWidth());
520 }
521 }
522 $this->output->clear($lineCount);
523 } else {
524 $lineCount = substr_count($this->previousMessage, "\n");
525 for ($i = 0; $i < $lineCount; ++$i) {
526 $this->cursor->moveToColumn(1);
527 $this->cursor->clearLine();
528 $this->cursor->moveUp();
529 }
530
531 $this->cursor->moveToColumn(1);
532 $this->cursor->clearLine();
533 }
534 }
535 } elseif ($this->step > 0) {
536 $message = \PHP_EOL.$message;
537 }
538
539 $this->previousMessage = $originalMessage;
540 $this->lastWriteTime = microtime(true);
541
542 $this->output->write($message);
543 ++$this->writeCount;
544 }
545
546 private function determineBestFormat(): string
547 {
548 return match ($this->output->getVerbosity()) {
549 // OutputInterface::VERBOSITY_QUIET: display is disabled anyway
550 OutputInterface::VERBOSITY_VERBOSE => $this->max ? self::FORMAT_VERBOSE : self::FORMAT_VERBOSE_NOMAX,
551 OutputInterface::VERBOSITY_VERY_VERBOSE => $this->max ? self::FORMAT_VERY_VERBOSE : self::FORMAT_VERY_VERBOSE_NOMAX,
552 OutputInterface::VERBOSITY_DEBUG => $this->max ? self::FORMAT_DEBUG : self::FORMAT_DEBUG_NOMAX,
553 default => $this->max ? self::FORMAT_NORMAL : self::FORMAT_NORMAL_NOMAX,
554 };
555 }
556
557 private static function initPlaceholderFormatters(): array
558 {
559 return [
560 'bar' => function (self $bar, OutputInterface $output) {
561 $completeBars = $bar->getBarOffset();
562 $display = str_repeat($bar->getBarCharacter(), $completeBars);
563 if ($completeBars < $bar->getBarWidth()) {
564 $emptyBars = $bar->getBarWidth() - $completeBars - Helper::length(Helper::removeDecoration($output->getFormatter(), $bar->getProgressCharacter()));
565 $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars);
566 }
567
568 return $display;
569 },
570 'elapsed' => fn (self $bar) => Helper::formatTime(time() - $bar->getStartTime(), 2),
571 'remaining' => function (self $bar) {
572 if (null === $bar->getMaxSteps()) {
573 throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
574 }
575
576 return Helper::formatTime($bar->getRemaining(), 2);
577 },
578 'estimated' => function (self $bar) {
579 if (null === $bar->getMaxSteps()) {
580 throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
581 }
582
583 return Helper::formatTime($bar->getEstimated(), 2);
584 },
585 'memory' => fn (self $bar) => Helper::formatMemory(memory_get_usage(true)),
586 'current' => fn (self $bar) => str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT),
587 'max' => fn (self $bar) => $bar->getMaxSteps(),
588 'percent' => fn (self $bar) => floor($bar->getProgressPercent() * 100),
589 ];
590 }
591
592 private static function initFormats(): array
593 {
594 return [
595 self::FORMAT_NORMAL => ' %current%/%max% [%bar%] %percent:3s%%',
596 self::FORMAT_NORMAL_NOMAX => ' %current% [%bar%]',
597
598 self::FORMAT_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%',
599 self::FORMAT_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%',
600
601 self::FORMAT_VERY_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%',
602 self::FORMAT_VERY_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%',
603
604 self::FORMAT_DEBUG => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%',
605 self::FORMAT_DEBUG_NOMAX => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
606 ];
607 }
608
609 private function buildLine(): string
610 {
611 \assert(null !== $this->format);
612
613 $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
614 $callback = function ($matches) {
615 if ($formatter = $this->getPlaceholderFormatter($matches[1])) {
616 $text = $formatter($this, $this->output);
617 } elseif (isset($this->messages[$matches[1]])) {
618 $text = $this->messages[$matches[1]];
619 } else {
620 return $matches[0];
621 }
622
623 if (isset($matches[2])) {
624 $text = sprintf('%'.$matches[2], $text);
625 }
626
627 return $text;
628 };
629 $line = preg_replace_callback($regex, $callback, $this->format);
630
631 // gets string length for each sub line with multiline format
632 $linesLength = array_map(fn ($subLine) => Helper::width(Helper::removeDecoration($this->output->getFormatter(), rtrim($subLine, "\r"))), explode("\n", $line));
633
634 $linesWidth = max($linesLength);
635
636 $terminalWidth = $this->terminal->getWidth();
637 if ($linesWidth <= $terminalWidth) {
638 return $line;
639 }
640
641 $this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth);
642
643 return preg_replace_callback($regex, $callback, $this->format);
644 }
645}
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}
diff --git a/vendor/symfony/console/Helper/QuestionHelper.php b/vendor/symfony/console/Helper/QuestionHelper.php
new file mode 100644
index 0000000..54825c6
--- /dev/null
+++ b/vendor/symfony/console/Helper/QuestionHelper.php
@@ -0,0 +1,589 @@
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\Cursor;
15use Symfony\Component\Console\Exception\MissingInputException;
16use Symfony\Component\Console\Exception\RuntimeException;
17use Symfony\Component\Console\Formatter\OutputFormatter;
18use Symfony\Component\Console\Formatter\OutputFormatterStyle;
19use Symfony\Component\Console\Input\InputInterface;
20use Symfony\Component\Console\Input\StreamableInputInterface;
21use Symfony\Component\Console\Output\ConsoleOutputInterface;
22use Symfony\Component\Console\Output\ConsoleSectionOutput;
23use Symfony\Component\Console\Output\OutputInterface;
24use Symfony\Component\Console\Question\ChoiceQuestion;
25use Symfony\Component\Console\Question\Question;
26use Symfony\Component\Console\Terminal;
27
28use function Symfony\Component\String\s;
29
30/**
31 * The QuestionHelper class provides helpers to interact with the user.
32 *
33 * @author Fabien Potencier <fabien@symfony.com>
34 */
35class QuestionHelper extends Helper
36{
37 private static bool $stty = true;
38 private static bool $stdinIsInteractive;
39
40 /**
41 * Asks a question to the user.
42 *
43 * @return mixed The user answer
44 *
45 * @throws RuntimeException If there is no data to read in the input stream
46 */
47 public function ask(InputInterface $input, OutputInterface $output, Question $question): mixed
48 {
49 if ($output instanceof ConsoleOutputInterface) {
50 $output = $output->getErrorOutput();
51 }
52
53 if (!$input->isInteractive()) {
54 return $this->getDefaultAnswer($question);
55 }
56
57 $inputStream = $input instanceof StreamableInputInterface ? $input->getStream() : null;
58 $inputStream ??= STDIN;
59
60 try {
61 if (!$question->getValidator()) {
62 return $this->doAsk($inputStream, $output, $question);
63 }
64
65 $interviewer = fn () => $this->doAsk($inputStream, $output, $question);
66
67 return $this->validateAttempts($interviewer, $output, $question);
68 } catch (MissingInputException $exception) {
69 $input->setInteractive(false);
70
71 if (null === $fallbackOutput = $this->getDefaultAnswer($question)) {
72 throw $exception;
73 }
74
75 return $fallbackOutput;
76 }
77 }
78
79 public function getName(): string
80 {
81 return 'question';
82 }
83
84 /**
85 * Prevents usage of stty.
86 */
87 public static function disableStty(): void
88 {
89 self::$stty = false;
90 }
91
92 /**
93 * Asks the question to the user.
94 *
95 * @param resource $inputStream
96 *
97 * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
98 */
99 private function doAsk($inputStream, OutputInterface $output, Question $question): mixed
100 {
101 $this->writePrompt($output, $question);
102
103 $autocomplete = $question->getAutocompleterCallback();
104
105 if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) {
106 $ret = false;
107 if ($question->isHidden()) {
108 try {
109 $hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable());
110 $ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse;
111 } catch (RuntimeException $e) {
112 if (!$question->isHiddenFallback()) {
113 throw $e;
114 }
115 }
116 }
117
118 if (false === $ret) {
119 $isBlocked = stream_get_meta_data($inputStream)['blocked'] ?? true;
120
121 if (!$isBlocked) {
122 stream_set_blocking($inputStream, true);
123 }
124
125 $ret = $this->readInput($inputStream, $question);
126
127 if (!$isBlocked) {
128 stream_set_blocking($inputStream, false);
129 }
130
131 if (false === $ret) {
132 throw new MissingInputException('Aborted.');
133 }
134 if ($question->isTrimmable()) {
135 $ret = trim($ret);
136 }
137 }
138 } else {
139 $autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete);
140 $ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete;
141 }
142
143 if ($output instanceof ConsoleSectionOutput) {
144 $output->addContent(''); // add EOL to the question
145 $output->addContent($ret);
146 }
147
148 $ret = \strlen($ret) > 0 ? $ret : $question->getDefault();
149
150 if ($normalizer = $question->getNormalizer()) {
151 return $normalizer($ret);
152 }
153
154 return $ret;
155 }
156
157 private function getDefaultAnswer(Question $question): mixed
158 {
159 $default = $question->getDefault();
160
161 if (null === $default) {
162 return $default;
163 }
164
165 if ($validator = $question->getValidator()) {
166 return \call_user_func($validator, $default);
167 } elseif ($question instanceof ChoiceQuestion) {
168 $choices = $question->getChoices();
169
170 if (!$question->isMultiselect()) {
171 return $choices[$default] ?? $default;
172 }
173
174 $default = explode(',', $default);
175 foreach ($default as $k => $v) {
176 $v = $question->isTrimmable() ? trim($v) : $v;
177 $default[$k] = $choices[$v] ?? $v;
178 }
179 }
180
181 return $default;
182 }
183
184 /**
185 * Outputs the question prompt.
186 */
187 protected function writePrompt(OutputInterface $output, Question $question): void
188 {
189 $message = $question->getQuestion();
190
191 if ($question instanceof ChoiceQuestion) {
192 $output->writeln(array_merge([
193 $question->getQuestion(),
194 ], $this->formatChoiceQuestionChoices($question, 'info')));
195
196 $message = $question->getPrompt();
197 }
198
199 $output->write($message);
200 }
201
202 /**
203 * @return string[]
204 */
205 protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag): array
206 {
207 $messages = [];
208
209 $maxWidth = max(array_map([__CLASS__, 'width'], array_keys($choices = $question->getChoices())));
210
211 foreach ($choices as $key => $value) {
212 $padding = str_repeat(' ', $maxWidth - self::width($key));
213
214 $messages[] = sprintf(" [<$tag>%s$padding</$tag>] %s", $key, $value);
215 }
216
217 return $messages;
218 }
219
220 /**
221 * Outputs an error message.
222 */
223 protected function writeError(OutputInterface $output, \Exception $error): void
224 {
225 if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
226 $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
227 } else {
228 $message = '<error>'.$error->getMessage().'</error>';
229 }
230
231 $output->writeln($message);
232 }
233
234 /**
235 * Autocompletes a question.
236 *
237 * @param resource $inputStream
238 */
239 private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string
240 {
241 $cursor = new Cursor($output, $inputStream);
242
243 $fullChoice = '';
244 $ret = '';
245
246 $i = 0;
247 $ofs = -1;
248 $matches = $autocomplete($ret);
249 $numMatches = \count($matches);
250
251 $sttyMode = shell_exec('stty -g');
252 $isStdin = 'php://stdin' === (stream_get_meta_data($inputStream)['uri'] ?? null);
253 $r = [$inputStream];
254 $w = [];
255
256 // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
257 shell_exec('stty -icanon -echo');
258
259 // Add highlighted text style
260 $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
261
262 // Read a keypress
263 while (!feof($inputStream)) {
264 while ($isStdin && 0 === @stream_select($r, $w, $w, 0, 100)) {
265 // Give signal handlers a chance to run
266 $r = [$inputStream];
267 }
268 $c = fread($inputStream, 1);
269
270 // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
271 if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
272 shell_exec('stty '.$sttyMode);
273 throw new MissingInputException('Aborted.');
274 } elseif ("\177" === $c) { // Backspace Character
275 if (0 === $numMatches && 0 !== $i) {
276 --$i;
277 $cursor->moveLeft(s($fullChoice)->slice(-1)->width(false));
278
279 $fullChoice = self::substr($fullChoice, 0, $i);
280 }
281
282 if (0 === $i) {
283 $ofs = -1;
284 $matches = $autocomplete($ret);
285 $numMatches = \count($matches);
286 } else {
287 $numMatches = 0;
288 }
289
290 // Pop the last character off the end of our string
291 $ret = self::substr($ret, 0, $i);
292 } elseif ("\033" === $c) {
293 // Did we read an escape sequence?
294 $c .= fread($inputStream, 2);
295
296 // A = Up Arrow. B = Down Arrow
297 if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
298 if ('A' === $c[2] && -1 === $ofs) {
299 $ofs = 0;
300 }
301
302 if (0 === $numMatches) {
303 continue;
304 }
305
306 $ofs += ('A' === $c[2]) ? -1 : 1;
307 $ofs = ($numMatches + $ofs) % $numMatches;
308 }
309 } elseif (\ord($c) < 32) {
310 if ("\t" === $c || "\n" === $c) {
311 if ($numMatches > 0 && -1 !== $ofs) {
312 $ret = (string) $matches[$ofs];
313 // Echo out remaining chars for current match
314 $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
315 $output->write($remainingCharacters);
316 $fullChoice .= $remainingCharacters;
317 $i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding);
318
319 $matches = array_filter(
320 $autocomplete($ret),
321 fn ($match) => '' === $ret || str_starts_with($match, $ret)
322 );
323 $numMatches = \count($matches);
324 $ofs = -1;
325 }
326
327 if ("\n" === $c) {
328 $output->write($c);
329 break;
330 }
331
332 $numMatches = 0;
333 }
334
335 continue;
336 } else {
337 if ("\x80" <= $c) {
338 $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
339 }
340
341 $output->write($c);
342 $ret .= $c;
343 $fullChoice .= $c;
344 ++$i;
345
346 $tempRet = $ret;
347
348 if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
349 $tempRet = $this->mostRecentlyEnteredValue($fullChoice);
350 }
351
352 $numMatches = 0;
353 $ofs = 0;
354
355 foreach ($autocomplete($ret) as $value) {
356 // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
357 if (str_starts_with($value, $tempRet)) {
358 $matches[$numMatches++] = $value;
359 }
360 }
361 }
362
363 $cursor->clearLineAfter();
364
365 if ($numMatches > 0 && -1 !== $ofs) {
366 $cursor->savePosition();
367 // Write highlighted text, complete the partially entered response
368 $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
369 $output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
370 $cursor->restorePosition();
371 }
372 }
373
374 // Reset stty so it behaves normally again
375 shell_exec('stty '.$sttyMode);
376
377 return $fullChoice;
378 }
379
380 private function mostRecentlyEnteredValue(string $entered): string
381 {
382 // Determine the most recent value that the user entered
383 if (!str_contains($entered, ',')) {
384 return $entered;
385 }
386
387 $choices = explode(',', $entered);
388 if ('' !== $lastChoice = trim($choices[\count($choices) - 1])) {
389 return $lastChoice;
390 }
391
392 return $entered;
393 }
394
395 /**
396 * Gets a hidden response from user.
397 *
398 * @param resource $inputStream The handler resource
399 * @param bool $trimmable Is the answer trimmable
400 *
401 * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
402 */
403 private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string
404 {
405 if ('\\' === \DIRECTORY_SEPARATOR) {
406 $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
407
408 // handle code running from a phar
409 if (str_starts_with(__FILE__, 'phar:')) {
410 $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
411 copy($exe, $tmpExe);
412 $exe = $tmpExe;
413 }
414
415 $sExec = shell_exec('"'.$exe.'"');
416 $value = $trimmable ? rtrim($sExec) : $sExec;
417 $output->writeln('');
418
419 if (isset($tmpExe)) {
420 unlink($tmpExe);
421 }
422
423 return $value;
424 }
425
426 if (self::$stty && Terminal::hasSttyAvailable()) {
427 $sttyMode = shell_exec('stty -g');
428 shell_exec('stty -echo');
429 } elseif ($this->isInteractiveInput($inputStream)) {
430 throw new RuntimeException('Unable to hide the response.');
431 }
432
433 $value = fgets($inputStream, 4096);
434
435 if (4095 === \strlen($value)) {
436 $errOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
437 $errOutput->warning('The value was possibly truncated by your shell or terminal emulator');
438 }
439
440 if (self::$stty && Terminal::hasSttyAvailable()) {
441 shell_exec('stty '.$sttyMode);
442 }
443
444 if (false === $value) {
445 throw new MissingInputException('Aborted.');
446 }
447 if ($trimmable) {
448 $value = trim($value);
449 }
450 $output->writeln('');
451
452 return $value;
453 }
454
455 /**
456 * Validates an attempt.
457 *
458 * @param callable $interviewer A callable that will ask for a question and return the result
459 *
460 * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
461 */
462 private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question): mixed
463 {
464 $error = null;
465 $attempts = $question->getMaxAttempts();
466
467 while (null === $attempts || $attempts--) {
468 if (null !== $error) {
469 $this->writeError($output, $error);
470 }
471
472 try {
473 return $question->getValidator()($interviewer());
474 } catch (RuntimeException $e) {
475 throw $e;
476 } catch (\Exception $error) {
477 }
478 }
479
480 throw $error;
481 }
482
483 private function isInteractiveInput($inputStream): bool
484 {
485 if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) {
486 return false;
487 }
488
489 if (isset(self::$stdinIsInteractive)) {
490 return self::$stdinIsInteractive;
491 }
492
493 return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r'));
494 }
495
496 /**
497 * Reads one or more lines of input and returns what is read.
498 *
499 * @param resource $inputStream The handler resource
500 * @param Question $question The question being asked
501 */
502 private function readInput($inputStream, Question $question): string|false
503 {
504 if (!$question->isMultiline()) {
505 $cp = $this->setIOCodepage();
506 $ret = fgets($inputStream, 4096);
507
508 return $this->resetIOCodepage($cp, $ret);
509 }
510
511 $multiLineStreamReader = $this->cloneInputStream($inputStream);
512 if (null === $multiLineStreamReader) {
513 return false;
514 }
515
516 $ret = '';
517 $cp = $this->setIOCodepage();
518 while (false !== ($char = fgetc($multiLineStreamReader))) {
519 if (\PHP_EOL === "{$ret}{$char}") {
520 break;
521 }
522 $ret .= $char;
523 }
524
525 return $this->resetIOCodepage($cp, $ret);
526 }
527
528 private function setIOCodepage(): int
529 {
530 if (\function_exists('sapi_windows_cp_set')) {
531 $cp = sapi_windows_cp_get();
532 sapi_windows_cp_set(sapi_windows_cp_get('oem'));
533
534 return $cp;
535 }
536
537 return 0;
538 }
539
540 /**
541 * Sets console I/O to the specified code page and converts the user input.
542 */
543 private function resetIOCodepage(int $cp, string|false $input): string|false
544 {
545 if (0 !== $cp) {
546 sapi_windows_cp_set($cp);
547
548 if (false !== $input && '' !== $input) {
549 $input = sapi_windows_cp_conv(sapi_windows_cp_get('oem'), $cp, $input);
550 }
551 }
552
553 return $input;
554 }
555
556 /**
557 * Clones an input stream in order to act on one instance of the same
558 * stream without affecting the other instance.
559 *
560 * @param resource $inputStream The handler resource
561 *
562 * @return resource|null The cloned resource, null in case it could not be cloned
563 */
564 private function cloneInputStream($inputStream)
565 {
566 $streamMetaData = stream_get_meta_data($inputStream);
567 $seekable = $streamMetaData['seekable'] ?? false;
568 $mode = $streamMetaData['mode'] ?? 'rb';
569 $uri = $streamMetaData['uri'] ?? null;
570
571 if (null === $uri) {
572 return null;
573 }
574
575 $cloneStream = fopen($uri, $mode);
576
577 // For seekable and writable streams, add all the same data to the
578 // cloned stream and then seek to the same offset.
579 if (true === $seekable && !\in_array($mode, ['r', 'rb', 'rt'])) {
580 $offset = ftell($inputStream);
581 rewind($inputStream);
582 stream_copy_to_stream($inputStream, $cloneStream);
583 fseek($inputStream, $offset);
584 fseek($cloneStream, $offset);
585 }
586
587 return $cloneStream;
588 }
589}
diff --git a/vendor/symfony/console/Helper/SymfonyQuestionHelper.php b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php
new file mode 100644
index 0000000..48d947b
--- /dev/null
+++ b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php
@@ -0,0 +1,103 @@
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\Formatter\OutputFormatter;
15use Symfony\Component\Console\Output\OutputInterface;
16use Symfony\Component\Console\Question\ChoiceQuestion;
17use Symfony\Component\Console\Question\ConfirmationQuestion;
18use Symfony\Component\Console\Question\Question;
19use Symfony\Component\Console\Style\SymfonyStyle;
20
21/**
22 * Symfony Style Guide compliant question helper.
23 *
24 * @author Kevin Bond <kevinbond@gmail.com>
25 */
26class SymfonyQuestionHelper extends QuestionHelper
27{
28 protected function writePrompt(OutputInterface $output, Question $question): void
29 {
30 $text = OutputFormatter::escapeTrailingBackslash($question->getQuestion());
31 $default = $question->getDefault();
32
33 if ($question->isMultiline()) {
34 $text .= sprintf(' (press %s to continue)', $this->getEofShortcut());
35 }
36
37 switch (true) {
38 case null === $default:
39 $text = sprintf(' <info>%s</info>:', $text);
40
41 break;
42
43 case $question instanceof ConfirmationQuestion:
44 $text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no');
45
46 break;
47
48 case $question instanceof ChoiceQuestion && $question->isMultiselect():
49 $choices = $question->getChoices();
50 $default = explode(',', $default);
51
52 foreach ($default as $key => $value) {
53 $default[$key] = $choices[trim($value)];
54 }
55
56 $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape(implode(', ', $default)));
57
58 break;
59
60 case $question instanceof ChoiceQuestion:
61 $choices = $question->getChoices();
62 $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($choices[$default] ?? $default));
63
64 break;
65
66 default:
67 $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($default));
68 }
69
70 $output->writeln($text);
71
72 $prompt = ' > ';
73
74 if ($question instanceof ChoiceQuestion) {
75 $output->writeln($this->formatChoiceQuestionChoices($question, 'comment'));
76
77 $prompt = $question->getPrompt();
78 }
79
80 $output->write($prompt);
81 }
82
83 protected function writeError(OutputInterface $output, \Exception $error): void
84 {
85 if ($output instanceof SymfonyStyle) {
86 $output->newLine();
87 $output->error($error->getMessage());
88
89 return;
90 }
91
92 parent::writeError($output, $error);
93 }
94
95 private function getEofShortcut(): string
96 {
97 if ('Windows' === \PHP_OS_FAMILY) {
98 return '<comment>Ctrl+Z</comment> then <comment>Enter</comment>';
99 }
100
101 return '<comment>Ctrl+D</comment>';
102 }
103}
diff --git a/vendor/symfony/console/Helper/Table.php b/vendor/symfony/console/Helper/Table.php
new file mode 100644
index 0000000..09709a2
--- /dev/null
+++ b/vendor/symfony/console/Helper/Table.php
@@ -0,0 +1,924 @@
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\RuntimeException;
16use Symfony\Component\Console\Formatter\OutputFormatter;
17use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface;
18use Symfony\Component\Console\Output\ConsoleSectionOutput;
19use Symfony\Component\Console\Output\OutputInterface;
20
21/**
22 * Provides helpers to display a table.
23 *
24 * @author Fabien Potencier <fabien@symfony.com>
25 * @author Саша Стаменковић <umpirsky@gmail.com>
26 * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
27 * @author Max Grigorian <maxakawizard@gmail.com>
28 * @author Dany Maillard <danymaillard93b@gmail.com>
29 */
30class Table
31{
32 private const SEPARATOR_TOP = 0;
33 private const SEPARATOR_TOP_BOTTOM = 1;
34 private const SEPARATOR_MID = 2;
35 private const SEPARATOR_BOTTOM = 3;
36 private const BORDER_OUTSIDE = 0;
37 private const BORDER_INSIDE = 1;
38 private const DISPLAY_ORIENTATION_DEFAULT = 'default';
39 private const DISPLAY_ORIENTATION_HORIZONTAL = 'horizontal';
40 private const DISPLAY_ORIENTATION_VERTICAL = 'vertical';
41
42 private ?string $headerTitle = null;
43 private ?string $footerTitle = null;
44 private array $headers = [];
45 private array $rows = [];
46 private array $effectiveColumnWidths = [];
47 private int $numberOfColumns;
48 private TableStyle $style;
49 private array $columnStyles = [];
50 private array $columnWidths = [];
51 private array $columnMaxWidths = [];
52 private bool $rendered = false;
53 private string $displayOrientation = self::DISPLAY_ORIENTATION_DEFAULT;
54
55 private static array $styles;
56
57 public function __construct(
58 private OutputInterface $output,
59 ) {
60 self::$styles ??= self::initStyles();
61
62 $this->setStyle('default');
63 }
64
65 /**
66 * Sets a style definition.
67 */
68 public static function setStyleDefinition(string $name, TableStyle $style): void
69 {
70 self::$styles ??= self::initStyles();
71
72 self::$styles[$name] = $style;
73 }
74
75 /**
76 * Gets a style definition by name.
77 */
78 public static function getStyleDefinition(string $name): TableStyle
79 {
80 self::$styles ??= self::initStyles();
81
82 return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
83 }
84
85 /**
86 * Sets table style.
87 *
88 * @return $this
89 */
90 public function setStyle(TableStyle|string $name): static
91 {
92 $this->style = $this->resolveStyle($name);
93
94 return $this;
95 }
96
97 /**
98 * Gets the current table style.
99 */
100 public function getStyle(): TableStyle
101 {
102 return $this->style;
103 }
104
105 /**
106 * Sets table column style.
107 *
108 * @param TableStyle|string $name The style name or a TableStyle instance
109 *
110 * @return $this
111 */
112 public function setColumnStyle(int $columnIndex, TableStyle|string $name): static
113 {
114 $this->columnStyles[$columnIndex] = $this->resolveStyle($name);
115
116 return $this;
117 }
118
119 /**
120 * Gets the current style for a column.
121 *
122 * If style was not set, it returns the global table style.
123 */
124 public function getColumnStyle(int $columnIndex): TableStyle
125 {
126 return $this->columnStyles[$columnIndex] ?? $this->getStyle();
127 }
128
129 /**
130 * Sets the minimum width of a column.
131 *
132 * @return $this
133 */
134 public function setColumnWidth(int $columnIndex, int $width): static
135 {
136 $this->columnWidths[$columnIndex] = $width;
137
138 return $this;
139 }
140
141 /**
142 * Sets the minimum width of all columns.
143 *
144 * @return $this
145 */
146 public function setColumnWidths(array $widths): static
147 {
148 $this->columnWidths = [];
149 foreach ($widths as $index => $width) {
150 $this->setColumnWidth($index, $width);
151 }
152
153 return $this;
154 }
155
156 /**
157 * Sets the maximum width of a column.
158 *
159 * Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while
160 * formatted strings are preserved.
161 *
162 * @return $this
163 */
164 public function setColumnMaxWidth(int $columnIndex, int $width): static
165 {
166 if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) {
167 throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter())));
168 }
169
170 $this->columnMaxWidths[$columnIndex] = $width;
171
172 return $this;
173 }
174
175 /**
176 * @return $this
177 */
178 public function setHeaders(array $headers): static
179 {
180 $headers = array_values($headers);
181 if ($headers && !\is_array($headers[0])) {
182 $headers = [$headers];
183 }
184
185 $this->headers = $headers;
186
187 return $this;
188 }
189
190 /**
191 * @return $this
192 */
193 public function setRows(array $rows): static
194 {
195 $this->rows = [];
196
197 return $this->addRows($rows);
198 }
199
200 /**
201 * @return $this
202 */
203 public function addRows(array $rows): static
204 {
205 foreach ($rows as $row) {
206 $this->addRow($row);
207 }
208
209 return $this;
210 }
211
212 /**
213 * @return $this
214 */
215 public function addRow(TableSeparator|array $row): static
216 {
217 if ($row instanceof TableSeparator) {
218 $this->rows[] = $row;
219
220 return $this;
221 }
222
223 $this->rows[] = array_values($row);
224
225 return $this;
226 }
227
228 /**
229 * Adds a row to the table, and re-renders the table.
230 *
231 * @return $this
232 */
233 public function appendRow(TableSeparator|array $row): static
234 {
235 if (!$this->output instanceof ConsoleSectionOutput) {
236 throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__));
237 }
238
239 if ($this->rendered) {
240 $this->output->clear($this->calculateRowCount());
241 }
242
243 $this->addRow($row);
244 $this->render();
245
246 return $this;
247 }
248
249 /**
250 * @return $this
251 */
252 public function setRow(int|string $column, array $row): static
253 {
254 $this->rows[$column] = $row;
255
256 return $this;
257 }
258
259 /**
260 * @return $this
261 */
262 public function setHeaderTitle(?string $title): static
263 {
264 $this->headerTitle = $title;
265
266 return $this;
267 }
268
269 /**
270 * @return $this
271 */
272 public function setFooterTitle(?string $title): static
273 {
274 $this->footerTitle = $title;
275
276 return $this;
277 }
278
279 /**
280 * @return $this
281 */
282 public function setHorizontal(bool $horizontal = true): static
283 {
284 $this->displayOrientation = $horizontal ? self::DISPLAY_ORIENTATION_HORIZONTAL : self::DISPLAY_ORIENTATION_DEFAULT;
285
286 return $this;
287 }
288
289 /**
290 * @return $this
291 */
292 public function setVertical(bool $vertical = true): static
293 {
294 $this->displayOrientation = $vertical ? self::DISPLAY_ORIENTATION_VERTICAL : self::DISPLAY_ORIENTATION_DEFAULT;
295
296 return $this;
297 }
298
299 /**
300 * Renders table to output.
301 *
302 * Example:
303 *
304 * +---------------+-----------------------+------------------+
305 * | ISBN | Title | Author |
306 * +---------------+-----------------------+------------------+
307 * | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
308 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
309 * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
310 * +---------------+-----------------------+------------------+
311 */
312 public function render(): void
313 {
314 $divider = new TableSeparator();
315 $isCellWithColspan = static fn ($cell) => $cell instanceof TableCell && $cell->getColspan() >= 2;
316
317 $horizontal = self::DISPLAY_ORIENTATION_HORIZONTAL === $this->displayOrientation;
318 $vertical = self::DISPLAY_ORIENTATION_VERTICAL === $this->displayOrientation;
319
320 $rows = [];
321 if ($horizontal) {
322 foreach ($this->headers[0] ?? [] as $i => $header) {
323 $rows[$i] = [$header];
324 foreach ($this->rows as $row) {
325 if ($row instanceof TableSeparator) {
326 continue;
327 }
328 if (isset($row[$i])) {
329 $rows[$i][] = $row[$i];
330 } elseif ($isCellWithColspan($rows[$i][0])) {
331 // Noop, there is a "title"
332 } else {
333 $rows[$i][] = null;
334 }
335 }
336 }
337 } elseif ($vertical) {
338 $formatter = $this->output->getFormatter();
339 $maxHeaderLength = array_reduce($this->headers[0] ?? [], static fn ($max, $header) => max($max, Helper::width(Helper::removeDecoration($formatter, $header))), 0);
340
341 foreach ($this->rows as $row) {
342 if ($row instanceof TableSeparator) {
343 continue;
344 }
345
346 if ($rows) {
347 $rows[] = [$divider];
348 }
349
350 $containsColspan = false;
351 foreach ($row as $cell) {
352 if ($containsColspan = $isCellWithColspan($cell)) {
353 break;
354 }
355 }
356
357 $headers = $this->headers[0] ?? [];
358 $maxRows = max(\count($headers), \count($row));
359 for ($i = 0; $i < $maxRows; ++$i) {
360 $cell = (string) ($row[$i] ?? '');
361
362 $eol = str_contains($cell, "\r\n") ? "\r\n" : "\n";
363 $parts = explode($eol, $cell);
364 foreach ($parts as $idx => $part) {
365 if ($headers && !$containsColspan) {
366 if (0 === $idx) {
367 $rows[] = [sprintf(
368 '<comment>%s%s</>: %s',
369 str_repeat(' ', $maxHeaderLength - Helper::width(Helper::removeDecoration($formatter, $headers[$i] ?? ''))),
370 $headers[$i] ?? '',
371 $part
372 )];
373 } else {
374 $rows[] = [sprintf(
375 '%s %s',
376 str_pad('', $maxHeaderLength, ' ', \STR_PAD_LEFT),
377 $part
378 )];
379 }
380 } elseif ('' !== $cell) {
381 $rows[] = [$part];
382 }
383 }
384 }
385 }
386 } else {
387 $rows = array_merge($this->headers, [$divider], $this->rows);
388 }
389
390 $this->calculateNumberOfColumns($rows);
391
392 $rowGroups = $this->buildTableRows($rows);
393 $this->calculateColumnsWidth($rowGroups);
394
395 $isHeader = !$horizontal;
396 $isFirstRow = $horizontal;
397 $hasTitle = (bool) $this->headerTitle;
398
399 foreach ($rowGroups as $rowGroup) {
400 $isHeaderSeparatorRendered = false;
401
402 foreach ($rowGroup as $row) {
403 if ($divider === $row) {
404 $isHeader = false;
405 $isFirstRow = true;
406
407 continue;
408 }
409
410 if ($row instanceof TableSeparator) {
411 $this->renderRowSeparator();
412
413 continue;
414 }
415
416 if (!$row) {
417 continue;
418 }
419
420 if ($isHeader && !$isHeaderSeparatorRendered) {
421 $this->renderRowSeparator(
422 self::SEPARATOR_TOP,
423 $hasTitle ? $this->headerTitle : null,
424 $hasTitle ? $this->style->getHeaderTitleFormat() : null
425 );
426 $hasTitle = false;
427 $isHeaderSeparatorRendered = true;
428 }
429
430 if ($isFirstRow) {
431 $this->renderRowSeparator(
432 $horizontal ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM,
433 $hasTitle ? $this->headerTitle : null,
434 $hasTitle ? $this->style->getHeaderTitleFormat() : null
435 );
436 $isFirstRow = false;
437 $hasTitle = false;
438 }
439
440 if ($vertical) {
441 $isHeader = false;
442 $isFirstRow = false;
443 }
444
445 if ($horizontal) {
446 $this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat());
447 } else {
448 $this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
449 }
450 }
451 }
452 $this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat());
453
454 $this->cleanup();
455 $this->rendered = true;
456 }
457
458 /**
459 * Renders horizontal header separator.
460 *
461 * Example:
462 *
463 * +-----+-----------+-------+
464 */
465 private function renderRowSeparator(int $type = self::SEPARATOR_MID, ?string $title = null, ?string $titleFormat = null): void
466 {
467 if (!$count = $this->numberOfColumns) {
468 return;
469 }
470
471 $borders = $this->style->getBorderChars();
472 if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) {
473 return;
474 }
475
476 $crossings = $this->style->getCrossingChars();
477 if (self::SEPARATOR_MID === $type) {
478 [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]];
479 } elseif (self::SEPARATOR_TOP === $type) {
480 [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]];
481 } elseif (self::SEPARATOR_TOP_BOTTOM === $type) {
482 [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]];
483 } else {
484 [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]];
485 }
486
487 $markup = $leftChar;
488 for ($column = 0; $column < $count; ++$column) {
489 $markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]);
490 $markup .= $column === $count - 1 ? $rightChar : $midChar;
491 }
492
493 if (null !== $title) {
494 $titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title)));
495 $markupLength = Helper::width($markup);
496 if ($titleLength > $limit = $markupLength - 4) {
497 $titleLength = $limit;
498 $formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, '')));
499 $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...');
500 }
501
502 $titleStart = intdiv($markupLength - $titleLength, 2);
503 if (false === mb_detect_encoding($markup, null, true)) {
504 $markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength);
505 } else {
506 $markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength);
507 }
508 }
509
510 $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
511 }
512
513 /**
514 * Renders vertical column separator.
515 */
516 private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string
517 {
518 $borders = $this->style->getBorderChars();
519
520 return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]);
521 }
522
523 /**
524 * Renders table row.
525 *
526 * Example:
527 *
528 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
529 */
530 private function renderRow(array $row, string $cellFormat, ?string $firstCellFormat = null): void
531 {
532 $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
533 $columns = $this->getRowColumns($row);
534 $last = \count($columns) - 1;
535 foreach ($columns as $i => $column) {
536 if ($firstCellFormat && 0 === $i) {
537 $rowContent .= $this->renderCell($row, $column, $firstCellFormat);
538 } else {
539 $rowContent .= $this->renderCell($row, $column, $cellFormat);
540 }
541 $rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE);
542 }
543 $this->output->writeln($rowContent);
544 }
545
546 /**
547 * Renders table cell with padding.
548 */
549 private function renderCell(array $row, int $column, string $cellFormat): string
550 {
551 $cell = $row[$column] ?? '';
552 $width = $this->effectiveColumnWidths[$column];
553 if ($cell instanceof TableCell && $cell->getColspan() > 1) {
554 // add the width of the following columns(numbers of colspan).
555 foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
556 $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn];
557 }
558 }
559
560 // str_pad won't work properly with multi-byte strings, we need to fix the padding
561 if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
562 $width += \strlen($cell) - mb_strwidth($cell, $encoding);
563 }
564
565 $style = $this->getColumnStyle($column);
566
567 if ($cell instanceof TableSeparator) {
568 return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width));
569 }
570
571 $width += Helper::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell));
572 $content = sprintf($style->getCellRowContentFormat(), $cell);
573
574 $padType = $style->getPadType();
575 if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) {
576 $isNotStyledByTag = !preg_match('/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell);
577 if ($isNotStyledByTag) {
578 $cellFormat = $cell->getStyle()->getCellFormat();
579 if (!\is_string($cellFormat)) {
580 $tag = http_build_query($cell->getStyle()->getTagOptions(), '', ';');
581 $cellFormat = '<'.$tag.'>%s</>';
582 }
583
584 if (str_contains($content, '</>')) {
585 $content = str_replace('</>', '', $content);
586 $width -= 3;
587 }
588 if (str_contains($content, '<fg=default;bg=default>')) {
589 $content = str_replace('<fg=default;bg=default>', '', $content);
590 $width -= \strlen('<fg=default;bg=default>');
591 }
592 }
593
594 $padType = $cell->getStyle()->getPadByAlign();
595 }
596
597 return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType));
598 }
599
600 /**
601 * Calculate number of columns for this table.
602 */
603 private function calculateNumberOfColumns(array $rows): void
604 {
605 $columns = [0];
606 foreach ($rows as $row) {
607 if ($row instanceof TableSeparator) {
608 continue;
609 }
610
611 $columns[] = $this->getNumberOfColumns($row);
612 }
613
614 $this->numberOfColumns = max($columns);
615 }
616
617 private function buildTableRows(array $rows): TableRows
618 {
619 /** @var WrappableOutputFormatterInterface $formatter */
620 $formatter = $this->output->getFormatter();
621 $unmergedRows = [];
622 for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
623 $rows = $this->fillNextRows($rows, $rowKey);
624
625 // Remove any new line breaks and replace it with a new line
626 foreach ($rows[$rowKey] as $column => $cell) {
627 $colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;
628
629 if (isset($this->columnMaxWidths[$column]) && Helper::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) {
630 $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
631 }
632 if (!str_contains($cell ?? '', "\n")) {
633 continue;
634 }
635 $eol = str_contains($cell ?? '', "\r\n") ? "\r\n" : "\n";
636 $escaped = implode($eol, array_map(OutputFormatter::escapeTrailingBackslash(...), explode($eol, $cell)));
637 $cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
638 $lines = explode($eol, str_replace($eol, '<fg=default;bg=default></>'.$eol, $cell));
639 foreach ($lines as $lineKey => $line) {
640 if ($colspan > 1) {
641 $line = new TableCell($line, ['colspan' => $colspan]);
642 }
643 if (0 === $lineKey) {
644 $rows[$rowKey][$column] = $line;
645 } else {
646 if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) {
647 $unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey);
648 }
649 $unmergedRows[$rowKey][$lineKey][$column] = $line;
650 }
651 }
652 }
653 }
654
655 return new TableRows(function () use ($rows, $unmergedRows): \Traversable {
656 foreach ($rows as $rowKey => $row) {
657 $rowGroup = [$row instanceof TableSeparator ? $row : $this->fillCells($row)];
658
659 if (isset($unmergedRows[$rowKey])) {
660 foreach ($unmergedRows[$rowKey] as $row) {
661 $rowGroup[] = $row instanceof TableSeparator ? $row : $this->fillCells($row);
662 }
663 }
664 yield $rowGroup;
665 }
666 });
667 }
668
669 private function calculateRowCount(): int
670 {
671 $numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows))));
672
673 if ($this->headers) {
674 ++$numberOfRows; // Add row for header separator
675 }
676
677 if ($this->rows) {
678 ++$numberOfRows; // Add row for footer separator
679 }
680
681 return $numberOfRows;
682 }
683
684 /**
685 * fill rows that contains rowspan > 1.
686 *
687 * @throws InvalidArgumentException
688 */
689 private function fillNextRows(array $rows, int $line): array
690 {
691 $unmergedRows = [];
692 foreach ($rows[$line] as $column => $cell) {
693 if (null !== $cell && !$cell instanceof TableCell && !\is_scalar($cell) && !$cell instanceof \Stringable) {
694 throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell)));
695 }
696 if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
697 $nbLines = $cell->getRowspan() - 1;
698 $lines = [$cell];
699 if (str_contains($cell, "\n")) {
700 $eol = str_contains($cell, "\r\n") ? "\r\n" : "\n";
701 $lines = explode($eol, str_replace($eol, '<fg=default;bg=default>'.$eol.'</>', $cell));
702 $nbLines = \count($lines) > $nbLines ? substr_count($cell, $eol) : $nbLines;
703
704 $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
705 unset($lines[0]);
706 }
707
708 // create a two dimensional array (rowspan x colspan)
709 $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
710 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
711 $value = $lines[$unmergedRowKey - $line] ?? '';
712 $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
713 if ($nbLines === $unmergedRowKey - $line) {
714 break;
715 }
716 }
717 }
718 }
719
720 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
721 // we need to know if $unmergedRow will be merged or inserted into $rows
722 if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRow) <= $this->numberOfColumns)) {
723 foreach ($unmergedRow as $cellKey => $cell) {
724 // insert cell into row at cellKey position
725 array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
726 }
727 } else {
728 $row = $this->copyRow($rows, $unmergedRowKey - 1);
729 foreach ($unmergedRow as $column => $cell) {
730 if ($cell) {
731 $row[$column] = $cell;
732 }
733 }
734 array_splice($rows, $unmergedRowKey, 0, [$row]);
735 }
736 }
737
738 return $rows;
739 }
740
741 /**
742 * fill cells for a row that contains colspan > 1.
743 */
744 private function fillCells(iterable $row): iterable
745 {
746 $newRow = [];
747
748 foreach ($row as $column => $cell) {
749 $newRow[] = $cell;
750 if ($cell instanceof TableCell && $cell->getColspan() > 1) {
751 foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
752 // insert empty value at column position
753 $newRow[] = '';
754 }
755 }
756 }
757
758 return $newRow ?: $row;
759 }
760
761 private function copyRow(array $rows, int $line): array
762 {
763 $row = $rows[$line];
764 foreach ($row as $cellKey => $cellValue) {
765 $row[$cellKey] = '';
766 if ($cellValue instanceof TableCell) {
767 $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
768 }
769 }
770
771 return $row;
772 }
773
774 /**
775 * Gets number of columns by row.
776 */
777 private function getNumberOfColumns(array $row): int
778 {
779 $columns = \count($row);
780 foreach ($row as $column) {
781 $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
782 }
783
784 return $columns;
785 }
786
787 /**
788 * Gets list of columns for the given row.
789 */
790 private function getRowColumns(array $row): array
791 {
792 $columns = range(0, $this->numberOfColumns - 1);
793 foreach ($row as $cellKey => $cell) {
794 if ($cell instanceof TableCell && $cell->getColspan() > 1) {
795 // exclude grouped columns.
796 $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
797 }
798 }
799
800 return $columns;
801 }
802
803 /**
804 * Calculates columns widths.
805 */
806 private function calculateColumnsWidth(iterable $groups): void
807 {
808 for ($column = 0; $column < $this->numberOfColumns; ++$column) {
809 $lengths = [];
810 foreach ($groups as $group) {
811 foreach ($group as $row) {
812 if ($row instanceof TableSeparator) {
813 continue;
814 }
815
816 foreach ($row as $i => $cell) {
817 if ($cell instanceof TableCell) {
818 $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
819 $textLength = Helper::width($textContent);
820 if ($textLength > 0) {
821 $contentColumns = mb_str_split($textContent, ceil($textLength / $cell->getColspan()));
822 foreach ($contentColumns as $position => $content) {
823 $row[$i + $position] = $content;
824 }
825 }
826 }
827 }
828
829 $lengths[] = $this->getCellWidth($row, $column);
830 }
831 }
832
833 $this->effectiveColumnWidths[$column] = max($lengths) + Helper::width($this->style->getCellRowContentFormat()) - 2;
834 }
835 }
836
837 private function getColumnSeparatorWidth(): int
838 {
839 return Helper::width(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3]));
840 }
841
842 private function getCellWidth(array $row, int $column): int
843 {
844 $cellWidth = 0;
845
846 if (isset($row[$column])) {
847 $cell = $row[$column];
848 $cellWidth = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $cell));
849 }
850
851 $columnWidth = $this->columnWidths[$column] ?? 0;
852 $cellWidth = max($cellWidth, $columnWidth);
853
854 return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
855 }
856
857 /**
858 * Called after rendering to cleanup cache data.
859 */
860 private function cleanup(): void
861 {
862 $this->effectiveColumnWidths = [];
863 unset($this->numberOfColumns);
864 }
865
866 /**
867 * @return array<string, TableStyle>
868 */
869 private static function initStyles(): array
870 {
871 $borderless = new TableStyle();
872 $borderless
873 ->setHorizontalBorderChars('=')
874 ->setVerticalBorderChars(' ')
875 ->setDefaultCrossingChar(' ')
876 ;
877
878 $compact = new TableStyle();
879 $compact
880 ->setHorizontalBorderChars('')
881 ->setVerticalBorderChars('')
882 ->setDefaultCrossingChar('')
883 ->setCellRowContentFormat('%s ')
884 ;
885
886 $styleGuide = new TableStyle();
887 $styleGuide
888 ->setHorizontalBorderChars('-')
889 ->setVerticalBorderChars(' ')
890 ->setDefaultCrossingChar(' ')
891 ->setCellHeaderFormat('%s')
892 ;
893
894 $box = (new TableStyle())
895 ->setHorizontalBorderChars('─')
896 ->setVerticalBorderChars('│')
897 ->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├')
898 ;
899
900 $boxDouble = (new TableStyle())
901 ->setHorizontalBorderChars('═', '─')
902 ->setVerticalBorderChars('║', '│')
903 ->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣')
904 ;
905
906 return [
907 'default' => new TableStyle(),
908 'borderless' => $borderless,
909 'compact' => $compact,
910 'symfony-style-guide' => $styleGuide,
911 'box' => $box,
912 'box-double' => $boxDouble,
913 ];
914 }
915
916 private function resolveStyle(TableStyle|string $name): TableStyle
917 {
918 if ($name instanceof TableStyle) {
919 return $name;
920 }
921
922 return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
923 }
924}
diff --git a/vendor/symfony/console/Helper/TableCell.php b/vendor/symfony/console/Helper/TableCell.php
new file mode 100644
index 0000000..1c4eeea
--- /dev/null
+++ b/vendor/symfony/console/Helper/TableCell.php
@@ -0,0 +1,71 @@
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;
15
16/**
17 * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
18 */
19class TableCell
20{
21 private array $options = [
22 'rowspan' => 1,
23 'colspan' => 1,
24 'style' => null,
25 ];
26
27 public function __construct(
28 private string $value = '',
29 array $options = [],
30 ) {
31 // check option names
32 if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
33 throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff)));
34 }
35
36 if (isset($options['style']) && !$options['style'] instanceof TableCellStyle) {
37 throw new InvalidArgumentException('The style option must be an instance of "TableCellStyle".');
38 }
39
40 $this->options = array_merge($this->options, $options);
41 }
42
43 /**
44 * Returns the cell value.
45 */
46 public function __toString(): string
47 {
48 return $this->value;
49 }
50
51 /**
52 * Gets number of colspan.
53 */
54 public function getColspan(): int
55 {
56 return (int) $this->options['colspan'];
57 }
58
59 /**
60 * Gets number of rowspan.
61 */
62 public function getRowspan(): int
63 {
64 return (int) $this->options['rowspan'];
65 }
66
67 public function getStyle(): ?TableCellStyle
68 {
69 return $this->options['style'];
70 }
71}
diff --git a/vendor/symfony/console/Helper/TableCellStyle.php b/vendor/symfony/console/Helper/TableCellStyle.php
new file mode 100644
index 0000000..49b97f8
--- /dev/null
+++ b/vendor/symfony/console/Helper/TableCellStyle.php
@@ -0,0 +1,84 @@
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;
15
16/**
17 * @author Yewhen Khoptynskyi <khoptynskyi@gmail.com>
18 */
19class TableCellStyle
20{
21 public const DEFAULT_ALIGN = 'left';
22
23 private const TAG_OPTIONS = [
24 'fg',
25 'bg',
26 'options',
27 ];
28
29 private const ALIGN_MAP = [
30 'left' => \STR_PAD_RIGHT,
31 'center' => \STR_PAD_BOTH,
32 'right' => \STR_PAD_LEFT,
33 ];
34
35 private array $options = [
36 'fg' => 'default',
37 'bg' => 'default',
38 'options' => null,
39 'align' => self::DEFAULT_ALIGN,
40 'cellFormat' => null,
41 ];
42
43 public function __construct(array $options = [])
44 {
45 if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
46 throw new InvalidArgumentException(sprintf('The TableCellStyle does not support the following options: \'%s\'.', implode('\', \'', $diff)));
47 }
48
49 if (isset($options['align']) && !\array_key_exists($options['align'], self::ALIGN_MAP)) {
50 throw new InvalidArgumentException(sprintf('Wrong align value. Value must be following: \'%s\'.', implode('\', \'', array_keys(self::ALIGN_MAP))));
51 }
52
53 $this->options = array_merge($this->options, $options);
54 }
55
56 public function getOptions(): array
57 {
58 return $this->options;
59 }
60
61 /**
62 * Gets options we need for tag for example fg, bg.
63 *
64 * @return string[]
65 */
66 public function getTagOptions(): array
67 {
68 return array_filter(
69 $this->getOptions(),
70 fn ($key) => \in_array($key, self::TAG_OPTIONS, true) && isset($this->options[$key]),
71 \ARRAY_FILTER_USE_KEY
72 );
73 }
74
75 public function getPadByAlign(): int
76 {
77 return self::ALIGN_MAP[$this->getOptions()['align']];
78 }
79
80 public function getCellFormat(): ?string
81 {
82 return $this->getOptions()['cellFormat'];
83 }
84}
diff --git a/vendor/symfony/console/Helper/TableRows.php b/vendor/symfony/console/Helper/TableRows.php
new file mode 100644
index 0000000..fb2dc27
--- /dev/null
+++ b/vendor/symfony/console/Helper/TableRows.php
@@ -0,0 +1,28 @@
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
14/**
15 * @internal
16 */
17class TableRows implements \IteratorAggregate
18{
19 public function __construct(
20 private \Closure $generator,
21 ) {
22 }
23
24 public function getIterator(): \Traversable
25 {
26 return ($this->generator)();
27 }
28}
diff --git a/vendor/symfony/console/Helper/TableSeparator.php b/vendor/symfony/console/Helper/TableSeparator.php
new file mode 100644
index 0000000..e541c53
--- /dev/null
+++ b/vendor/symfony/console/Helper/TableSeparator.php
@@ -0,0 +1,25 @@
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
14/**
15 * Marks a row as being a separator.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 */
19class TableSeparator extends TableCell
20{
21 public function __construct(array $options = [])
22 {
23 parent::__construct('', $options);
24 }
25}
diff --git a/vendor/symfony/console/Helper/TableStyle.php b/vendor/symfony/console/Helper/TableStyle.php
new file mode 100644
index 0000000..be956c1
--- /dev/null
+++ b/vendor/symfony/console/Helper/TableStyle.php
@@ -0,0 +1,362 @@
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;
16
17/**
18 * Defines the styles for a Table.
19 *
20 * @author Fabien Potencier <fabien@symfony.com>
21 * @author Саша Стаменковић <umpirsky@gmail.com>
22 * @author Dany Maillard <danymaillard93b@gmail.com>
23 */
24class TableStyle
25{
26 private string $paddingChar = ' ';
27 private string $horizontalOutsideBorderChar = '-';
28 private string $horizontalInsideBorderChar = '-';
29 private string $verticalOutsideBorderChar = '|';
30 private string $verticalInsideBorderChar = '|';
31 private string $crossingChar = '+';
32 private string $crossingTopRightChar = '+';
33 private string $crossingTopMidChar = '+';
34 private string $crossingTopLeftChar = '+';
35 private string $crossingMidRightChar = '+';
36 private string $crossingBottomRightChar = '+';
37 private string $crossingBottomMidChar = '+';
38 private string $crossingBottomLeftChar = '+';
39 private string $crossingMidLeftChar = '+';
40 private string $crossingTopLeftBottomChar = '+';
41 private string $crossingTopMidBottomChar = '+';
42 private string $crossingTopRightBottomChar = '+';
43 private string $headerTitleFormat = '<fg=black;bg=white;options=bold> %s </>';
44 private string $footerTitleFormat = '<fg=black;bg=white;options=bold> %s </>';
45 private string $cellHeaderFormat = '<info>%s</info>';
46 private string $cellRowFormat = '%s';
47 private string $cellRowContentFormat = ' %s ';
48 private string $borderFormat = '%s';
49 private int $padType = \STR_PAD_RIGHT;
50
51 /**
52 * Sets padding character, used for cell padding.
53 *
54 * @return $this
55 */
56 public function setPaddingChar(string $paddingChar): static
57 {
58 if (!$paddingChar) {
59 throw new LogicException('The padding char must not be empty.');
60 }
61
62 $this->paddingChar = $paddingChar;
63
64 return $this;
65 }
66
67 /**
68 * Gets padding character, used for cell padding.
69 */
70 public function getPaddingChar(): string
71 {
72 return $this->paddingChar;
73 }
74
75 /**
76 * Sets horizontal border characters.
77 *
78 * <code>
79 * ╔═══════════════╤══════════════════════════╤══════════════════╗
80 * 1 ISBN 2 Title │ Author ║
81 * ╠═══════════════╪══════════════════════════╪══════════════════╣
82 * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║
83 * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║
84 * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║
85 * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
86 * ╚═══════════════╧══════════════════════════╧══════════════════╝
87 * </code>
88 *
89 * @return $this
90 */
91 public function setHorizontalBorderChars(string $outside, ?string $inside = null): static
92 {
93 $this->horizontalOutsideBorderChar = $outside;
94 $this->horizontalInsideBorderChar = $inside ?? $outside;
95
96 return $this;
97 }
98
99 /**
100 * Sets vertical border characters.
101 *
102 * <code>
103 * ╔═══════════════╤══════════════════════════╤══════════════════╗
104 * ║ ISBN │ Title │ Author ║
105 * ╠═══════1═══════╪══════════════════════════╪══════════════════╣
106 * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║
107 * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║
108 * ╟───────2───────┼──────────────────────────┼──────────────────╢
109 * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║
110 * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
111 * ╚═══════════════╧══════════════════════════╧══════════════════╝
112 * </code>
113 *
114 * @return $this
115 */
116 public function setVerticalBorderChars(string $outside, ?string $inside = null): static
117 {
118 $this->verticalOutsideBorderChar = $outside;
119 $this->verticalInsideBorderChar = $inside ?? $outside;
120
121 return $this;
122 }
123
124 /**
125 * Gets border characters.
126 *
127 * @internal
128 */
129 public function getBorderChars(): array
130 {
131 return [
132 $this->horizontalOutsideBorderChar,
133 $this->verticalOutsideBorderChar,
134 $this->horizontalInsideBorderChar,
135 $this->verticalInsideBorderChar,
136 ];
137 }
138
139 /**
140 * Sets crossing characters.
141 *
142 * Example:
143 * <code>
144 * 1═══════════════2══════════════════════════2══════════════════3
145 * ║ ISBN │ Title │ Author ║
146 * 8'══════════════0'═════════════════════════0'═════════════════4'
147 * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║
148 * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║
149 * 8───────────────0──────────────────────────0──────────────────4
150 * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║
151 * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
152 * 7═══════════════6══════════════════════════6══════════════════5
153 * </code>
154 *
155 * @param string $cross Crossing char (see #0 of example)
156 * @param string $topLeft Top left char (see #1 of example)
157 * @param string $topMid Top mid char (see #2 of example)
158 * @param string $topRight Top right char (see #3 of example)
159 * @param string $midRight Mid right char (see #4 of example)
160 * @param string $bottomRight Bottom right char (see #5 of example)
161 * @param string $bottomMid Bottom mid char (see #6 of example)
162 * @param string $bottomLeft Bottom left char (see #7 of example)
163 * @param string $midLeft Mid left char (see #8 of example)
164 * @param string|null $topLeftBottom Top left bottom char (see #8' of example), equals to $midLeft if null
165 * @param string|null $topMidBottom Top mid bottom char (see #0' of example), equals to $cross if null
166 * @param string|null $topRightBottom Top right bottom char (see #4' of example), equals to $midRight if null
167 *
168 * @return $this
169 */
170 public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, ?string $topLeftBottom = null, ?string $topMidBottom = null, ?string $topRightBottom = null): static
171 {
172 $this->crossingChar = $cross;
173 $this->crossingTopLeftChar = $topLeft;
174 $this->crossingTopMidChar = $topMid;
175 $this->crossingTopRightChar = $topRight;
176 $this->crossingMidRightChar = $midRight;
177 $this->crossingBottomRightChar = $bottomRight;
178 $this->crossingBottomMidChar = $bottomMid;
179 $this->crossingBottomLeftChar = $bottomLeft;
180 $this->crossingMidLeftChar = $midLeft;
181 $this->crossingTopLeftBottomChar = $topLeftBottom ?? $midLeft;
182 $this->crossingTopMidBottomChar = $topMidBottom ?? $cross;
183 $this->crossingTopRightBottomChar = $topRightBottom ?? $midRight;
184
185 return $this;
186 }
187
188 /**
189 * Sets default crossing character used for each cross.
190 *
191 * @see {@link setCrossingChars()} for setting each crossing individually.
192 */
193 public function setDefaultCrossingChar(string $char): self
194 {
195 return $this->setCrossingChars($char, $char, $char, $char, $char, $char, $char, $char, $char);
196 }
197
198 /**
199 * Gets crossing character.
200 */
201 public function getCrossingChar(): string
202 {
203 return $this->crossingChar;
204 }
205
206 /**
207 * Gets crossing characters.
208 *
209 * @internal
210 */
211 public function getCrossingChars(): array
212 {
213 return [
214 $this->crossingChar,
215 $this->crossingTopLeftChar,
216 $this->crossingTopMidChar,
217 $this->crossingTopRightChar,
218 $this->crossingMidRightChar,
219 $this->crossingBottomRightChar,
220 $this->crossingBottomMidChar,
221 $this->crossingBottomLeftChar,
222 $this->crossingMidLeftChar,
223 $this->crossingTopLeftBottomChar,
224 $this->crossingTopMidBottomChar,
225 $this->crossingTopRightBottomChar,
226 ];
227 }
228
229 /**
230 * Sets header cell format.
231 *
232 * @return $this
233 */
234 public function setCellHeaderFormat(string $cellHeaderFormat): static
235 {
236 $this->cellHeaderFormat = $cellHeaderFormat;
237
238 return $this;
239 }
240
241 /**
242 * Gets header cell format.
243 */
244 public function getCellHeaderFormat(): string
245 {
246 return $this->cellHeaderFormat;
247 }
248
249 /**
250 * Sets row cell format.
251 *
252 * @return $this
253 */
254 public function setCellRowFormat(string $cellRowFormat): static
255 {
256 $this->cellRowFormat = $cellRowFormat;
257
258 return $this;
259 }
260
261 /**
262 * Gets row cell format.
263 */
264 public function getCellRowFormat(): string
265 {
266 return $this->cellRowFormat;
267 }
268
269 /**
270 * Sets row cell content format.
271 *
272 * @return $this
273 */
274 public function setCellRowContentFormat(string $cellRowContentFormat): static
275 {
276 $this->cellRowContentFormat = $cellRowContentFormat;
277
278 return $this;
279 }
280
281 /**
282 * Gets row cell content format.
283 */
284 public function getCellRowContentFormat(): string
285 {
286 return $this->cellRowContentFormat;
287 }
288
289 /**
290 * Sets table border format.
291 *
292 * @return $this
293 */
294 public function setBorderFormat(string $borderFormat): static
295 {
296 $this->borderFormat = $borderFormat;
297
298 return $this;
299 }
300
301 /**
302 * Gets table border format.
303 */
304 public function getBorderFormat(): string
305 {
306 return $this->borderFormat;
307 }
308
309 /**
310 * Sets cell padding type.
311 *
312 * @return $this
313 */
314 public function setPadType(int $padType): static
315 {
316 if (!\in_array($padType, [\STR_PAD_LEFT, \STR_PAD_RIGHT, \STR_PAD_BOTH], true)) {
317 throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).');
318 }
319
320 $this->padType = $padType;
321
322 return $this;
323 }
324
325 /**
326 * Gets cell padding type.
327 */
328 public function getPadType(): int
329 {
330 return $this->padType;
331 }
332
333 public function getHeaderTitleFormat(): string
334 {
335 return $this->headerTitleFormat;
336 }
337
338 /**
339 * @return $this
340 */
341 public function setHeaderTitleFormat(string $format): static
342 {
343 $this->headerTitleFormat = $format;
344
345 return $this;
346 }
347
348 public function getFooterTitleFormat(): string
349 {
350 return $this->footerTitleFormat;
351 }
352
353 /**
354 * @return $this
355 */
356 public function setFooterTitleFormat(string $format): static
357 {
358 $this->footerTitleFormat = $format;
359
360 return $this;
361 }
362}
diff --git a/vendor/symfony/console/Input/ArgvInput.php b/vendor/symfony/console/Input/ArgvInput.php
new file mode 100644
index 0000000..95703ba
--- /dev/null
+++ b/vendor/symfony/console/Input/ArgvInput.php
@@ -0,0 +1,396 @@
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\Input;
13
14use Symfony\Component\Console\Exception\RuntimeException;
15
16/**
17 * ArgvInput represents an input coming from the CLI arguments.
18 *
19 * Usage:
20 *
21 * $input = new ArgvInput();
22 *
23 * By default, the `$_SERVER['argv']` array is used for the input values.
24 *
25 * This can be overridden by explicitly passing the input values in the constructor:
26 *
27 * $input = new ArgvInput($_SERVER['argv']);
28 *
29 * If you pass it yourself, don't forget that the first element of the array
30 * is the name of the running application.
31 *
32 * When passing an argument to the constructor, be sure that it respects
33 * the same rules as the argv one. It's almost always better to use the
34 * `StringInput` when you want to provide your own input.
35 *
36 * @author Fabien Potencier <fabien@symfony.com>
37 *
38 * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
39 * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
40 */
41class ArgvInput extends Input
42{
43 /** @var list<string> */
44 private array $tokens;
45 private array $parsed;
46
47 /** @param list<string>|null $argv */
48 public function __construct(?array $argv = null, ?InputDefinition $definition = null)
49 {
50 $argv ??= $_SERVER['argv'] ?? [];
51
52 // strip the application name
53 array_shift($argv);
54
55 $this->tokens = $argv;
56
57 parent::__construct($definition);
58 }
59
60 /** @param list<string> $tokens */
61 protected function setTokens(array $tokens): void
62 {
63 $this->tokens = $tokens;
64 }
65
66 protected function parse(): void
67 {
68 $parseOptions = true;
69 $this->parsed = $this->tokens;
70 while (null !== $token = array_shift($this->parsed)) {
71 $parseOptions = $this->parseToken($token, $parseOptions);
72 }
73 }
74
75 protected function parseToken(string $token, bool $parseOptions): bool
76 {
77 if ($parseOptions && '' == $token) {
78 $this->parseArgument($token);
79 } elseif ($parseOptions && '--' == $token) {
80 return false;
81 } elseif ($parseOptions && str_starts_with($token, '--')) {
82 $this->parseLongOption($token);
83 } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
84 $this->parseShortOption($token);
85 } else {
86 $this->parseArgument($token);
87 }
88
89 return $parseOptions;
90 }
91
92 /**
93 * Parses a short option.
94 */
95 private function parseShortOption(string $token): void
96 {
97 $name = substr($token, 1);
98
99 if (\strlen($name) > 1) {
100 if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
101 // an option with a value (with no space)
102 $this->addShortOption($name[0], substr($name, 1));
103 } else {
104 $this->parseShortOptionSet($name);
105 }
106 } else {
107 $this->addShortOption($name, null);
108 }
109 }
110
111 /**
112 * Parses a short option set.
113 *
114 * @throws RuntimeException When option given doesn't exist
115 */
116 private function parseShortOptionSet(string $name): void
117 {
118 $len = \strlen($name);
119 for ($i = 0; $i < $len; ++$i) {
120 if (!$this->definition->hasShortcut($name[$i])) {
121 $encoding = mb_detect_encoding($name, null, true);
122 throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding)));
123 }
124
125 $option = $this->definition->getOptionForShortcut($name[$i]);
126 if ($option->acceptValue()) {
127 $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
128
129 break;
130 } else {
131 $this->addLongOption($option->getName(), null);
132 }
133 }
134 }
135
136 /**
137 * Parses a long option.
138 */
139 private function parseLongOption(string $token): void
140 {
141 $name = substr($token, 2);
142
143 if (false !== $pos = strpos($name, '=')) {
144 if ('' === $value = substr($name, $pos + 1)) {
145 array_unshift($this->parsed, $value);
146 }
147 $this->addLongOption(substr($name, 0, $pos), $value);
148 } else {
149 $this->addLongOption($name, null);
150 }
151 }
152
153 /**
154 * Parses an argument.
155 *
156 * @throws RuntimeException When too many arguments are given
157 */
158 private function parseArgument(string $token): void
159 {
160 $c = \count($this->arguments);
161
162 // if input is expecting another argument, add it
163 if ($this->definition->hasArgument($c)) {
164 $arg = $this->definition->getArgument($c);
165 $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
166
167 // if last argument isArray(), append token to last argument
168 } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
169 $arg = $this->definition->getArgument($c - 1);
170 $this->arguments[$arg->getName()][] = $token;
171
172 // unexpected argument
173 } else {
174 $all = $this->definition->getArguments();
175 $symfonyCommandName = null;
176 if (($inputArgument = $all[$key = array_key_first($all)] ?? null) && 'command' === $inputArgument->getName()) {
177 $symfonyCommandName = $this->arguments['command'] ?? null;
178 unset($all[$key]);
179 }
180
181 if (\count($all)) {
182 if ($symfonyCommandName) {
183 $message = sprintf('Too many arguments to "%s" command, expected arguments "%s".', $symfonyCommandName, implode('" "', array_keys($all)));
184 } else {
185 $message = sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all)));
186 }
187 } elseif ($symfonyCommandName) {
188 $message = sprintf('No arguments expected for "%s" command, got "%s".', $symfonyCommandName, $token);
189 } else {
190 $message = sprintf('No arguments expected, got "%s".', $token);
191 }
192
193 throw new RuntimeException($message);
194 }
195 }
196
197 /**
198 * Adds a short option value.
199 *
200 * @throws RuntimeException When option given doesn't exist
201 */
202 private function addShortOption(string $shortcut, mixed $value): void
203 {
204 if (!$this->definition->hasShortcut($shortcut)) {
205 throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
206 }
207
208 $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
209 }
210
211 /**
212 * Adds a long option value.
213 *
214 * @throws RuntimeException When option given doesn't exist
215 */
216 private function addLongOption(string $name, mixed $value): void
217 {
218 if (!$this->definition->hasOption($name)) {
219 if (!$this->definition->hasNegation($name)) {
220 throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
221 }
222
223 $optionName = $this->definition->negationToName($name);
224 if (null !== $value) {
225 throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
226 }
227 $this->options[$optionName] = false;
228
229 return;
230 }
231
232 $option = $this->definition->getOption($name);
233
234 if (null !== $value && !$option->acceptValue()) {
235 throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
236 }
237
238 if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) {
239 // if option accepts an optional or mandatory argument
240 // let's see if there is one provided
241 $next = array_shift($this->parsed);
242 if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', null], true)) {
243 $value = $next;
244 } else {
245 array_unshift($this->parsed, $next);
246 }
247 }
248
249 if (null === $value) {
250 if ($option->isValueRequired()) {
251 throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
252 }
253
254 if (!$option->isArray() && !$option->isValueOptional()) {
255 $value = true;
256 }
257 }
258
259 if ($option->isArray()) {
260 $this->options[$name][] = $value;
261 } else {
262 $this->options[$name] = $value;
263 }
264 }
265
266 public function getFirstArgument(): ?string
267 {
268 $isOption = false;
269 foreach ($this->tokens as $i => $token) {
270 if ($token && '-' === $token[0]) {
271 if (str_contains($token, '=') || !isset($this->tokens[$i + 1])) {
272 continue;
273 }
274
275 // If it's a long option, consider that everything after "--" is the option name.
276 // Otherwise, use the last char (if it's a short option set, only the last one can take a value with space separator)
277 $name = '-' === $token[1] ? substr($token, 2) : substr($token, -1);
278 if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) {
279 // noop
280 } elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) {
281 $isOption = true;
282 }
283
284 continue;
285 }
286
287 if ($isOption) {
288 $isOption = false;
289 continue;
290 }
291
292 return $token;
293 }
294
295 return null;
296 }
297
298 public function hasParameterOption(string|array $values, bool $onlyParams = false): bool
299 {
300 $values = (array) $values;
301
302 foreach ($this->tokens as $token) {
303 if ($onlyParams && '--' === $token) {
304 return false;
305 }
306 foreach ($values as $value) {
307 // Options with values:
308 // For long options, test for '--option=' at beginning
309 // For short options, test for '-o' at beginning
310 $leading = str_starts_with($value, '--') ? $value.'=' : $value;
311 if ($token === $value || '' !== $leading && str_starts_with($token, $leading)) {
312 return true;
313 }
314 }
315 }
316
317 return false;
318 }
319
320 public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed
321 {
322 $values = (array) $values;
323 $tokens = $this->tokens;
324
325 while (0 < \count($tokens)) {
326 $token = array_shift($tokens);
327 if ($onlyParams && '--' === $token) {
328 return $default;
329 }
330
331 foreach ($values as $value) {
332 if ($token === $value) {
333 return array_shift($tokens);
334 }
335 // Options with values:
336 // For long options, test for '--option=' at beginning
337 // For short options, test for '-o' at beginning
338 $leading = str_starts_with($value, '--') ? $value.'=' : $value;
339 if ('' !== $leading && str_starts_with($token, $leading)) {
340 return substr($token, \strlen($leading));
341 }
342 }
343 }
344
345 return $default;
346 }
347
348 /**
349 * Returns un-parsed and not validated tokens.
350 *
351 * @param bool $strip Whether to return the raw parameters (false) or the values after the command name (true)
352 *
353 * @return list<string>
354 */
355 public function getRawTokens(bool $strip = false): array
356 {
357 if (!$strip) {
358 return $this->tokens;
359 }
360
361 $parameters = [];
362 $keep = false;
363 foreach ($this->tokens as $value) {
364 if (!$keep && $value === $this->getFirstArgument()) {
365 $keep = true;
366
367 continue;
368 }
369 if ($keep) {
370 $parameters[] = $value;
371 }
372 }
373
374 return $parameters;
375 }
376
377 /**
378 * Returns a stringified representation of the args passed to the command.
379 */
380 public function __toString(): string
381 {
382 $tokens = array_map(function ($token) {
383 if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
384 return $match[1].$this->escapeToken($match[2]);
385 }
386
387 if ($token && '-' !== $token[0]) {
388 return $this->escapeToken($token);
389 }
390
391 return $token;
392 }, $this->tokens);
393
394 return implode(' ', $tokens);
395 }
396}
diff --git a/vendor/symfony/console/Input/ArrayInput.php b/vendor/symfony/console/Input/ArrayInput.php
new file mode 100644
index 0000000..d27ff41
--- /dev/null
+++ b/vendor/symfony/console/Input/ArrayInput.php
@@ -0,0 +1,191 @@
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\Input;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Component\Console\Exception\InvalidOptionException;
16
17/**
18 * ArrayInput represents an input provided as an array.
19 *
20 * Usage:
21 *
22 * $input = new ArrayInput(['command' => 'foo:bar', 'foo' => 'bar', '--bar' => 'foobar']);
23 *
24 * @author Fabien Potencier <fabien@symfony.com>
25 */
26class ArrayInput extends Input
27{
28 public function __construct(
29 private array $parameters,
30 ?InputDefinition $definition = null,
31 ) {
32 parent::__construct($definition);
33 }
34
35 public function getFirstArgument(): ?string
36 {
37 foreach ($this->parameters as $param => $value) {
38 if ($param && \is_string($param) && '-' === $param[0]) {
39 continue;
40 }
41
42 return $value;
43 }
44
45 return null;
46 }
47
48 public function hasParameterOption(string|array $values, bool $onlyParams = false): bool
49 {
50 $values = (array) $values;
51
52 foreach ($this->parameters as $k => $v) {
53 if (!\is_int($k)) {
54 $v = $k;
55 }
56
57 if ($onlyParams && '--' === $v) {
58 return false;
59 }
60
61 if (\in_array($v, $values)) {
62 return true;
63 }
64 }
65
66 return false;
67 }
68
69 public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed
70 {
71 $values = (array) $values;
72
73 foreach ($this->parameters as $k => $v) {
74 if ($onlyParams && ('--' === $k || (\is_int($k) && '--' === $v))) {
75 return $default;
76 }
77
78 if (\is_int($k)) {
79 if (\in_array($v, $values)) {
80 return true;
81 }
82 } elseif (\in_array($k, $values)) {
83 return $v;
84 }
85 }
86
87 return $default;
88 }
89
90 /**
91 * Returns a stringified representation of the args passed to the command.
92 */
93 public function __toString(): string
94 {
95 $params = [];
96 foreach ($this->parameters as $param => $val) {
97 if ($param && \is_string($param) && '-' === $param[0]) {
98 $glue = ('-' === $param[1]) ? '=' : ' ';
99 if (\is_array($val)) {
100 foreach ($val as $v) {
101 $params[] = $param.('' != $v ? $glue.$this->escapeToken($v) : '');
102 }
103 } else {
104 $params[] = $param.('' != $val ? $glue.$this->escapeToken($val) : '');
105 }
106 } else {
107 $params[] = \is_array($val) ? implode(' ', array_map($this->escapeToken(...), $val)) : $this->escapeToken($val);
108 }
109 }
110
111 return implode(' ', $params);
112 }
113
114 protected function parse(): void
115 {
116 foreach ($this->parameters as $key => $value) {
117 if ('--' === $key) {
118 return;
119 }
120 if (str_starts_with($key, '--')) {
121 $this->addLongOption(substr($key, 2), $value);
122 } elseif (str_starts_with($key, '-')) {
123 $this->addShortOption(substr($key, 1), $value);
124 } else {
125 $this->addArgument($key, $value);
126 }
127 }
128 }
129
130 /**
131 * Adds a short option value.
132 *
133 * @throws InvalidOptionException When option given doesn't exist
134 */
135 private function addShortOption(string $shortcut, mixed $value): void
136 {
137 if (!$this->definition->hasShortcut($shortcut)) {
138 throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut));
139 }
140
141 $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
142 }
143
144 /**
145 * Adds a long option value.
146 *
147 * @throws InvalidOptionException When option given doesn't exist
148 * @throws InvalidOptionException When a required value is missing
149 */
150 private function addLongOption(string $name, mixed $value): void
151 {
152 if (!$this->definition->hasOption($name)) {
153 if (!$this->definition->hasNegation($name)) {
154 throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name));
155 }
156
157 $optionName = $this->definition->negationToName($name);
158 $this->options[$optionName] = false;
159
160 return;
161 }
162
163 $option = $this->definition->getOption($name);
164
165 if (null === $value) {
166 if ($option->isValueRequired()) {
167 throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name));
168 }
169
170 if (!$option->isValueOptional()) {
171 $value = true;
172 }
173 }
174
175 $this->options[$name] = $value;
176 }
177
178 /**
179 * Adds an argument value.
180 *
181 * @throws InvalidArgumentException When argument given doesn't exist
182 */
183 private function addArgument(string|int $name, mixed $value): void
184 {
185 if (!$this->definition->hasArgument($name)) {
186 throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
187 }
188
189 $this->arguments[$name] = $value;
190 }
191}
diff --git a/vendor/symfony/console/Input/Input.php b/vendor/symfony/console/Input/Input.php
new file mode 100644
index 0000000..5a8b9a2
--- /dev/null
+++ b/vendor/symfony/console/Input/Input.php
@@ -0,0 +1,174 @@
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\Input;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Component\Console\Exception\RuntimeException;
16
17/**
18 * Input is the base class for all concrete Input classes.
19 *
20 * Three concrete classes are provided by default:
21 *
22 * * `ArgvInput`: The input comes from the CLI arguments (argv)
23 * * `StringInput`: The input is provided as a string
24 * * `ArrayInput`: The input is provided as an array
25 *
26 * @author Fabien Potencier <fabien@symfony.com>
27 */
28abstract class Input implements InputInterface, StreamableInputInterface
29{
30 protected InputDefinition $definition;
31 /** @var resource */
32 protected $stream;
33 protected array $options = [];
34 protected array $arguments = [];
35 protected bool $interactive = true;
36
37 public function __construct(?InputDefinition $definition = null)
38 {
39 if (null === $definition) {
40 $this->definition = new InputDefinition();
41 } else {
42 $this->bind($definition);
43 $this->validate();
44 }
45 }
46
47 public function bind(InputDefinition $definition): void
48 {
49 $this->arguments = [];
50 $this->options = [];
51 $this->definition = $definition;
52
53 $this->parse();
54 }
55
56 /**
57 * Processes command line arguments.
58 */
59 abstract protected function parse(): void;
60
61 public function validate(): void
62 {
63 $definition = $this->definition;
64 $givenArguments = $this->arguments;
65
66 $missingArguments = array_filter(array_keys($definition->getArguments()), fn ($argument) => !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired());
67
68 if (\count($missingArguments) > 0) {
69 throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));
70 }
71 }
72
73 public function isInteractive(): bool
74 {
75 return $this->interactive;
76 }
77
78 public function setInteractive(bool $interactive): void
79 {
80 $this->interactive = $interactive;
81 }
82
83 public function getArguments(): array
84 {
85 return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
86 }
87
88 public function getArgument(string $name): mixed
89 {
90 if (!$this->definition->hasArgument($name)) {
91 throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
92 }
93
94 return $this->arguments[$name] ?? $this->definition->getArgument($name)->getDefault();
95 }
96
97 public function setArgument(string $name, mixed $value): void
98 {
99 if (!$this->definition->hasArgument($name)) {
100 throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
101 }
102
103 $this->arguments[$name] = $value;
104 }
105
106 public function hasArgument(string $name): bool
107 {
108 return $this->definition->hasArgument($name);
109 }
110
111 public function getOptions(): array
112 {
113 return array_merge($this->definition->getOptionDefaults(), $this->options);
114 }
115
116 public function getOption(string $name): mixed
117 {
118 if ($this->definition->hasNegation($name)) {
119 if (null === $value = $this->getOption($this->definition->negationToName($name))) {
120 return $value;
121 }
122
123 return !$value;
124 }
125
126 if (!$this->definition->hasOption($name)) {
127 throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
128 }
129
130 return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
131 }
132
133 public function setOption(string $name, mixed $value): void
134 {
135 if ($this->definition->hasNegation($name)) {
136 $this->options[$this->definition->negationToName($name)] = !$value;
137
138 return;
139 } elseif (!$this->definition->hasOption($name)) {
140 throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
141 }
142
143 $this->options[$name] = $value;
144 }
145
146 public function hasOption(string $name): bool
147 {
148 return $this->definition->hasOption($name) || $this->definition->hasNegation($name);
149 }
150
151 /**
152 * Escapes a token through escapeshellarg if it contains unsafe chars.
153 */
154 public function escapeToken(string $token): string
155 {
156 return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
157 }
158
159 /**
160 * @param resource $stream
161 */
162 public function setStream($stream): void
163 {
164 $this->stream = $stream;
165 }
166
167 /**
168 * @return resource
169 */
170 public function getStream()
171 {
172 return $this->stream;
173 }
174}
diff --git a/vendor/symfony/console/Input/InputArgument.php b/vendor/symfony/console/Input/InputArgument.php
new file mode 100644
index 0000000..a5d9492
--- /dev/null
+++ b/vendor/symfony/console/Input/InputArgument.php
@@ -0,0 +1,160 @@
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\Input;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Completion\CompletionInput;
16use Symfony\Component\Console\Completion\CompletionSuggestions;
17use Symfony\Component\Console\Completion\Suggestion;
18use Symfony\Component\Console\Exception\InvalidArgumentException;
19use Symfony\Component\Console\Exception\LogicException;
20
21/**
22 * Represents a command line argument.
23 *
24 * @author Fabien Potencier <fabien@symfony.com>
25 */
26class InputArgument
27{
28 /**
29 * Providing an argument is required (e.g. just 'app:foo' is not allowed).
30 */
31 public const REQUIRED = 1;
32
33 /**
34 * Providing an argument is optional (e.g. 'app:foo' and 'app:foo bar' are both allowed). This is the default behavior of arguments.
35 */
36 public const OPTIONAL = 2;
37
38 /**
39 * The argument accepts multiple values and turn them into an array (e.g. 'app:foo bar baz' will result in value ['bar', 'baz']).
40 */
41 public const IS_ARRAY = 4;
42
43 private int $mode;
44 private string|int|bool|array|float|null $default;
45
46 /**
47 * @param string $name The argument name
48 * @param int-mask-of<InputArgument::*>|null $mode The argument mode: a bit mask of self::REQUIRED, self::OPTIONAL and self::IS_ARRAY
49 * @param string $description A description text
50 * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only)
51 * @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
52 *
53 * @throws InvalidArgumentException When argument mode is not valid
54 */
55 public function __construct(
56 private string $name,
57 ?int $mode = null,
58 private string $description = '',
59 string|bool|int|float|array|null $default = null,
60 private \Closure|array $suggestedValues = [],
61 ) {
62 if (null === $mode) {
63 $mode = self::OPTIONAL;
64 } elseif ($mode >= (self::IS_ARRAY << 1) || $mode < 1) {
65 throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
66 }
67
68 $this->mode = $mode;
69
70 $this->setDefault($default);
71 }
72
73 /**
74 * Returns the argument name.
75 */
76 public function getName(): string
77 {
78 return $this->name;
79 }
80
81 /**
82 * Returns true if the argument is required.
83 *
84 * @return bool true if parameter mode is self::REQUIRED, false otherwise
85 */
86 public function isRequired(): bool
87 {
88 return self::REQUIRED === (self::REQUIRED & $this->mode);
89 }
90
91 /**
92 * Returns true if the argument can take multiple values.
93 *
94 * @return bool true if mode is self::IS_ARRAY, false otherwise
95 */
96 public function isArray(): bool
97 {
98 return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
99 }
100
101 /**
102 * Sets the default value.
103 */
104 public function setDefault(string|bool|int|float|array|null $default): void
105 {
106 if ($this->isRequired() && null !== $default) {
107 throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
108 }
109
110 if ($this->isArray()) {
111 if (null === $default) {
112 $default = [];
113 } elseif (!\is_array($default)) {
114 throw new LogicException('A default value for an array argument must be an array.');
115 }
116 }
117
118 $this->default = $default;
119 }
120
121 /**
122 * Returns the default value.
123 */
124 public function getDefault(): string|bool|int|float|array|null
125 {
126 return $this->default;
127 }
128
129 /**
130 * Returns true if the argument has values for input completion.
131 */
132 public function hasCompletion(): bool
133 {
134 return [] !== $this->suggestedValues;
135 }
136
137 /**
138 * Supplies suggestions when command resolves possible completion options for input.
139 *
140 * @see Command::complete()
141 */
142 public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
143 {
144 $values = $this->suggestedValues;
145 if ($values instanceof \Closure && !\is_array($values = $values($input))) {
146 throw new LogicException(sprintf('Closure for argument "%s" must return an array. Got "%s".', $this->name, get_debug_type($values)));
147 }
148 if ($values) {
149 $suggestions->suggestValues($values);
150 }
151 }
152
153 /**
154 * Returns the description text.
155 */
156 public function getDescription(): string
157 {
158 return $this->description;
159 }
160}
diff --git a/vendor/symfony/console/Input/InputAwareInterface.php b/vendor/symfony/console/Input/InputAwareInterface.php
new file mode 100644
index 0000000..ba4664c
--- /dev/null
+++ b/vendor/symfony/console/Input/InputAwareInterface.php
@@ -0,0 +1,26 @@
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\Input;
13
14/**
15 * InputAwareInterface should be implemented by classes that depends on the
16 * Console Input.
17 *
18 * @author Wouter J <waldio.webdesign@gmail.com>
19 */
20interface InputAwareInterface
21{
22 /**
23 * Sets the Console Input.
24 */
25 public function setInput(InputInterface $input): void;
26}
diff --git a/vendor/symfony/console/Input/InputDefinition.php b/vendor/symfony/console/Input/InputDefinition.php
new file mode 100644
index 0000000..f27e297
--- /dev/null
+++ b/vendor/symfony/console/Input/InputDefinition.php
@@ -0,0 +1,402 @@
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\Input;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Component\Console\Exception\LogicException;
16
17/**
18 * A InputDefinition represents a set of valid command line arguments and options.
19 *
20 * Usage:
21 *
22 * $definition = new InputDefinition([
23 * new InputArgument('name', InputArgument::REQUIRED),
24 * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED),
25 * ]);
26 *
27 * @author Fabien Potencier <fabien@symfony.com>
28 */
29class InputDefinition
30{
31 private array $arguments = [];
32 private int $requiredCount = 0;
33 private ?InputArgument $lastArrayArgument = null;
34 private ?InputArgument $lastOptionalArgument = null;
35 private array $options = [];
36 private array $negations = [];
37 private array $shortcuts = [];
38
39 /**
40 * @param array $definition An array of InputArgument and InputOption instance
41 */
42 public function __construct(array $definition = [])
43 {
44 $this->setDefinition($definition);
45 }
46
47 /**
48 * Sets the definition of the input.
49 */
50 public function setDefinition(array $definition): void
51 {
52 $arguments = [];
53 $options = [];
54 foreach ($definition as $item) {
55 if ($item instanceof InputOption) {
56 $options[] = $item;
57 } else {
58 $arguments[] = $item;
59 }
60 }
61
62 $this->setArguments($arguments);
63 $this->setOptions($options);
64 }
65
66 /**
67 * Sets the InputArgument objects.
68 *
69 * @param InputArgument[] $arguments An array of InputArgument objects
70 */
71 public function setArguments(array $arguments = []): void
72 {
73 $this->arguments = [];
74 $this->requiredCount = 0;
75 $this->lastOptionalArgument = null;
76 $this->lastArrayArgument = null;
77 $this->addArguments($arguments);
78 }
79
80 /**
81 * Adds an array of InputArgument objects.
82 *
83 * @param InputArgument[] $arguments An array of InputArgument objects
84 */
85 public function addArguments(?array $arguments = []): void
86 {
87 if (null !== $arguments) {
88 foreach ($arguments as $argument) {
89 $this->addArgument($argument);
90 }
91 }
92 }
93
94 /**
95 * @throws LogicException When incorrect argument is given
96 */
97 public function addArgument(InputArgument $argument): void
98 {
99 if (isset($this->arguments[$argument->getName()])) {
100 throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
101 }
102
103 if (null !== $this->lastArrayArgument) {
104 throw new LogicException(sprintf('Cannot add a required argument "%s" after an array argument "%s".', $argument->getName(), $this->lastArrayArgument->getName()));
105 }
106
107 if ($argument->isRequired() && null !== $this->lastOptionalArgument) {
108 throw new LogicException(sprintf('Cannot add a required argument "%s" after an optional one "%s".', $argument->getName(), $this->lastOptionalArgument->getName()));
109 }
110
111 if ($argument->isArray()) {
112 $this->lastArrayArgument = $argument;
113 }
114
115 if ($argument->isRequired()) {
116 ++$this->requiredCount;
117 } else {
118 $this->lastOptionalArgument = $argument;
119 }
120
121 $this->arguments[$argument->getName()] = $argument;
122 }
123
124 /**
125 * Returns an InputArgument by name or by position.
126 *
127 * @throws InvalidArgumentException When argument given doesn't exist
128 */
129 public function getArgument(string|int $name): InputArgument
130 {
131 if (!$this->hasArgument($name)) {
132 throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
133 }
134
135 $arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;
136
137 return $arguments[$name];
138 }
139
140 /**
141 * Returns true if an InputArgument object exists by name or position.
142 */
143 public function hasArgument(string|int $name): bool
144 {
145 $arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;
146
147 return isset($arguments[$name]);
148 }
149
150 /**
151 * Gets the array of InputArgument objects.
152 *
153 * @return InputArgument[]
154 */
155 public function getArguments(): array
156 {
157 return $this->arguments;
158 }
159
160 /**
161 * Returns the number of InputArguments.
162 */
163 public function getArgumentCount(): int
164 {
165 return null !== $this->lastArrayArgument ? \PHP_INT_MAX : \count($this->arguments);
166 }
167
168 /**
169 * Returns the number of required InputArguments.
170 */
171 public function getArgumentRequiredCount(): int
172 {
173 return $this->requiredCount;
174 }
175
176 /**
177 * @return array<string|bool|int|float|array|null>
178 */
179 public function getArgumentDefaults(): array
180 {
181 $values = [];
182 foreach ($this->arguments as $argument) {
183 $values[$argument->getName()] = $argument->getDefault();
184 }
185
186 return $values;
187 }
188
189 /**
190 * Sets the InputOption objects.
191 *
192 * @param InputOption[] $options An array of InputOption objects
193 */
194 public function setOptions(array $options = []): void
195 {
196 $this->options = [];
197 $this->shortcuts = [];
198 $this->negations = [];
199 $this->addOptions($options);
200 }
201
202 /**
203 * Adds an array of InputOption objects.
204 *
205 * @param InputOption[] $options An array of InputOption objects
206 */
207 public function addOptions(array $options = []): void
208 {
209 foreach ($options as $option) {
210 $this->addOption($option);
211 }
212 }
213
214 /**
215 * @throws LogicException When option given already exist
216 */
217 public function addOption(InputOption $option): void
218 {
219 if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
220 throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
221 }
222 if (isset($this->negations[$option->getName()])) {
223 throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
224 }
225
226 if ($option->getShortcut()) {
227 foreach (explode('|', $option->getShortcut()) as $shortcut) {
228 if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) {
229 throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
230 }
231 }
232 }
233
234 $this->options[$option->getName()] = $option;
235 if ($option->getShortcut()) {
236 foreach (explode('|', $option->getShortcut()) as $shortcut) {
237 $this->shortcuts[$shortcut] = $option->getName();
238 }
239 }
240
241 if ($option->isNegatable()) {
242 $negatedName = 'no-'.$option->getName();
243 if (isset($this->options[$negatedName])) {
244 throw new LogicException(sprintf('An option named "%s" already exists.', $negatedName));
245 }
246 $this->negations[$negatedName] = $option->getName();
247 }
248 }
249
250 /**
251 * Returns an InputOption by name.
252 *
253 * @throws InvalidArgumentException When option given doesn't exist
254 */
255 public function getOption(string $name): InputOption
256 {
257 if (!$this->hasOption($name)) {
258 throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
259 }
260
261 return $this->options[$name];
262 }
263
264 /**
265 * Returns true if an InputOption object exists by name.
266 *
267 * This method can't be used to check if the user included the option when
268 * executing the command (use getOption() instead).
269 */
270 public function hasOption(string $name): bool
271 {
272 return isset($this->options[$name]);
273 }
274
275 /**
276 * Gets the array of InputOption objects.
277 *
278 * @return InputOption[]
279 */
280 public function getOptions(): array
281 {
282 return $this->options;
283 }
284
285 /**
286 * Returns true if an InputOption object exists by shortcut.
287 */
288 public function hasShortcut(string $name): bool
289 {
290 return isset($this->shortcuts[$name]);
291 }
292
293 /**
294 * Returns true if an InputOption object exists by negated name.
295 */
296 public function hasNegation(string $name): bool
297 {
298 return isset($this->negations[$name]);
299 }
300
301 /**
302 * Gets an InputOption by shortcut.
303 */
304 public function getOptionForShortcut(string $shortcut): InputOption
305 {
306 return $this->getOption($this->shortcutToName($shortcut));
307 }
308
309 /**
310 * @return array<string|bool|int|float|array|null>
311 */
312 public function getOptionDefaults(): array
313 {
314 $values = [];
315 foreach ($this->options as $option) {
316 $values[$option->getName()] = $option->getDefault();
317 }
318
319 return $values;
320 }
321
322 /**
323 * Returns the InputOption name given a shortcut.
324 *
325 * @throws InvalidArgumentException When option given does not exist
326 *
327 * @internal
328 */
329 public function shortcutToName(string $shortcut): string
330 {
331 if (!isset($this->shortcuts[$shortcut])) {
332 throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
333 }
334
335 return $this->shortcuts[$shortcut];
336 }
337
338 /**
339 * Returns the InputOption name given a negation.
340 *
341 * @throws InvalidArgumentException When option given does not exist
342 *
343 * @internal
344 */
345 public function negationToName(string $negation): string
346 {
347 if (!isset($this->negations[$negation])) {
348 throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $negation));
349 }
350
351 return $this->negations[$negation];
352 }
353
354 /**
355 * Gets the synopsis.
356 */
357 public function getSynopsis(bool $short = false): string
358 {
359 $elements = [];
360
361 if ($short && $this->getOptions()) {
362 $elements[] = '[options]';
363 } elseif (!$short) {
364 foreach ($this->getOptions() as $option) {
365 $value = '';
366 if ($option->acceptValue()) {
367 $value = sprintf(
368 ' %s%s%s',
369 $option->isValueOptional() ? '[' : '',
370 strtoupper($option->getName()),
371 $option->isValueOptional() ? ']' : ''
372 );
373 }
374
375 $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
376 $negation = $option->isNegatable() ? sprintf('|--no-%s', $option->getName()) : '';
377 $elements[] = sprintf('[%s--%s%s%s]', $shortcut, $option->getName(), $value, $negation);
378 }
379 }
380
381 if (\count($elements) && $this->getArguments()) {
382 $elements[] = '[--]';
383 }
384
385 $tail = '';
386 foreach ($this->getArguments() as $argument) {
387 $element = '<'.$argument->getName().'>';
388 if ($argument->isArray()) {
389 $element .= '...';
390 }
391
392 if (!$argument->isRequired()) {
393 $element = '['.$element;
394 $tail .= ']';
395 }
396
397 $elements[] = $element;
398 }
399
400 return implode(' ', $elements).$tail;
401 }
402}
diff --git a/vendor/symfony/console/Input/InputInterface.php b/vendor/symfony/console/Input/InputInterface.php
new file mode 100644
index 0000000..c177d96
--- /dev/null
+++ b/vendor/symfony/console/Input/InputInterface.php
@@ -0,0 +1,138 @@
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\Input;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Component\Console\Exception\RuntimeException;
16
17/**
18 * InputInterface is the interface implemented by all input classes.
19 *
20 * @author Fabien Potencier <fabien@symfony.com>
21 */
22interface InputInterface
23{
24 /**
25 * Returns the first argument from the raw parameters (not parsed).
26 */
27 public function getFirstArgument(): ?string;
28
29 /**
30 * Returns true if the raw parameters (not parsed) contain a value.
31 *
32 * This method is to be used to introspect the input parameters
33 * before they have been validated. It must be used carefully.
34 * Does not necessarily return the correct result for short options
35 * when multiple flags are combined in the same option.
36 *
37 * @param string|array $values The values to look for in the raw parameters (can be an array)
38 * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
39 */
40 public function hasParameterOption(string|array $values, bool $onlyParams = false): bool;
41
42 /**
43 * Returns the value of a raw option (not parsed).
44 *
45 * This method is to be used to introspect the input parameters
46 * before they have been validated. It must be used carefully.
47 * Does not necessarily return the correct result for short options
48 * when multiple flags are combined in the same option.
49 *
50 * @param string|array $values The value(s) to look for in the raw parameters (can be an array)
51 * @param string|bool|int|float|array|null $default The default value to return if no result is found
52 * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
53 */
54 public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed;
55
56 /**
57 * Binds the current Input instance with the given arguments and options.
58 *
59 * @throws RuntimeException
60 */
61 public function bind(InputDefinition $definition): void;
62
63 /**
64 * Validates the input.
65 *
66 * @throws RuntimeException When not enough arguments are given
67 */
68 public function validate(): void;
69
70 /**
71 * Returns all the given arguments merged with the default values.
72 *
73 * @return array<string|bool|int|float|array|null>
74 */
75 public function getArguments(): array;
76
77 /**
78 * Returns the argument value for a given argument name.
79 *
80 * @throws InvalidArgumentException When argument given doesn't exist
81 */
82 public function getArgument(string $name): mixed;
83
84 /**
85 * Sets an argument value by name.
86 *
87 * @throws InvalidArgumentException When argument given doesn't exist
88 */
89 public function setArgument(string $name, mixed $value): void;
90
91 /**
92 * Returns true if an InputArgument object exists by name or position.
93 */
94 public function hasArgument(string $name): bool;
95
96 /**
97 * Returns all the given options merged with the default values.
98 *
99 * @return array<string|bool|int|float|array|null>
100 */
101 public function getOptions(): array;
102
103 /**
104 * Returns the option value for a given option name.
105 *
106 * @throws InvalidArgumentException When option given doesn't exist
107 */
108 public function getOption(string $name): mixed;
109
110 /**
111 * Sets an option value by name.
112 *
113 * @throws InvalidArgumentException When option given doesn't exist
114 */
115 public function setOption(string $name, mixed $value): void;
116
117 /**
118 * Returns true if an InputOption object exists by name.
119 */
120 public function hasOption(string $name): bool;
121
122 /**
123 * Is this input means interactive?
124 */
125 public function isInteractive(): bool;
126
127 /**
128 * Sets the input interactivity.
129 */
130 public function setInteractive(bool $interactive): void;
131
132 /**
133 * Returns a stringified representation of the args passed to the command.
134 *
135 * InputArguments MUST be escaped as well as the InputOption values passed to the command.
136 */
137 public function __toString(): string;
138}
diff --git a/vendor/symfony/console/Input/InputOption.php b/vendor/symfony/console/Input/InputOption.php
new file mode 100644
index 0000000..617c348
--- /dev/null
+++ b/vendor/symfony/console/Input/InputOption.php
@@ -0,0 +1,262 @@
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\Input;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Completion\CompletionInput;
16use Symfony\Component\Console\Completion\CompletionSuggestions;
17use Symfony\Component\Console\Completion\Suggestion;
18use Symfony\Component\Console\Exception\InvalidArgumentException;
19use Symfony\Component\Console\Exception\LogicException;
20
21/**
22 * Represents a command line option.
23 *
24 * @author Fabien Potencier <fabien@symfony.com>
25 */
26class InputOption
27{
28 /**
29 * Do not accept input for the option (e.g. --yell). This is the default behavior of options.
30 */
31 public const VALUE_NONE = 1;
32
33 /**
34 * A value must be passed when the option is used (e.g. --iterations=5 or -i5).
35 */
36 public const VALUE_REQUIRED = 2;
37
38 /**
39 * The option may or may not have a value (e.g. --yell or --yell=loud).
40 */
41 public const VALUE_OPTIONAL = 4;
42
43 /**
44 * The option accepts multiple values (e.g. --dir=/foo --dir=/bar).
45 */
46 public const VALUE_IS_ARRAY = 8;
47
48 /**
49 * The option allows passing a negated variant (e.g. --ansi or --no-ansi).
50 */
51 public const VALUE_NEGATABLE = 16;
52
53 private string $name;
54 private ?string $shortcut;
55 private int $mode;
56 private string|int|bool|array|float|null $default;
57
58 /**
59 * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
60 * @param int-mask-of<InputOption::*>|null $mode The option mode: One of the VALUE_* constants
61 * @param string|bool|int|float|array|null $default The default value (must be null for self::VALUE_NONE)
62 * @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
63 *
64 * @throws InvalidArgumentException If option mode is invalid or incompatible
65 */
66 public function __construct(
67 string $name,
68 string|array|null $shortcut = null,
69 ?int $mode = null,
70 private string $description = '',
71 string|bool|int|float|array|null $default = null,
72 private array|\Closure $suggestedValues = [],
73 ) {
74 if (str_starts_with($name, '--')) {
75 $name = substr($name, 2);
76 }
77
78 if (!$name) {
79 throw new InvalidArgumentException('An option name cannot be empty.');
80 }
81
82 if ('' === $shortcut || [] === $shortcut || false === $shortcut) {
83 $shortcut = null;
84 }
85
86 if (null !== $shortcut) {
87 if (\is_array($shortcut)) {
88 $shortcut = implode('|', $shortcut);
89 }
90 $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
91 $shortcuts = array_filter($shortcuts, 'strlen');
92 $shortcut = implode('|', $shortcuts);
93
94 if ('' === $shortcut) {
95 throw new InvalidArgumentException('An option shortcut cannot be empty.');
96 }
97 }
98
99 if (null === $mode) {
100 $mode = self::VALUE_NONE;
101 } elseif ($mode >= (self::VALUE_NEGATABLE << 1) || $mode < 1) {
102 throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
103 }
104
105 $this->name = $name;
106 $this->shortcut = $shortcut;
107 $this->mode = $mode;
108
109 if ($suggestedValues && !$this->acceptValue()) {
110 throw new LogicException('Cannot set suggested values if the option does not accept a value.');
111 }
112 if ($this->isArray() && !$this->acceptValue()) {
113 throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
114 }
115 if ($this->isNegatable() && $this->acceptValue()) {
116 throw new InvalidArgumentException('Impossible to have an option mode VALUE_NEGATABLE if the option also accepts a value.');
117 }
118
119 $this->setDefault($default);
120 }
121
122 /**
123 * Returns the option shortcut.
124 */
125 public function getShortcut(): ?string
126 {
127 return $this->shortcut;
128 }
129
130 /**
131 * Returns the option name.
132 */
133 public function getName(): string
134 {
135 return $this->name;
136 }
137
138 /**
139 * Returns true if the option accepts a value.
140 *
141 * @return bool true if value mode is not self::VALUE_NONE, false otherwise
142 */
143 public function acceptValue(): bool
144 {
145 return $this->isValueRequired() || $this->isValueOptional();
146 }
147
148 /**
149 * Returns true if the option requires a value.
150 *
151 * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise
152 */
153 public function isValueRequired(): bool
154 {
155 return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
156 }
157
158 /**
159 * Returns true if the option takes an optional value.
160 *
161 * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise
162 */
163 public function isValueOptional(): bool
164 {
165 return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
166 }
167
168 /**
169 * Returns true if the option can take multiple values.
170 *
171 * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise
172 */
173 public function isArray(): bool
174 {
175 return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
176 }
177
178 /**
179 * Returns true if the option allows passing a negated variant.
180 *
181 * @return bool true if mode is self::VALUE_NEGATABLE, false otherwise
182 */
183 public function isNegatable(): bool
184 {
185 return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode);
186 }
187
188 /**
189 * Sets the default value.
190 */
191 public function setDefault(string|bool|int|float|array|null $default): void
192 {
193 if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
194 throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
195 }
196
197 if ($this->isArray()) {
198 if (null === $default) {
199 $default = [];
200 } elseif (!\is_array($default)) {
201 throw new LogicException('A default value for an array option must be an array.');
202 }
203 }
204
205 $this->default = $this->acceptValue() || $this->isNegatable() ? $default : false;
206 }
207
208 /**
209 * Returns the default value.
210 */
211 public function getDefault(): string|bool|int|float|array|null
212 {
213 return $this->default;
214 }
215
216 /**
217 * Returns the description text.
218 */
219 public function getDescription(): string
220 {
221 return $this->description;
222 }
223
224 /**
225 * Returns true if the option has values for input completion.
226 */
227 public function hasCompletion(): bool
228 {
229 return [] !== $this->suggestedValues;
230 }
231
232 /**
233 * Supplies suggestions when command resolves possible completion options for input.
234 *
235 * @see Command::complete()
236 */
237 public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
238 {
239 $values = $this->suggestedValues;
240 if ($values instanceof \Closure && !\is_array($values = $values($input))) {
241 throw new LogicException(sprintf('Closure for option "%s" must return an array. Got "%s".', $this->name, get_debug_type($values)));
242 }
243 if ($values) {
244 $suggestions->suggestValues($values);
245 }
246 }
247
248 /**
249 * Checks whether the given option equals this one.
250 */
251 public function equals(self $option): bool
252 {
253 return $option->getName() === $this->getName()
254 && $option->getShortcut() === $this->getShortcut()
255 && $option->getDefault() === $this->getDefault()
256 && $option->isNegatable() === $this->isNegatable()
257 && $option->isArray() === $this->isArray()
258 && $option->isValueRequired() === $this->isValueRequired()
259 && $option->isValueOptional() === $this->isValueOptional()
260 ;
261 }
262}
diff --git a/vendor/symfony/console/Input/StreamableInputInterface.php b/vendor/symfony/console/Input/StreamableInputInterface.php
new file mode 100644
index 0000000..4a0dc01
--- /dev/null
+++ b/vendor/symfony/console/Input/StreamableInputInterface.php
@@ -0,0 +1,37 @@
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\Input;
13
14/**
15 * StreamableInputInterface is the interface implemented by all input classes
16 * that have an input stream.
17 *
18 * @author Robin Chalas <robin.chalas@gmail.com>
19 */
20interface StreamableInputInterface extends InputInterface
21{
22 /**
23 * Sets the input stream to read from when interacting with the user.
24 *
25 * This is mainly useful for testing purpose.
26 *
27 * @param resource $stream The input stream
28 */
29 public function setStream($stream): void;
30
31 /**
32 * Returns the input stream.
33 *
34 * @return resource|null
35 */
36 public function getStream();
37}
diff --git a/vendor/symfony/console/Input/StringInput.php b/vendor/symfony/console/Input/StringInput.php
new file mode 100644
index 0000000..8357001
--- /dev/null
+++ b/vendor/symfony/console/Input/StringInput.php
@@ -0,0 +1,85 @@
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\Input;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15
16/**
17 * StringInput represents an input provided as a string.
18 *
19 * Usage:
20 *
21 * $input = new StringInput('foo --bar="foobar"');
22 *
23 * @author Fabien Potencier <fabien@symfony.com>
24 */
25class StringInput extends ArgvInput
26{
27 public const REGEX_UNQUOTED_STRING = '([^\s\\\\]+?)';
28 public const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
29
30 /**
31 * @param string $input A string representing the parameters from the CLI
32 */
33 public function __construct(string $input)
34 {
35 parent::__construct([]);
36
37 $this->setTokens($this->tokenize($input));
38 }
39
40 /**
41 * Tokenizes a string.
42 *
43 * @return list<string>
44 *
45 * @throws InvalidArgumentException When unable to parse input (should never happen)
46 */
47 private function tokenize(string $input): array
48 {
49 $tokens = [];
50 $length = \strlen($input);
51 $cursor = 0;
52 $token = null;
53 while ($cursor < $length) {
54 if ('\\' === $input[$cursor]) {
55 $token .= $input[++$cursor] ?? '';
56 ++$cursor;
57 continue;
58 }
59
60 if (preg_match('/\s+/A', $input, $match, 0, $cursor)) {
61 if (null !== $token) {
62 $tokens[] = $token;
63 $token = null;
64 }
65 } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, 0, $cursor)) {
66 $token .= $match[1].$match[2].stripcslashes(str_replace(['"\'', '\'"', '\'\'', '""'], '', substr($match[3], 1, -1)));
67 } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, 0, $cursor)) {
68 $token .= stripcslashes(substr($match[0], 1, -1));
69 } elseif (preg_match('/'.self::REGEX_UNQUOTED_STRING.'/A', $input, $match, 0, $cursor)) {
70 $token .= $match[1];
71 } else {
72 // should never happen
73 throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ...".', substr($input, $cursor, 10)));
74 }
75
76 $cursor += \strlen($match[0]);
77 }
78
79 if (null !== $token) {
80 $tokens[] = $token;
81 }
82
83 return $tokens;
84 }
85}
diff --git a/vendor/symfony/console/LICENSE b/vendor/symfony/console/LICENSE
new file mode 100644
index 0000000..0138f8f
--- /dev/null
+++ b/vendor/symfony/console/LICENSE
@@ -0,0 +1,19 @@
1Copyright (c) 2004-present Fabien Potencier
2
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is furnished
8to do so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in all
11copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19THE SOFTWARE.
diff --git a/vendor/symfony/console/Logger/ConsoleLogger.php b/vendor/symfony/console/Logger/ConsoleLogger.php
new file mode 100644
index 0000000..ad6e49c
--- /dev/null
+++ b/vendor/symfony/console/Logger/ConsoleLogger.php
@@ -0,0 +1,120 @@
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\Logger;
13
14use Psr\Log\AbstractLogger;
15use Psr\Log\InvalidArgumentException;
16use Psr\Log\LogLevel;
17use Symfony\Component\Console\Output\ConsoleOutputInterface;
18use Symfony\Component\Console\Output\OutputInterface;
19
20/**
21 * PSR-3 compliant console logger.
22 *
23 * @author Kévin Dunglas <dunglas@gmail.com>
24 *
25 * @see https://www.php-fig.org/psr/psr-3/
26 */
27class ConsoleLogger extends AbstractLogger
28{
29 public const INFO = 'info';
30 public const ERROR = 'error';
31
32 private array $verbosityLevelMap = [
33 LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
34 LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
35 LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
36 LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
37 LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
38 LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE,
39 LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE,
40 LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG,
41 ];
42 private array $formatLevelMap = [
43 LogLevel::EMERGENCY => self::ERROR,
44 LogLevel::ALERT => self::ERROR,
45 LogLevel::CRITICAL => self::ERROR,
46 LogLevel::ERROR => self::ERROR,
47 LogLevel::WARNING => self::INFO,
48 LogLevel::NOTICE => self::INFO,
49 LogLevel::INFO => self::INFO,
50 LogLevel::DEBUG => self::INFO,
51 ];
52 private bool $errored = false;
53
54 public function __construct(
55 private OutputInterface $output,
56 array $verbosityLevelMap = [],
57 array $formatLevelMap = [],
58 ) {
59 $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
60 $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
61 }
62
63 public function log($level, $message, array $context = []): void
64 {
65 if (!isset($this->verbosityLevelMap[$level])) {
66 throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
67 }
68
69 $output = $this->output;
70
71 // Write to the error output if necessary and available
72 if (self::ERROR === $this->formatLevelMap[$level]) {
73 if ($this->output instanceof ConsoleOutputInterface) {
74 $output = $output->getErrorOutput();
75 }
76 $this->errored = true;
77 }
78
79 // the if condition check isn't necessary -- it's the same one that $output will do internally anyway.
80 // We only do it for efficiency here as the message formatting is relatively expensive.
81 if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) {
82 $output->writeln(sprintf('<%1$s>[%2$s] %3$s</%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]);
83 }
84 }
85
86 /**
87 * Returns true when any messages have been logged at error levels.
88 */
89 public function hasErrored(): bool
90 {
91 return $this->errored;
92 }
93
94 /**
95 * Interpolates context values into the message placeholders.
96 *
97 * @author PHP Framework Interoperability Group
98 */
99 private function interpolate(string $message, array $context): string
100 {
101 if (!str_contains($message, '{')) {
102 return $message;
103 }
104
105 $replacements = [];
106 foreach ($context as $key => $val) {
107 if (null === $val || \is_scalar($val) || $val instanceof \Stringable) {
108 $replacements["{{$key}}"] = $val;
109 } elseif ($val instanceof \DateTimeInterface) {
110 $replacements["{{$key}}"] = $val->format(\DateTimeInterface::RFC3339);
111 } elseif (\is_object($val)) {
112 $replacements["{{$key}}"] = '[object '.$val::class.']';
113 } else {
114 $replacements["{{$key}}"] = '['.\gettype($val).']';
115 }
116 }
117
118 return strtr($message, $replacements);
119 }
120}
diff --git a/vendor/symfony/console/Messenger/RunCommandContext.php b/vendor/symfony/console/Messenger/RunCommandContext.php
new file mode 100644
index 0000000..2ee5415
--- /dev/null
+++ b/vendor/symfony/console/Messenger/RunCommandContext.php
@@ -0,0 +1,25 @@
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\Messenger;
13
14/**
15 * @author Kevin Bond <kevinbond@gmail.com>
16 */
17final class RunCommandContext
18{
19 public function __construct(
20 public readonly RunCommandMessage $message,
21 public readonly int $exitCode,
22 public readonly string $output,
23 ) {
24 }
25}
diff --git a/vendor/symfony/console/Messenger/RunCommandMessage.php b/vendor/symfony/console/Messenger/RunCommandMessage.php
new file mode 100644
index 0000000..b530c43
--- /dev/null
+++ b/vendor/symfony/console/Messenger/RunCommandMessage.php
@@ -0,0 +1,36 @@
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\Messenger;
13
14use Symfony\Component\Console\Exception\RunCommandFailedException;
15
16/**
17 * @author Kevin Bond <kevinbond@gmail.com>
18 */
19class RunCommandMessage implements \Stringable
20{
21 /**
22 * @param bool $throwOnFailure If the command has a non-zero exit code, throw {@see RunCommandFailedException}
23 * @param bool $catchExceptions @see Application::setCatchExceptions()
24 */
25 public function __construct(
26 public readonly string $input,
27 public readonly bool $throwOnFailure = true,
28 public readonly bool $catchExceptions = false,
29 ) {
30 }
31
32 public function __toString(): string
33 {
34 return $this->input;
35 }
36}
diff --git a/vendor/symfony/console/Messenger/RunCommandMessageHandler.php b/vendor/symfony/console/Messenger/RunCommandMessageHandler.php
new file mode 100644
index 0000000..1bc4994
--- /dev/null
+++ b/vendor/symfony/console/Messenger/RunCommandMessageHandler.php
@@ -0,0 +1,49 @@
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\Messenger;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Exception\RunCommandFailedException;
17use Symfony\Component\Console\Input\StringInput;
18use Symfony\Component\Console\Output\BufferedOutput;
19
20/**
21 * @author Kevin Bond <kevinbond@gmail.com>
22 */
23final class RunCommandMessageHandler
24{
25 public function __construct(
26 private readonly Application $application,
27 ) {
28 }
29
30 public function __invoke(RunCommandMessage $message): RunCommandContext
31 {
32 $input = new StringInput($message->input);
33 $output = new BufferedOutput();
34
35 $this->application->setCatchExceptions($message->catchExceptions);
36
37 try {
38 $exitCode = $this->application->run($input, $output);
39 } catch (\Throwable $e) {
40 throw new RunCommandFailedException($e, new RunCommandContext($message, Command::FAILURE, $output->fetch()));
41 }
42
43 if ($message->throwOnFailure && Command::SUCCESS !== $exitCode) {
44 throw new RunCommandFailedException(sprintf('Command "%s" exited with code "%s".', $message->input, $exitCode), new RunCommandContext($message, $exitCode, $output->fetch()));
45 }
46
47 return new RunCommandContext($message, $exitCode, $output->fetch());
48 }
49}
diff --git a/vendor/symfony/console/Output/AnsiColorMode.php b/vendor/symfony/console/Output/AnsiColorMode.php
new file mode 100644
index 0000000..ca40ffb
--- /dev/null
+++ b/vendor/symfony/console/Output/AnsiColorMode.php
@@ -0,0 +1,106 @@
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\Output;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15
16/**
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @author Julien Boudry <julien@condorcet.vote>
19 */
20enum AnsiColorMode
21{
22 /*
23 * Classical 4-bit Ansi colors, including 8 classical colors and 8 bright color. Output syntax is "ESC[${foreGroundColorcode};${backGroundColorcode}m"
24 * Must be compatible with all terminals and it's the minimal version supported.
25 */
26 case Ansi4;
27
28 /*
29 * 8-bit Ansi colors (240 different colors + 16 duplicate color codes, ensuring backward compatibility).
30 * Output syntax is: "ESC[38;5;${foreGroundColorcode};48;5;${backGroundColorcode}m"
31 * Should be compatible with most terminals.
32 */
33 case Ansi8;
34
35 /*
36 * 24-bit Ansi colors (RGB).
37 * Output syntax is: "ESC[38;2;${foreGroundColorcodeRed};${foreGroundColorcodeGreen};${foreGroundColorcodeBlue};48;2;${backGroundColorcodeRed};${backGroundColorcodeGreen};${backGroundColorcodeBlue}m"
38 * May be compatible with many modern terminals.
39 */
40 case Ansi24;
41
42 /**
43 * Converts an RGB hexadecimal color to the corresponding Ansi code.
44 */
45 public function convertFromHexToAnsiColorCode(string $hexColor): string
46 {
47 $hexColor = str_replace('#', '', $hexColor);
48
49 if (3 === \strlen($hexColor)) {
50 $hexColor = $hexColor[0].$hexColor[0].$hexColor[1].$hexColor[1].$hexColor[2].$hexColor[2];
51 }
52
53 if (6 !== \strlen($hexColor)) {
54 throw new InvalidArgumentException(sprintf('Invalid "#%s" color.', $hexColor));
55 }
56
57 $color = hexdec($hexColor);
58
59 $r = ($color >> 16) & 255;
60 $g = ($color >> 8) & 255;
61 $b = $color & 255;
62
63 return match ($this) {
64 self::Ansi4 => (string) $this->convertFromRGB($r, $g, $b),
65 self::Ansi8 => '8;5;'.((string) $this->convertFromRGB($r, $g, $b)),
66 self::Ansi24 => sprintf('8;2;%d;%d;%d', $r, $g, $b),
67 };
68 }
69
70 private function convertFromRGB(int $r, int $g, int $b): int
71 {
72 return match ($this) {
73 self::Ansi4 => $this->degradeHexColorToAnsi4($r, $g, $b),
74 self::Ansi8 => $this->degradeHexColorToAnsi8($r, $g, $b),
75 default => throw new InvalidArgumentException("RGB cannot be converted to {$this->name}."),
76 };
77 }
78
79 private function degradeHexColorToAnsi4(int $r, int $g, int $b): int
80 {
81 return round($b / 255) << 2 | (round($g / 255) << 1) | round($r / 255);
82 }
83
84 /**
85 * Inspired from https://github.com/ajalt/colormath/blob/e464e0da1b014976736cf97250063248fc77b8e7/colormath/src/commonMain/kotlin/com/github/ajalt/colormath/model/Ansi256.kt code (MIT license).
86 */
87 private function degradeHexColorToAnsi8(int $r, int $g, int $b): int
88 {
89 if ($r === $g && $g === $b) {
90 if ($r < 8) {
91 return 16;
92 }
93
94 if ($r > 248) {
95 return 231;
96 }
97
98 return (int) round(($r - 8) / 247 * 24) + 232;
99 } else {
100 return 16 +
101 (36 * (int) round($r / 255 * 5)) +
102 (6 * (int) round($g / 255 * 5)) +
103 (int) round($b / 255 * 5);
104 }
105 }
106}
diff --git a/vendor/symfony/console/Output/BufferedOutput.php b/vendor/symfony/console/Output/BufferedOutput.php
new file mode 100644
index 0000000..3c8d390
--- /dev/null
+++ b/vendor/symfony/console/Output/BufferedOutput.php
@@ -0,0 +1,40 @@
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\Output;
13
14/**
15 * @author Jean-François Simon <contact@jfsimon.fr>
16 */
17class BufferedOutput extends Output
18{
19 private string $buffer = '';
20
21 /**
22 * Empties buffer and returns its content.
23 */
24 public function fetch(): string
25 {
26 $content = $this->buffer;
27 $this->buffer = '';
28
29 return $content;
30 }
31
32 protected function doWrite(string $message, bool $newline): void
33 {
34 $this->buffer .= $message;
35
36 if ($newline) {
37 $this->buffer .= \PHP_EOL;
38 }
39 }
40}
diff --git a/vendor/symfony/console/Output/ConsoleOutput.php b/vendor/symfony/console/Output/ConsoleOutput.php
new file mode 100644
index 0000000..2ad3dbc
--- /dev/null
+++ b/vendor/symfony/console/Output/ConsoleOutput.php
@@ -0,0 +1,153 @@
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\Output;
13
14use Symfony\Component\Console\Formatter\OutputFormatterInterface;
15
16/**
17 * ConsoleOutput is the default class for all CLI output. It uses STDOUT and STDERR.
18 *
19 * This class is a convenient wrapper around `StreamOutput` for both STDOUT and STDERR.
20 *
21 * $output = new ConsoleOutput();
22 *
23 * This is equivalent to:
24 *
25 * $output = new StreamOutput(fopen('php://stdout', 'w'));
26 * $stdErr = new StreamOutput(fopen('php://stderr', 'w'));
27 *
28 * @author Fabien Potencier <fabien@symfony.com>
29 */
30class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
31{
32 private OutputInterface $stderr;
33 private array $consoleSectionOutputs = [];
34
35 /**
36 * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
37 * @param bool|null $decorated Whether to decorate messages (null for auto-guessing)
38 * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
39 */
40 public function __construct(int $verbosity = self::VERBOSITY_NORMAL, ?bool $decorated = null, ?OutputFormatterInterface $formatter = null)
41 {
42 parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter);
43
44 if (null === $formatter) {
45 // for BC reasons, stdErr has it own Formatter only when user don't inject a specific formatter.
46 $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated);
47
48 return;
49 }
50
51 $actualDecorated = $this->isDecorated();
52 $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter());
53
54 if (null === $decorated) {
55 $this->setDecorated($actualDecorated && $this->stderr->isDecorated());
56 }
57 }
58
59 /**
60 * Creates a new output section.
61 */
62 public function section(): ConsoleSectionOutput
63 {
64 return new ConsoleSectionOutput($this->getStream(), $this->consoleSectionOutputs, $this->getVerbosity(), $this->isDecorated(), $this->getFormatter());
65 }
66
67 public function setDecorated(bool $decorated): void
68 {
69 parent::setDecorated($decorated);
70 $this->stderr->setDecorated($decorated);
71 }
72
73 public function setFormatter(OutputFormatterInterface $formatter): void
74 {
75 parent::setFormatter($formatter);
76 $this->stderr->setFormatter($formatter);
77 }
78
79 public function setVerbosity(int $level): void
80 {
81 parent::setVerbosity($level);
82 $this->stderr->setVerbosity($level);
83 }
84
85 public function getErrorOutput(): OutputInterface
86 {
87 return $this->stderr;
88 }
89
90 public function setErrorOutput(OutputInterface $error): void
91 {
92 $this->stderr = $error;
93 }
94
95 /**
96 * Returns true if current environment supports writing console output to
97 * STDOUT.
98 */
99 protected function hasStdoutSupport(): bool
100 {
101 return false === $this->isRunningOS400();
102 }
103
104 /**
105 * Returns true if current environment supports writing console output to
106 * STDERR.
107 */
108 protected function hasStderrSupport(): bool
109 {
110 return false === $this->isRunningOS400();
111 }
112
113 /**
114 * Checks if current executing environment is IBM iSeries (OS400), which
115 * doesn't properly convert character-encodings between ASCII to EBCDIC.
116 */
117 private function isRunningOS400(): bool
118 {
119 $checks = [
120 \function_exists('php_uname') ? php_uname('s') : '',
121 getenv('OSTYPE'),
122 \PHP_OS,
123 ];
124
125 return false !== stripos(implode(';', $checks), 'OS400');
126 }
127
128 /**
129 * @return resource
130 */
131 private function openOutputStream()
132 {
133 if (!$this->hasStdoutSupport()) {
134 return fopen('php://output', 'w');
135 }
136
137 // Use STDOUT when possible to prevent from opening too many file descriptors
138 return \defined('STDOUT') ? \STDOUT : (@fopen('php://stdout', 'w') ?: fopen('php://output', 'w'));
139 }
140
141 /**
142 * @return resource
143 */
144 private function openErrorStream()
145 {
146 if (!$this->hasStderrSupport()) {
147 return fopen('php://output', 'w');
148 }
149
150 // Use STDERR when possible to prevent from opening too many file descriptors
151 return \defined('STDERR') ? \STDERR : (@fopen('php://stderr', 'w') ?: fopen('php://output', 'w'));
152 }
153}
diff --git a/vendor/symfony/console/Output/ConsoleOutputInterface.php b/vendor/symfony/console/Output/ConsoleOutputInterface.php
new file mode 100644
index 0000000..1f8f147
--- /dev/null
+++ b/vendor/symfony/console/Output/ConsoleOutputInterface.php
@@ -0,0 +1,30 @@
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\Output;
13
14/**
15 * ConsoleOutputInterface is the interface implemented by ConsoleOutput class.
16 * This adds information about stderr and section output stream.
17 *
18 * @author Dariusz Górecki <darek.krk@gmail.com>
19 */
20interface ConsoleOutputInterface extends OutputInterface
21{
22 /**
23 * Gets the OutputInterface for errors.
24 */
25 public function getErrorOutput(): OutputInterface;
26
27 public function setErrorOutput(OutputInterface $error): void;
28
29 public function section(): ConsoleSectionOutput;
30}
diff --git a/vendor/symfony/console/Output/ConsoleSectionOutput.php b/vendor/symfony/console/Output/ConsoleSectionOutput.php
new file mode 100644
index 0000000..09aa7fe
--- /dev/null
+++ b/vendor/symfony/console/Output/ConsoleSectionOutput.php
@@ -0,0 +1,237 @@
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\Output;
13
14use Symfony\Component\Console\Formatter\OutputFormatterInterface;
15use Symfony\Component\Console\Helper\Helper;
16use Symfony\Component\Console\Terminal;
17
18/**
19 * @author Pierre du Plessis <pdples@gmail.com>
20 * @author Gabriel Ostrolucký <gabriel.ostrolucky@gmail.com>
21 */
22class ConsoleSectionOutput extends StreamOutput
23{
24 private array $content = [];
25 private int $lines = 0;
26 private array $sections;
27 private Terminal $terminal;
28 private int $maxHeight = 0;
29
30 /**
31 * @param resource $stream
32 * @param ConsoleSectionOutput[] $sections
33 */
34 public function __construct($stream, array &$sections, int $verbosity, bool $decorated, OutputFormatterInterface $formatter)
35 {
36 parent::__construct($stream, $verbosity, $decorated, $formatter);
37 array_unshift($sections, $this);
38 $this->sections = &$sections;
39 $this->terminal = new Terminal();
40 }
41
42 /**
43 * Defines a maximum number of lines for this section.
44 *
45 * When more lines are added, the section will automatically scroll to the
46 * end (i.e. remove the first lines to comply with the max height).
47 */
48 public function setMaxHeight(int $maxHeight): void
49 {
50 // when changing max height, clear output of current section and redraw again with the new height
51 $previousMaxHeight = $this->maxHeight;
52 $this->maxHeight = $maxHeight;
53 $existingContent = $this->popStreamContentUntilCurrentSection($previousMaxHeight ? min($previousMaxHeight, $this->lines) : $this->lines);
54
55 parent::doWrite($this->getVisibleContent(), false);
56 parent::doWrite($existingContent, false);
57 }
58
59 /**
60 * Clears previous output for this section.
61 *
62 * @param int $lines Number of lines to clear. If null, then the entire output of this section is cleared
63 */
64 public function clear(?int $lines = null): void
65 {
66 if (!$this->content || !$this->isDecorated()) {
67 return;
68 }
69
70 if ($lines) {
71 array_splice($this->content, -$lines);
72 } else {
73 $lines = $this->lines;
74 $this->content = [];
75 }
76
77 $this->lines -= $lines;
78
79 parent::doWrite($this->popStreamContentUntilCurrentSection($this->maxHeight ? min($this->maxHeight, $lines) : $lines), false);
80 }
81
82 /**
83 * Overwrites the previous output with a new message.
84 */
85 public function overwrite(string|iterable $message): void
86 {
87 $this->clear();
88 $this->writeln($message);
89 }
90
91 public function getContent(): string
92 {
93 return implode('', $this->content);
94 }
95
96 public function getVisibleContent(): string
97 {
98 if (0 === $this->maxHeight) {
99 return $this->getContent();
100 }
101
102 return implode('', \array_slice($this->content, -$this->maxHeight));
103 }
104
105 /**
106 * @internal
107 */
108 public function addContent(string $input, bool $newline = true): int
109 {
110 $width = $this->terminal->getWidth();
111 $lines = explode(\PHP_EOL, $input);
112 $linesAdded = 0;
113 $count = \count($lines) - 1;
114 foreach ($lines as $i => $lineContent) {
115 // re-add the line break (that has been removed in the above `explode()` for
116 // - every line that is not the last line
117 // - if $newline is required, also add it to the last line
118 if ($i < $count || $newline) {
119 $lineContent .= \PHP_EOL;
120 }
121
122 // skip line if there is no text (or newline for that matter)
123 if ('' === $lineContent) {
124 continue;
125 }
126
127 // For the first line, check if the previous line (last entry of `$this->content`)
128 // needs to be continued (i.e. does not end with a line break).
129 if (0 === $i
130 && (false !== $lastLine = end($this->content))
131 && !str_ends_with($lastLine, \PHP_EOL)
132 ) {
133 // deduct the line count of the previous line
134 $this->lines -= (int) ceil($this->getDisplayLength($lastLine) / $width) ?: 1;
135 // concatenate previous and new line
136 $lineContent = $lastLine.$lineContent;
137 // replace last entry of `$this->content` with the new expanded line
138 array_splice($this->content, -1, 1, $lineContent);
139 } else {
140 // otherwise just add the new content
141 $this->content[] = $lineContent;
142 }
143
144 $linesAdded += (int) ceil($this->getDisplayLength($lineContent) / $width) ?: 1;
145 }
146
147 $this->lines += $linesAdded;
148
149 return $linesAdded;
150 }
151
152 /**
153 * @internal
154 */
155 public function addNewLineOfInputSubmit(): void
156 {
157 $this->content[] = \PHP_EOL;
158 ++$this->lines;
159 }
160
161 protected function doWrite(string $message, bool $newline): void
162 {
163 // Simulate newline behavior for consistent output formatting, avoiding extra logic
164 if (!$newline && str_ends_with($message, \PHP_EOL)) {
165 $message = substr($message, 0, -\strlen(\PHP_EOL));
166 $newline = true;
167 }
168
169 if (!$this->isDecorated()) {
170 parent::doWrite($message, $newline);
171
172 return;
173 }
174
175 // Check if the previous line (last entry of `$this->content`) needs to be continued
176 // (i.e. does not end with a line break). In which case, it needs to be erased first.
177 $linesToClear = $deleteLastLine = ($lastLine = end($this->content) ?: '') && !str_ends_with($lastLine, \PHP_EOL) ? 1 : 0;
178
179 $linesAdded = $this->addContent($message, $newline);
180
181 if ($lineOverflow = $this->maxHeight > 0 && $this->lines > $this->maxHeight) {
182 // on overflow, clear the whole section and redraw again (to remove the first lines)
183 $linesToClear = $this->maxHeight;
184 }
185
186 $erasedContent = $this->popStreamContentUntilCurrentSection($linesToClear);
187
188 if ($lineOverflow) {
189 // redraw existing lines of the section
190 $previousLinesOfSection = \array_slice($this->content, $this->lines - $this->maxHeight, $this->maxHeight - $linesAdded);
191 parent::doWrite(implode('', $previousLinesOfSection), false);
192 }
193
194 // if the last line was removed, re-print its content together with the new content.
195 // otherwise, just print the new content.
196 parent::doWrite($deleteLastLine ? $lastLine.$message : $message, true);
197 parent::doWrite($erasedContent, false);
198 }
199
200 /**
201 * At initial stage, cursor is at the end of stream output. This method makes cursor crawl upwards until it hits
202 * current section. Then it erases content it crawled through. Optionally, it erases part of current section too.
203 */
204 private function popStreamContentUntilCurrentSection(int $numberOfLinesToClearFromCurrentSection = 0): string
205 {
206 $numberOfLinesToClear = $numberOfLinesToClearFromCurrentSection;
207 $erasedContent = [];
208
209 foreach ($this->sections as $section) {
210 if ($section === $this) {
211 break;
212 }
213
214 $numberOfLinesToClear += $section->maxHeight ? min($section->lines, $section->maxHeight) : $section->lines;
215 if ('' !== $sectionContent = $section->getVisibleContent()) {
216 if (!str_ends_with($sectionContent, \PHP_EOL)) {
217 $sectionContent .= \PHP_EOL;
218 }
219 $erasedContent[] = $sectionContent;
220 }
221 }
222
223 if ($numberOfLinesToClear > 0) {
224 // move cursor up n lines
225 parent::doWrite(sprintf("\x1b[%dA", $numberOfLinesToClear), false);
226 // erase to end of screen
227 parent::doWrite("\x1b[0J", false);
228 }
229
230 return implode('', array_reverse($erasedContent));
231 }
232
233 private function getDisplayLength(string $text): int
234 {
235 return Helper::width(Helper::removeDecoration($this->getFormatter(), str_replace("\t", ' ', $text)));
236 }
237}
diff --git a/vendor/symfony/console/Output/NullOutput.php b/vendor/symfony/console/Output/NullOutput.php
new file mode 100644
index 0000000..40ae332
--- /dev/null
+++ b/vendor/symfony/console/Output/NullOutput.php
@@ -0,0 +1,89 @@
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\Output;
13
14use Symfony\Component\Console\Formatter\NullOutputFormatter;
15use Symfony\Component\Console\Formatter\OutputFormatterInterface;
16
17/**
18 * NullOutput suppresses all output.
19 *
20 * $output = new NullOutput();
21 *
22 * @author Fabien Potencier <fabien@symfony.com>
23 * @author Tobias Schultze <http://tobion.de>
24 */
25class NullOutput implements OutputInterface
26{
27 private NullOutputFormatter $formatter;
28
29 public function setFormatter(OutputFormatterInterface $formatter): void
30 {
31 // do nothing
32 }
33
34 public function getFormatter(): OutputFormatterInterface
35 {
36 // to comply with the interface we must return a OutputFormatterInterface
37 return $this->formatter ??= new NullOutputFormatter();
38 }
39
40 public function setDecorated(bool $decorated): void
41 {
42 // do nothing
43 }
44
45 public function isDecorated(): bool
46 {
47 return false;
48 }
49
50 public function setVerbosity(int $level): void
51 {
52 // do nothing
53 }
54
55 public function getVerbosity(): int
56 {
57 return self::VERBOSITY_QUIET;
58 }
59
60 public function isQuiet(): bool
61 {
62 return true;
63 }
64
65 public function isVerbose(): bool
66 {
67 return false;
68 }
69
70 public function isVeryVerbose(): bool
71 {
72 return false;
73 }
74
75 public function isDebug(): bool
76 {
77 return false;
78 }
79
80 public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL): void
81 {
82 // do nothing
83 }
84
85 public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL): void
86 {
87 // do nothing
88 }
89}
diff --git a/vendor/symfony/console/Output/Output.php b/vendor/symfony/console/Output/Output.php
new file mode 100644
index 0000000..2bb1057
--- /dev/null
+++ b/vendor/symfony/console/Output/Output.php
@@ -0,0 +1,138 @@
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\Output;
13
14use Symfony\Component\Console\Formatter\OutputFormatter;
15use Symfony\Component\Console\Formatter\OutputFormatterInterface;
16
17/**
18 * Base class for output classes.
19 *
20 * There are five levels of verbosity:
21 *
22 * * normal: no option passed (normal output)
23 * * verbose: -v (more output)
24 * * very verbose: -vv (highly extended output)
25 * * debug: -vvv (all debug output)
26 * * quiet: -q (no output)
27 *
28 * @author Fabien Potencier <fabien@symfony.com>
29 */
30abstract class Output implements OutputInterface
31{
32 private int $verbosity;
33 private OutputFormatterInterface $formatter;
34
35 /**
36 * @param int|null $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
37 * @param bool $decorated Whether to decorate messages
38 * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
39 */
40 public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, ?OutputFormatterInterface $formatter = null)
41 {
42 $this->verbosity = $verbosity ?? self::VERBOSITY_NORMAL;
43 $this->formatter = $formatter ?? new OutputFormatter();
44 $this->formatter->setDecorated($decorated);
45 }
46
47 public function setFormatter(OutputFormatterInterface $formatter): void
48 {
49 $this->formatter = $formatter;
50 }
51
52 public function getFormatter(): OutputFormatterInterface
53 {
54 return $this->formatter;
55 }
56
57 public function setDecorated(bool $decorated): void
58 {
59 $this->formatter->setDecorated($decorated);
60 }
61
62 public function isDecorated(): bool
63 {
64 return $this->formatter->isDecorated();
65 }
66
67 public function setVerbosity(int $level): void
68 {
69 $this->verbosity = $level;
70 }
71
72 public function getVerbosity(): int
73 {
74 return $this->verbosity;
75 }
76
77 public function isQuiet(): bool
78 {
79 return self::VERBOSITY_QUIET === $this->verbosity;
80 }
81
82 public function isVerbose(): bool
83 {
84 return self::VERBOSITY_VERBOSE <= $this->verbosity;
85 }
86
87 public function isVeryVerbose(): bool
88 {
89 return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
90 }
91
92 public function isDebug(): bool
93 {
94 return self::VERBOSITY_DEBUG <= $this->verbosity;
95 }
96
97 public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL): void
98 {
99 $this->write($messages, true, $options);
100 }
101
102 public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL): void
103 {
104 if (!is_iterable($messages)) {
105 $messages = [$messages];
106 }
107
108 $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN;
109 $type = $types & $options ?: self::OUTPUT_NORMAL;
110
111 $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG;
112 $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL;
113
114 if ($verbosity > $this->getVerbosity()) {
115 return;
116 }
117
118 foreach ($messages as $message) {
119 switch ($type) {
120 case OutputInterface::OUTPUT_NORMAL:
121 $message = $this->formatter->format($message);
122 break;
123 case OutputInterface::OUTPUT_RAW:
124 break;
125 case OutputInterface::OUTPUT_PLAIN:
126 $message = strip_tags($this->formatter->format($message));
127 break;
128 }
129
130 $this->doWrite($message ?? '', $newline);
131 }
132 }
133
134 /**
135 * Writes a message to the output.
136 */
137 abstract protected function doWrite(string $message, bool $newline): void;
138}
diff --git a/vendor/symfony/console/Output/OutputInterface.php b/vendor/symfony/console/Output/OutputInterface.php
new file mode 100644
index 0000000..41315fb
--- /dev/null
+++ b/vendor/symfony/console/Output/OutputInterface.php
@@ -0,0 +1,100 @@
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\Output;
13
14use Symfony\Component\Console\Formatter\OutputFormatterInterface;
15
16/**
17 * OutputInterface is the interface implemented by all Output classes.
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 */
21interface OutputInterface
22{
23 public const VERBOSITY_QUIET = 16;
24 public const VERBOSITY_NORMAL = 32;
25 public const VERBOSITY_VERBOSE = 64;
26 public const VERBOSITY_VERY_VERBOSE = 128;
27 public const VERBOSITY_DEBUG = 256;
28
29 public const OUTPUT_NORMAL = 1;
30 public const OUTPUT_RAW = 2;
31 public const OUTPUT_PLAIN = 4;
32
33 /**
34 * Writes a message to the output.
35 *
36 * @param bool $newline Whether to add a newline
37 * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants),
38 * 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
39 */
40 public function write(string|iterable $messages, bool $newline = false, int $options = 0): void;
41
42 /**
43 * Writes a message to the output and adds a newline at the end.
44 *
45 * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants),
46 * 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
47 */
48 public function writeln(string|iterable $messages, int $options = 0): void;
49
50 /**
51 * Sets the verbosity of the output.
52 *
53 * @param self::VERBOSITY_* $level
54 */
55 public function setVerbosity(int $level): void;
56
57 /**
58 * Gets the current verbosity of the output.
59 *
60 * @return self::VERBOSITY_*
61 */
62 public function getVerbosity(): int;
63
64 /**
65 * Returns whether verbosity is quiet (-q).
66 */
67 public function isQuiet(): bool;
68
69 /**
70 * Returns whether verbosity is verbose (-v).
71 */
72 public function isVerbose(): bool;
73
74 /**
75 * Returns whether verbosity is very verbose (-vv).
76 */
77 public function isVeryVerbose(): bool;
78
79 /**
80 * Returns whether verbosity is debug (-vvv).
81 */
82 public function isDebug(): bool;
83
84 /**
85 * Sets the decorated flag.
86 */
87 public function setDecorated(bool $decorated): void;
88
89 /**
90 * Gets the decorated flag.
91 */
92 public function isDecorated(): bool;
93
94 public function setFormatter(OutputFormatterInterface $formatter): void;
95
96 /**
97 * Returns current output formatter instance.
98 */
99 public function getFormatter(): OutputFormatterInterface;
100}
diff --git a/vendor/symfony/console/Output/StreamOutput.php b/vendor/symfony/console/Output/StreamOutput.php
new file mode 100644
index 0000000..b46f4d2
--- /dev/null
+++ b/vendor/symfony/console/Output/StreamOutput.php
@@ -0,0 +1,122 @@
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\Output;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Component\Console\Formatter\OutputFormatterInterface;
16
17/**
18 * StreamOutput writes the output to a given stream.
19 *
20 * Usage:
21 *
22 * $output = new StreamOutput(fopen('php://stdout', 'w'));
23 *
24 * As `StreamOutput` can use any stream, you can also use a file:
25 *
26 * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false));
27 *
28 * @author Fabien Potencier <fabien@symfony.com>
29 */
30class StreamOutput extends Output
31{
32 /** @var resource */
33 private $stream;
34
35 /**
36 * @param resource $stream A stream resource
37 * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
38 * @param bool|null $decorated Whether to decorate messages (null for auto-guessing)
39 * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
40 *
41 * @throws InvalidArgumentException When first argument is not a real stream
42 */
43 public function __construct($stream, int $verbosity = self::VERBOSITY_NORMAL, ?bool $decorated = null, ?OutputFormatterInterface $formatter = null)
44 {
45 if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) {
46 throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
47 }
48
49 $this->stream = $stream;
50
51 $decorated ??= $this->hasColorSupport();
52
53 parent::__construct($verbosity, $decorated, $formatter);
54 }
55
56 /**
57 * Gets the stream attached to this StreamOutput instance.
58 *
59 * @return resource
60 */
61 public function getStream()
62 {
63 return $this->stream;
64 }
65
66 protected function doWrite(string $message, bool $newline): void
67 {
68 if ($newline) {
69 $message .= \PHP_EOL;
70 }
71
72 @fwrite($this->stream, $message);
73
74 fflush($this->stream);
75 }
76
77 /**
78 * Returns true if the stream supports colorization.
79 *
80 * Colorization is disabled if not supported by the stream:
81 *
82 * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo
83 * terminals via named pipes, so we can only check the environment.
84 *
85 * Reference: Composer\XdebugHandler\Process::supportsColor
86 * https://github.com/composer/xdebug-handler
87 *
88 * @return bool true if the stream supports colorization, false otherwise
89 */
90 protected function hasColorSupport(): bool
91 {
92 // Follow https://no-color.org/
93 if ('' !== ($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR') ?: '')) {
94 return false;
95 }
96
97 // Detect msysgit/mingw and assume this is a tty because detection
98 // does not work correctly, see https://github.com/composer/composer/issues/9690
99 if (!@stream_isatty($this->stream) && !\in_array(strtoupper((string) getenv('MSYSTEM')), ['MINGW32', 'MINGW64'], true)) {
100 return false;
101 }
102
103 if ('\\' === \DIRECTORY_SEPARATOR && @sapi_windows_vt100_support($this->stream)) {
104 return true;
105 }
106
107 if ('Hyper' === getenv('TERM_PROGRAM')
108 || false !== getenv('COLORTERM')
109 || false !== getenv('ANSICON')
110 || 'ON' === getenv('ConEmuANSI')
111 ) {
112 return true;
113 }
114
115 if ('dumb' === $term = (string) getenv('TERM')) {
116 return false;
117 }
118
119 // See https://github.com/chalk/supports-color/blob/d4f413efaf8da045c5ab440ed418ef02dbb28bf1/index.js#L157
120 return preg_match('/^((screen|xterm|vt100|vt220|putty|rxvt|ansi|cygwin|linux).*)|(.*-256(color)?(-bce)?)$/', $term);
121 }
122}
diff --git a/vendor/symfony/console/Output/TrimmedBufferOutput.php b/vendor/symfony/console/Output/TrimmedBufferOutput.php
new file mode 100644
index 0000000..c1862a2
--- /dev/null
+++ b/vendor/symfony/console/Output/TrimmedBufferOutput.php
@@ -0,0 +1,58 @@
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\Output;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Component\Console\Formatter\OutputFormatterInterface;
16
17/**
18 * A BufferedOutput that keeps only the last N chars.
19 *
20 * @author Jérémy Derussé <jeremy@derusse.com>
21 */
22class TrimmedBufferOutput extends Output
23{
24 private int $maxLength;
25 private string $buffer = '';
26
27 public function __construct(int $maxLength, ?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, ?OutputFormatterInterface $formatter = null)
28 {
29 if ($maxLength <= 0) {
30 throw new InvalidArgumentException(sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength));
31 }
32
33 parent::__construct($verbosity, $decorated, $formatter);
34 $this->maxLength = $maxLength;
35 }
36
37 /**
38 * Empties buffer and returns its content.
39 */
40 public function fetch(): string
41 {
42 $content = $this->buffer;
43 $this->buffer = '';
44
45 return $content;
46 }
47
48 protected function doWrite(string $message, bool $newline): void
49 {
50 $this->buffer .= $message;
51
52 if ($newline) {
53 $this->buffer .= \PHP_EOL;
54 }
55
56 $this->buffer = substr($this->buffer, 0 - $this->maxLength);
57 }
58}
diff --git a/vendor/symfony/console/Question/ChoiceQuestion.php b/vendor/symfony/console/Question/ChoiceQuestion.php
new file mode 100644
index 0000000..0ccad05
--- /dev/null
+++ b/vendor/symfony/console/Question/ChoiceQuestion.php
@@ -0,0 +1,178 @@
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\Question;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15
16/**
17 * Represents a choice question.
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 */
21class ChoiceQuestion extends Question
22{
23 private bool $multiselect = false;
24 private string $prompt = ' > ';
25 private string $errorMessage = 'Value "%s" is invalid';
26
27 /**
28 * @param string $question The question to ask to the user
29 * @param array $choices The list of available choices
30 * @param mixed $default The default answer to return
31 */
32 public function __construct(
33 string $question,
34 private array $choices,
35 mixed $default = null,
36 ) {
37 if (!$choices) {
38 throw new \LogicException('Choice question must have at least 1 choice available.');
39 }
40
41 parent::__construct($question, $default);
42
43 $this->setValidator($this->getDefaultValidator());
44 $this->setAutocompleterValues($choices);
45 }
46
47 /**
48 * Returns available choices.
49 */
50 public function getChoices(): array
51 {
52 return $this->choices;
53 }
54
55 /**
56 * Sets multiselect option.
57 *
58 * When multiselect is set to true, multiple choices can be answered.
59 *
60 * @return $this
61 */
62 public function setMultiselect(bool $multiselect): static
63 {
64 $this->multiselect = $multiselect;
65 $this->setValidator($this->getDefaultValidator());
66
67 return $this;
68 }
69
70 /**
71 * Returns whether the choices are multiselect.
72 */
73 public function isMultiselect(): bool
74 {
75 return $this->multiselect;
76 }
77
78 /**
79 * Gets the prompt for choices.
80 */
81 public function getPrompt(): string
82 {
83 return $this->prompt;
84 }
85
86 /**
87 * Sets the prompt for choices.
88 *
89 * @return $this
90 */
91 public function setPrompt(string $prompt): static
92 {
93 $this->prompt = $prompt;
94
95 return $this;
96 }
97
98 /**
99 * Sets the error message for invalid values.
100 *
101 * The error message has a string placeholder (%s) for the invalid value.
102 *
103 * @return $this
104 */
105 public function setErrorMessage(string $errorMessage): static
106 {
107 $this->errorMessage = $errorMessage;
108 $this->setValidator($this->getDefaultValidator());
109
110 return $this;
111 }
112
113 private function getDefaultValidator(): callable
114 {
115 $choices = $this->choices;
116 $errorMessage = $this->errorMessage;
117 $multiselect = $this->multiselect;
118 $isAssoc = $this->isAssoc($choices);
119
120 return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) {
121 if ($multiselect) {
122 // Check for a separated comma values
123 if (!preg_match('/^[^,]+(?:,[^,]+)*$/', (string) $selected, $matches)) {
124 throw new InvalidArgumentException(sprintf($errorMessage, $selected));
125 }
126
127 $selectedChoices = explode(',', (string) $selected);
128 } else {
129 $selectedChoices = [$selected];
130 }
131
132 if ($this->isTrimmable()) {
133 foreach ($selectedChoices as $k => $v) {
134 $selectedChoices[$k] = trim((string) $v);
135 }
136 }
137
138 $multiselectChoices = [];
139 foreach ($selectedChoices as $value) {
140 $results = [];
141 foreach ($choices as $key => $choice) {
142 if ($choice === $value) {
143 $results[] = $key;
144 }
145 }
146
147 if (\count($results) > 1) {
148 throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of "%s".', implode('" or "', $results)));
149 }
150
151 $result = array_search($value, $choices);
152
153 if (!$isAssoc) {
154 if (false !== $result) {
155 $result = $choices[$result];
156 } elseif (isset($choices[$value])) {
157 $result = $choices[$value];
158 }
159 } elseif (false === $result && isset($choices[$value])) {
160 $result = $value;
161 }
162
163 if (false === $result) {
164 throw new InvalidArgumentException(sprintf($errorMessage, $value));
165 }
166
167 // For associative choices, consistently return the key as string:
168 $multiselectChoices[] = $isAssoc ? (string) $result : $result;
169 }
170
171 if ($multiselect) {
172 return $multiselectChoices;
173 }
174
175 return current($multiselectChoices);
176 };
177 }
178}
diff --git a/vendor/symfony/console/Question/ConfirmationQuestion.php b/vendor/symfony/console/Question/ConfirmationQuestion.php
new file mode 100644
index 0000000..951d681
--- /dev/null
+++ b/vendor/symfony/console/Question/ConfirmationQuestion.php
@@ -0,0 +1,57 @@
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\Question;
13
14/**
15 * Represents a yes/no question.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 */
19class ConfirmationQuestion extends Question
20{
21 /**
22 * @param string $question The question to ask to the user
23 * @param bool $default The default answer to return, true or false
24 * @param string $trueAnswerRegex A regex to match the "yes" answer
25 */
26 public function __construct(
27 string $question,
28 bool $default = true,
29 private string $trueAnswerRegex = '/^y/i',
30 ) {
31 parent::__construct($question, $default);
32
33 $this->setNormalizer($this->getDefaultNormalizer());
34 }
35
36 /**
37 * Returns the default answer normalizer.
38 */
39 private function getDefaultNormalizer(): callable
40 {
41 $default = $this->getDefault();
42 $regex = $this->trueAnswerRegex;
43
44 return function ($answer) use ($default, $regex) {
45 if (\is_bool($answer)) {
46 return $answer;
47 }
48
49 $answerIsTrue = (bool) preg_match($regex, $answer);
50 if (false === $default) {
51 return $answer && $answerIsTrue;
52 }
53
54 return '' === $answer || $answerIsTrue;
55 };
56 }
57}
diff --git a/vendor/symfony/console/Question/Question.php b/vendor/symfony/console/Question/Question.php
new file mode 100644
index 0000000..46a60c7
--- /dev/null
+++ b/vendor/symfony/console/Question/Question.php
@@ -0,0 +1,280 @@
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\Question;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Component\Console\Exception\LogicException;
16
17/**
18 * Represents a Question.
19 *
20 * @author Fabien Potencier <fabien@symfony.com>
21 */
22class Question
23{
24 private ?int $attempts = null;
25 private bool $hidden = false;
26 private bool $hiddenFallback = true;
27 private ?\Closure $autocompleterCallback = null;
28 private ?\Closure $validator = null;
29 private ?\Closure $normalizer = null;
30 private bool $trimmable = true;
31 private bool $multiline = false;
32
33 /**
34 * @param string $question The question to ask to the user
35 * @param string|bool|int|float|null $default The default answer to return if the user enters nothing
36 */
37 public function __construct(
38 private string $question,
39 private string|bool|int|float|null $default = null,
40 ) {
41 }
42
43 /**
44 * Returns the question.
45 */
46 public function getQuestion(): string
47 {
48 return $this->question;
49 }
50
51 /**
52 * Returns the default answer.
53 */
54 public function getDefault(): string|bool|int|float|null
55 {
56 return $this->default;
57 }
58
59 /**
60 * Returns whether the user response accepts newline characters.
61 */
62 public function isMultiline(): bool
63 {
64 return $this->multiline;
65 }
66
67 /**
68 * Sets whether the user response should accept newline characters.
69 *
70 * @return $this
71 */
72 public function setMultiline(bool $multiline): static
73 {
74 $this->multiline = $multiline;
75
76 return $this;
77 }
78
79 /**
80 * Returns whether the user response must be hidden.
81 */
82 public function isHidden(): bool
83 {
84 return $this->hidden;
85 }
86
87 /**
88 * Sets whether the user response must be hidden or not.
89 *
90 * @return $this
91 *
92 * @throws LogicException In case the autocompleter is also used
93 */
94 public function setHidden(bool $hidden): static
95 {
96 if ($this->autocompleterCallback) {
97 throw new LogicException('A hidden question cannot use the autocompleter.');
98 }
99
100 $this->hidden = $hidden;
101
102 return $this;
103 }
104
105 /**
106 * In case the response cannot be hidden, whether to fallback on non-hidden question or not.
107 */
108 public function isHiddenFallback(): bool
109 {
110 return $this->hiddenFallback;
111 }
112
113 /**
114 * Sets whether to fallback on non-hidden question if the response cannot be hidden.
115 *
116 * @return $this
117 */
118 public function setHiddenFallback(bool $fallback): static
119 {
120 $this->hiddenFallback = $fallback;
121
122 return $this;
123 }
124
125 /**
126 * Gets values for the autocompleter.
127 */
128 public function getAutocompleterValues(): ?iterable
129 {
130 $callback = $this->getAutocompleterCallback();
131
132 return $callback ? $callback('') : null;
133 }
134
135 /**
136 * Sets values for the autocompleter.
137 *
138 * @return $this
139 *
140 * @throws LogicException
141 */
142 public function setAutocompleterValues(?iterable $values): static
143 {
144 if (\is_array($values)) {
145 $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);
146
147 $callback = static fn () => $values;
148 } elseif ($values instanceof \Traversable) {
149 $callback = static function () use ($values) {
150 static $valueCache;
151
152 return $valueCache ??= iterator_to_array($values, false);
153 };
154 } else {
155 $callback = null;
156 }
157
158 return $this->setAutocompleterCallback($callback);
159 }
160
161 /**
162 * Gets the callback function used for the autocompleter.
163 */
164 public function getAutocompleterCallback(): ?callable
165 {
166 return $this->autocompleterCallback;
167 }
168
169 /**
170 * Sets the callback function used for the autocompleter.
171 *
172 * The callback is passed the user input as argument and should return an iterable of corresponding suggestions.
173 *
174 * @return $this
175 */
176 public function setAutocompleterCallback(?callable $callback): static
177 {
178 if ($this->hidden && null !== $callback) {
179 throw new LogicException('A hidden question cannot use the autocompleter.');
180 }
181
182 $this->autocompleterCallback = null === $callback ? null : $callback(...);
183
184 return $this;
185 }
186
187 /**
188 * Sets a validator for the question.
189 *
190 * @return $this
191 */
192 public function setValidator(?callable $validator): static
193 {
194 $this->validator = null === $validator ? null : $validator(...);
195
196 return $this;
197 }
198
199 /**
200 * Gets the validator for the question.
201 */
202 public function getValidator(): ?callable
203 {
204 return $this->validator;
205 }
206
207 /**
208 * Sets the maximum number of attempts.
209 *
210 * Null means an unlimited number of attempts.
211 *
212 * @return $this
213 *
214 * @throws InvalidArgumentException in case the number of attempts is invalid
215 */
216 public function setMaxAttempts(?int $attempts): static
217 {
218 if (null !== $attempts && $attempts < 1) {
219 throw new InvalidArgumentException('Maximum number of attempts must be a positive value.');
220 }
221
222 $this->attempts = $attempts;
223
224 return $this;
225 }
226
227 /**
228 * Gets the maximum number of attempts.
229 *
230 * Null means an unlimited number of attempts.
231 */
232 public function getMaxAttempts(): ?int
233 {
234 return $this->attempts;
235 }
236
237 /**
238 * Sets a normalizer for the response.
239 *
240 * The normalizer can be a callable (a string), a closure or a class implementing __invoke.
241 *
242 * @return $this
243 */
244 public function setNormalizer(callable $normalizer): static
245 {
246 $this->normalizer = $normalizer(...);
247
248 return $this;
249 }
250
251 /**
252 * Gets the normalizer for the response.
253 *
254 * The normalizer can ba a callable (a string), a closure or a class implementing __invoke.
255 */
256 public function getNormalizer(): ?callable
257 {
258 return $this->normalizer;
259 }
260
261 protected function isAssoc(array $array): bool
262 {
263 return (bool) \count(array_filter(array_keys($array), 'is_string'));
264 }
265
266 public function isTrimmable(): bool
267 {
268 return $this->trimmable;
269 }
270
271 /**
272 * @return $this
273 */
274 public function setTrimmable(bool $trimmable): static
275 {
276 $this->trimmable = $trimmable;
277
278 return $this;
279 }
280}
diff --git a/vendor/symfony/console/README.md b/vendor/symfony/console/README.md
new file mode 100644
index 0000000..92f70e7
--- /dev/null
+++ b/vendor/symfony/console/README.md
@@ -0,0 +1,27 @@
1Console Component
2=================
3
4The Console component eases the creation of beautiful and testable command line
5interfaces.
6
7Sponsor
8-------
9
10Help Symfony by [sponsoring][1] its development!
11
12Resources
13---------
14
15 * [Documentation](https://symfony.com/doc/current/components/console.html)
16 * [Contributing](https://symfony.com/doc/current/contributing/index.html)
17 * [Report issues](https://github.com/symfony/symfony/issues) and
18 [send Pull Requests](https://github.com/symfony/symfony/pulls)
19 in the [main Symfony repository](https://github.com/symfony/symfony)
20
21Credits
22-------
23
24`Resources/bin/hiddeninput.exe` is a third party binary provided within this
25component. Find sources and license at https://github.com/Seldaek/hidden-input.
26
27[1]: https://symfony.com/sponsor
diff --git a/vendor/symfony/console/Resources/bin/hiddeninput.exe b/vendor/symfony/console/Resources/bin/hiddeninput.exe
new file mode 100644
index 0000000..c8cf65e
--- /dev/null
+++ b/vendor/symfony/console/Resources/bin/hiddeninput.exe
Binary files differ
diff --git a/vendor/symfony/console/Resources/completion.bash b/vendor/symfony/console/Resources/completion.bash
new file mode 100644
index 0000000..0d76eac
--- /dev/null
+++ b/vendor/symfony/console/Resources/completion.bash
@@ -0,0 +1,94 @@
1# This file is part of the Symfony package.
2#
3# (c) Fabien Potencier <fabien@symfony.com>
4#
5# For the full copyright and license information, please view
6# https://symfony.com/doc/current/contributing/code/license.html
7
8_sf_{{ COMMAND_NAME }}() {
9
10 # Use the default completion for shell redirect operators.
11 for w in '>' '>>' '&>' '<'; do
12 if [[ $w = "${COMP_WORDS[COMP_CWORD-1]}" ]]; then
13 compopt -o filenames
14 COMPREPLY=($(compgen -f -- "${COMP_WORDS[COMP_CWORD]}"))
15 return 0
16 fi
17 done
18
19 # Use newline as only separator to allow space in completion values
20 IFS=$'\n'
21 local sf_cmd="${COMP_WORDS[0]}"
22
23 # for an alias, get the real script behind it
24 sf_cmd_type=$(type -t $sf_cmd)
25 if [[ $sf_cmd_type == "alias" ]]; then
26 sf_cmd=$(alias $sf_cmd | sed -E "s/alias $sf_cmd='(.*)'/\1/")
27 elif [[ $sf_cmd_type == "file" ]]; then
28 sf_cmd=$(type -p $sf_cmd)
29 fi
30
31 if [[ $sf_cmd_type != "function" && ! -x $sf_cmd ]]; then
32 return 1
33 fi
34
35 local cur prev words cword
36 _get_comp_words_by_ref -n := cur prev words cword
37
38 local completecmd=("$sf_cmd" "_complete" "--no-interaction" "-sbash" "-c$cword" "-a{{ VERSION }}")
39 for w in ${words[@]}; do
40 w=$(printf -- '%b' "$w")
41 # remove quotes from typed values
42 quote="${w:0:1}"
43 if [ "$quote" == \' ]; then
44 w="${w%\'}"
45 w="${w#\'}"
46 elif [ "$quote" == \" ]; then
47 w="${w%\"}"
48 w="${w#\"}"
49 fi
50 # empty values are ignored
51 if [ ! -z "$w" ]; then
52 completecmd+=("-i$w")
53 fi
54 done
55
56 local sfcomplete
57 if sfcomplete=$(${completecmd[@]} 2>&1); then
58 local quote suggestions
59 quote=${cur:0:1}
60
61 # Use single quotes by default if suggestions contains backslash (FQCN)
62 if [ "$quote" == '' ] && [[ "$sfcomplete" =~ \\ ]]; then
63 quote=\'
64 fi
65
66 if [ "$quote" == \' ]; then
67 # single quotes: no additional escaping (does not accept ' in values)
68 suggestions=$(for s in $sfcomplete; do printf $'%q%q%q\n' "$quote" "$s" "$quote"; done)
69 elif [ "$quote" == \" ]; then
70 # double quotes: double escaping for \ $ ` "
71 suggestions=$(for s in $sfcomplete; do
72 s=${s//\\/\\\\}
73 s=${s//\$/\\\$}
74 s=${s//\`/\\\`}
75 s=${s//\"/\\\"}
76 printf $'%q%q%q\n' "$quote" "$s" "$quote";
77 done)
78 else
79 # no quotes: double escaping
80 suggestions=$(for s in $sfcomplete; do printf $'%q\n' $(printf '%q' "$s"); done)
81 fi
82 COMPREPLY=($(IFS=$'\n' compgen -W "$suggestions" -- $(printf -- "%q" "$cur")))
83 __ltrim_colon_completions "$cur"
84 else
85 if [[ "$sfcomplete" != *"Command \"_complete\" is not defined."* ]]; then
86 >&2 echo
87 >&2 echo $sfcomplete
88 fi
89
90 return 1
91 fi
92}
93
94complete -F _sf_{{ COMMAND_NAME }} {{ COMMAND_NAME }}
diff --git a/vendor/symfony/console/Resources/completion.fish b/vendor/symfony/console/Resources/completion.fish
new file mode 100644
index 0000000..1853dd8
--- /dev/null
+++ b/vendor/symfony/console/Resources/completion.fish
@@ -0,0 +1,25 @@
1# This file is part of the Symfony package.
2#
3# (c) Fabien Potencier <fabien@symfony.com>
4#
5# For the full copyright and license information, please view
6# https://symfony.com/doc/current/contributing/code/license.html
7
8function _sf_{{ COMMAND_NAME }}
9 set sf_cmd (commandline -o)
10 set c (count (commandline -oc))
11
12 set completecmd "$sf_cmd[1]" "_complete" "--no-interaction" "-sfish" "-a{{ VERSION }}"
13
14 for i in $sf_cmd
15 if [ $i != "" ]
16 set completecmd $completecmd "-i$i"
17 end
18 end
19
20 set completecmd $completecmd "-c$c"
21
22 $completecmd
23end
24
25complete -c '{{ COMMAND_NAME }}' -a '(_sf_{{ COMMAND_NAME }})' -f
diff --git a/vendor/symfony/console/Resources/completion.zsh b/vendor/symfony/console/Resources/completion.zsh
new file mode 100644
index 0000000..ff76fe5
--- /dev/null
+++ b/vendor/symfony/console/Resources/completion.zsh
@@ -0,0 +1,82 @@
1#compdef {{ COMMAND_NAME }}
2
3# This file is part of the Symfony package.
4#
5# (c) Fabien Potencier <fabien@symfony.com>
6#
7# For the full copyright and license information, please view
8# https://symfony.com/doc/current/contributing/code/license.html
9
10#
11# zsh completions for {{ COMMAND_NAME }}
12#
13# References:
14# - https://github.com/spf13/cobra/blob/master/zsh_completions.go
15# - https://github.com/symfony/symfony/blob/5.4/src/Symfony/Component/Console/Resources/completion.bash
16#
17_sf_{{ COMMAND_NAME }}() {
18 local lastParam flagPrefix requestComp out comp
19 local -a completions
20
21 # The user could have moved the cursor backwards on the command-line.
22 # We need to trigger completion from the $CURRENT location, so we need
23 # to truncate the command-line ($words) up to the $CURRENT location.
24 # (We cannot use $CURSOR as its value does not work when a command is an alias.)
25 words=("${=words[1,CURRENT]}") lastParam=${words[-1]}
26
27 # For zsh, when completing a flag with an = (e.g., {{ COMMAND_NAME }} -n=<TAB>)
28 # completions must be prefixed with the flag
29 setopt local_options BASH_REMATCH
30 if [[ "${lastParam}" =~ '-.*=' ]]; then
31 # We are dealing with a flag with an =
32 flagPrefix="-P ${BASH_REMATCH}"
33 fi
34
35 # Prepare the command to obtain completions
36 requestComp="${words[0]} ${words[1]} _complete --no-interaction -szsh -a{{ VERSION }} -c$((CURRENT-1))" i=""
37 for w in ${words[@]}; do
38 w=$(printf -- '%b' "$w")
39 # remove quotes from typed values
40 quote="${w:0:1}"
41 if [ "$quote" = \' ]; then
42 w="${w%\'}"
43 w="${w#\'}"
44 elif [ "$quote" = \" ]; then
45 w="${w%\"}"
46 w="${w#\"}"
47 fi
48 # empty values are ignored
49 if [ ! -z "$w" ]; then
50 i="${i}-i${w} "
51 fi
52 done
53
54 # Ensure at least 1 input
55 if [ "${i}" = "" ]; then
56 requestComp="${requestComp} -i\" \""
57 else
58 requestComp="${requestComp} ${i}"
59 fi
60
61 # Use eval to handle any environment variables and such
62 out=$(eval ${requestComp} 2>/dev/null)
63
64 while IFS='\n' read -r comp; do
65 if [ -n "$comp" ]; then
66 # If requested, completions are returned with a description.
67 # The description is preceded by a TAB character.
68 # For zsh's _describe, we need to use a : instead of a TAB.
69 # We first need to escape any : as part of the completion itself.
70 comp=${comp//:/\\:}
71 local tab=$(printf '\t')
72 comp=${comp//$tab/:}
73 completions+=${comp}
74 fi
75 done < <(printf "%s\n" "${out[@]}")
76
77 # Let inbuilt _describe handle completions
78 eval _describe "completions" completions $flagPrefix
79 return $?
80}
81
82compdef _sf_{{ COMMAND_NAME }} {{ COMMAND_NAME }}
diff --git a/vendor/symfony/console/SignalRegistry/SignalMap.php b/vendor/symfony/console/SignalRegistry/SignalMap.php
new file mode 100644
index 0000000..de419bd
--- /dev/null
+++ b/vendor/symfony/console/SignalRegistry/SignalMap.php
@@ -0,0 +1,36 @@
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\SignalRegistry;
13
14/**
15 * @author Grégoire Pineau <lyrixx@lyrixx.info>
16 */
17class SignalMap
18{
19 private static array $map;
20
21 public static function getSignalName(int $signal): ?string
22 {
23 if (!\extension_loaded('pcntl')) {
24 return null;
25 }
26
27 if (!isset(self::$map)) {
28 $r = new \ReflectionExtension('pcntl');
29 $c = $r->getConstants();
30 $map = array_filter($c, fn ($k) => str_starts_with($k, 'SIG') && !str_starts_with($k, 'SIG_'), \ARRAY_FILTER_USE_KEY);
31 self::$map = array_flip($map);
32 }
33
34 return self::$map[$signal] ?? null;
35 }
36}
diff --git a/vendor/symfony/console/SignalRegistry/SignalRegistry.php b/vendor/symfony/console/SignalRegistry/SignalRegistry.php
new file mode 100644
index 0000000..ef2e5f0
--- /dev/null
+++ b/vendor/symfony/console/SignalRegistry/SignalRegistry.php
@@ -0,0 +1,57 @@
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\SignalRegistry;
13
14final class SignalRegistry
15{
16 private array $signalHandlers = [];
17
18 public function __construct()
19 {
20 if (\function_exists('pcntl_async_signals')) {
21 pcntl_async_signals(true);
22 }
23 }
24
25 public function register(int $signal, callable $signalHandler): void
26 {
27 if (!isset($this->signalHandlers[$signal])) {
28 $previousCallback = pcntl_signal_get_handler($signal);
29
30 if (\is_callable($previousCallback)) {
31 $this->signalHandlers[$signal][] = $previousCallback;
32 }
33 }
34
35 $this->signalHandlers[$signal][] = $signalHandler;
36
37 pcntl_signal($signal, $this->handle(...));
38 }
39
40 public static function isSupported(): bool
41 {
42 return \function_exists('pcntl_signal');
43 }
44
45 /**
46 * @internal
47 */
48 public function handle(int $signal): void
49 {
50 $count = \count($this->signalHandlers[$signal]);
51
52 foreach ($this->signalHandlers[$signal] as $i => $signalHandler) {
53 $hasNext = $i !== $count - 1;
54 $signalHandler($signal, $hasNext);
55 }
56 }
57}
diff --git a/vendor/symfony/console/SingleCommandApplication.php b/vendor/symfony/console/SingleCommandApplication.php
new file mode 100644
index 0000000..ff1c172
--- /dev/null
+++ b/vendor/symfony/console/SingleCommandApplication.php
@@ -0,0 +1,72 @@
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;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Input\InputInterface;
16use Symfony\Component\Console\Output\OutputInterface;
17
18/**
19 * @author Grégoire Pineau <lyrixx@lyrixx.info>
20 */
21class SingleCommandApplication extends Command
22{
23 private string $version = 'UNKNOWN';
24 private bool $autoExit = true;
25 private bool $running = false;
26
27 /**
28 * @return $this
29 */
30 public function setVersion(string $version): static
31 {
32 $this->version = $version;
33
34 return $this;
35 }
36
37 /**
38 * @final
39 *
40 * @return $this
41 */
42 public function setAutoExit(bool $autoExit): static
43 {
44 $this->autoExit = $autoExit;
45
46 return $this;
47 }
48
49 public function run(?InputInterface $input = null, ?OutputInterface $output = null): int
50 {
51 if ($this->running) {
52 return parent::run($input, $output);
53 }
54
55 // We use the command name as the application name
56 $application = new Application($this->getName() ?: 'UNKNOWN', $this->version);
57 $application->setAutoExit($this->autoExit);
58 // Fix the usage of the command displayed with "--help"
59 $this->setName($_SERVER['argv'][0]);
60 $application->add($this);
61 $application->setDefaultCommand($this->getName(), true);
62
63 $this->running = true;
64 try {
65 $ret = $application->run($input, $output);
66 } finally {
67 $this->running = false;
68 }
69
70 return $ret ?? 1;
71 }
72}
diff --git a/vendor/symfony/console/Style/OutputStyle.php b/vendor/symfony/console/Style/OutputStyle.php
new file mode 100644
index 0000000..9f62ea3
--- /dev/null
+++ b/vendor/symfony/console/Style/OutputStyle.php
@@ -0,0 +1,109 @@
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\Style;
13
14use Symfony\Component\Console\Formatter\OutputFormatterInterface;
15use Symfony\Component\Console\Helper\ProgressBar;
16use Symfony\Component\Console\Output\ConsoleOutputInterface;
17use Symfony\Component\Console\Output\OutputInterface;
18
19/**
20 * Decorates output to add console style guide helpers.
21 *
22 * @author Kevin Bond <kevinbond@gmail.com>
23 */
24abstract class OutputStyle implements OutputInterface, StyleInterface
25{
26 public function __construct(
27 private OutputInterface $output,
28 ) {
29 }
30
31 public function newLine(int $count = 1): void
32 {
33 $this->output->write(str_repeat(\PHP_EOL, $count));
34 }
35
36 public function createProgressBar(int $max = 0): ProgressBar
37 {
38 return new ProgressBar($this->output, $max);
39 }
40
41 public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL): void
42 {
43 $this->output->write($messages, $newline, $type);
44 }
45
46 public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL): void
47 {
48 $this->output->writeln($messages, $type);
49 }
50
51 public function setVerbosity(int $level): void
52 {
53 $this->output->setVerbosity($level);
54 }
55
56 public function getVerbosity(): int
57 {
58 return $this->output->getVerbosity();
59 }
60
61 public function setDecorated(bool $decorated): void
62 {
63 $this->output->setDecorated($decorated);
64 }
65
66 public function isDecorated(): bool
67 {
68 return $this->output->isDecorated();
69 }
70
71 public function setFormatter(OutputFormatterInterface $formatter): void
72 {
73 $this->output->setFormatter($formatter);
74 }
75
76 public function getFormatter(): OutputFormatterInterface
77 {
78 return $this->output->getFormatter();
79 }
80
81 public function isQuiet(): bool
82 {
83 return $this->output->isQuiet();
84 }
85
86 public function isVerbose(): bool
87 {
88 return $this->output->isVerbose();
89 }
90
91 public function isVeryVerbose(): bool
92 {
93 return $this->output->isVeryVerbose();
94 }
95
96 public function isDebug(): bool
97 {
98 return $this->output->isDebug();
99 }
100
101 protected function getErrorOutput(): OutputInterface
102 {
103 if (!$this->output instanceof ConsoleOutputInterface) {
104 return $this->output;
105 }
106
107 return $this->output->getErrorOutput();
108 }
109}
diff --git a/vendor/symfony/console/Style/StyleInterface.php b/vendor/symfony/console/Style/StyleInterface.php
new file mode 100644
index 0000000..fcc5bc7
--- /dev/null
+++ b/vendor/symfony/console/Style/StyleInterface.php
@@ -0,0 +1,110 @@
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\Style;
13
14/**
15 * Output style helpers.
16 *
17 * @author Kevin Bond <kevinbond@gmail.com>
18 */
19interface StyleInterface
20{
21 /**
22 * Formats a command title.
23 */
24 public function title(string $message): void;
25
26 /**
27 * Formats a section title.
28 */
29 public function section(string $message): void;
30
31 /**
32 * Formats a list.
33 */
34 public function listing(array $elements): void;
35
36 /**
37 * Formats informational text.
38 */
39 public function text(string|array $message): void;
40
41 /**
42 * Formats a success result bar.
43 */
44 public function success(string|array $message): void;
45
46 /**
47 * Formats an error result bar.
48 */
49 public function error(string|array $message): void;
50
51 /**
52 * Formats an warning result bar.
53 */
54 public function warning(string|array $message): void;
55
56 /**
57 * Formats a note admonition.
58 */
59 public function note(string|array $message): void;
60
61 /**
62 * Formats a caution admonition.
63 */
64 public function caution(string|array $message): void;
65
66 /**
67 * Formats a table.
68 */
69 public function table(array $headers, array $rows): void;
70
71 /**
72 * Asks a question.
73 */
74 public function ask(string $question, ?string $default = null, ?callable $validator = null): mixed;
75
76 /**
77 * Asks a question with the user input hidden.
78 */
79 public function askHidden(string $question, ?callable $validator = null): mixed;
80
81 /**
82 * Asks for confirmation.
83 */
84 public function confirm(string $question, bool $default = true): bool;
85
86 /**
87 * Asks a choice question.
88 */
89 public function choice(string $question, array $choices, mixed $default = null): mixed;
90
91 /**
92 * Add newline(s).
93 */
94 public function newLine(int $count = 1): void;
95
96 /**
97 * Starts the progress output.
98 */
99 public function progressStart(int $max = 0): void;
100
101 /**
102 * Advances the progress output X steps.
103 */
104 public function progressAdvance(int $step = 1): void;
105
106 /**
107 * Finishes the progress output.
108 */
109 public function progressFinish(): void;
110}
diff --git a/vendor/symfony/console/Style/SymfonyStyle.php b/vendor/symfony/console/Style/SymfonyStyle.php
new file mode 100644
index 0000000..19ad892
--- /dev/null
+++ b/vendor/symfony/console/Style/SymfonyStyle.php
@@ -0,0 +1,455 @@
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\Style;
13
14use Symfony\Component\Console\Exception\InvalidArgumentException;
15use Symfony\Component\Console\Exception\RuntimeException;
16use Symfony\Component\Console\Formatter\OutputFormatter;
17use Symfony\Component\Console\Helper\Helper;
18use Symfony\Component\Console\Helper\OutputWrapper;
19use Symfony\Component\Console\Helper\ProgressBar;
20use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
21use Symfony\Component\Console\Helper\Table;
22use Symfony\Component\Console\Helper\TableCell;
23use Symfony\Component\Console\Helper\TableSeparator;
24use Symfony\Component\Console\Input\InputInterface;
25use Symfony\Component\Console\Output\ConsoleOutputInterface;
26use Symfony\Component\Console\Output\ConsoleSectionOutput;
27use Symfony\Component\Console\Output\OutputInterface;
28use Symfony\Component\Console\Output\TrimmedBufferOutput;
29use Symfony\Component\Console\Question\ChoiceQuestion;
30use Symfony\Component\Console\Question\ConfirmationQuestion;
31use Symfony\Component\Console\Question\Question;
32use Symfony\Component\Console\Terminal;
33
34/**
35 * Output decorator helpers for the Symfony Style Guide.
36 *
37 * @author Kevin Bond <kevinbond@gmail.com>
38 */
39class SymfonyStyle extends OutputStyle
40{
41 public const MAX_LINE_LENGTH = 120;
42
43 private SymfonyQuestionHelper $questionHelper;
44 private ProgressBar $progressBar;
45 private int $lineLength;
46 private TrimmedBufferOutput $bufferedOutput;
47
48 public function __construct(
49 private InputInterface $input,
50 private OutputInterface $output,
51 ) {
52 $this->bufferedOutput = new TrimmedBufferOutput(\DIRECTORY_SEPARATOR === '\\' ? 4 : 2, $output->getVerbosity(), false, clone $output->getFormatter());
53 // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not.
54 $width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH;
55 $this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);
56
57 parent::__construct($output);
58 }
59
60 /**
61 * Formats a message as a block of text.
62 */
63 public function block(string|array $messages, ?string $type = null, ?string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true): void
64 {
65 $messages = \is_array($messages) ? array_values($messages) : [$messages];
66
67 $this->autoPrependBlock();
68 $this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, $escape));
69 $this->newLine();
70 }
71
72 public function title(string $message): void
73 {
74 $this->autoPrependBlock();
75 $this->writeln([
76 sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
77 sprintf('<comment>%s</>', str_repeat('=', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))),
78 ]);
79 $this->newLine();
80 }
81
82 public function section(string $message): void
83 {
84 $this->autoPrependBlock();
85 $this->writeln([
86 sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
87 sprintf('<comment>%s</>', str_repeat('-', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))),
88 ]);
89 $this->newLine();
90 }
91
92 public function listing(array $elements): void
93 {
94 $this->autoPrependText();
95 $elements = array_map(fn ($element) => sprintf(' * %s', $element), $elements);
96
97 $this->writeln($elements);
98 $this->newLine();
99 }
100
101 public function text(string|array $message): void
102 {
103 $this->autoPrependText();
104
105 $messages = \is_array($message) ? array_values($message) : [$message];
106 foreach ($messages as $message) {
107 $this->writeln(sprintf(' %s', $message));
108 }
109 }
110
111 /**
112 * Formats a command comment.
113 */
114 public function comment(string|array $message): void
115 {
116 $this->block($message, null, null, '<fg=default;bg=default> // </>', false, false);
117 }
118
119 public function success(string|array $message): void
120 {
121 $this->block($message, 'OK', 'fg=black;bg=green', ' ', true);
122 }
123
124 public function error(string|array $message): void
125 {
126 $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true);
127 }
128
129 public function warning(string|array $message): void
130 {
131 $this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true);
132 }
133
134 public function note(string|array $message): void
135 {
136 $this->block($message, 'NOTE', 'fg=yellow', ' ! ');
137 }
138
139 /**
140 * Formats an info message.
141 */
142 public function info(string|array $message): void
143 {
144 $this->block($message, 'INFO', 'fg=green', ' ', true);
145 }
146
147 public function caution(string|array $message): void
148 {
149 $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true);
150 }
151
152 public function table(array $headers, array $rows): void
153 {
154 $this->createTable()
155 ->setHeaders($headers)
156 ->setRows($rows)
157 ->render()
158 ;
159
160 $this->newLine();
161 }
162
163 /**
164 * Formats a horizontal table.
165 */
166 public function horizontalTable(array $headers, array $rows): void
167 {
168 $this->createTable()
169 ->setHorizontal(true)
170 ->setHeaders($headers)
171 ->setRows($rows)
172 ->render()
173 ;
174
175 $this->newLine();
176 }
177
178 /**
179 * Formats a list of key/value horizontally.
180 *
181 * Each row can be one of:
182 * * 'A title'
183 * * ['key' => 'value']
184 * * new TableSeparator()
185 */
186 public function definitionList(string|array|TableSeparator ...$list): void
187 {
188 $headers = [];
189 $row = [];
190 foreach ($list as $value) {
191 if ($value instanceof TableSeparator) {
192 $headers[] = $value;
193 $row[] = $value;
194 continue;
195 }
196 if (\is_string($value)) {
197 $headers[] = new TableCell($value, ['colspan' => 2]);
198 $row[] = null;
199 continue;
200 }
201 if (!\is_array($value)) {
202 throw new InvalidArgumentException('Value should be an array, string, or an instance of TableSeparator.');
203 }
204 $headers[] = key($value);
205 $row[] = current($value);
206 }
207
208 $this->horizontalTable($headers, [$row]);
209 }
210
211 public function ask(string $question, ?string $default = null, ?callable $validator = null): mixed
212 {
213 $question = new Question($question, $default);
214 $question->setValidator($validator);
215
216 return $this->askQuestion($question);
217 }
218
219 public function askHidden(string $question, ?callable $validator = null): mixed
220 {
221 $question = new Question($question);
222
223 $question->setHidden(true);
224 $question->setValidator($validator);
225
226 return $this->askQuestion($question);
227 }
228
229 public function confirm(string $question, bool $default = true): bool
230 {
231 return $this->askQuestion(new ConfirmationQuestion($question, $default));
232 }
233
234 public function choice(string $question, array $choices, mixed $default = null, bool $multiSelect = false): mixed
235 {
236 if (null !== $default) {
237 $values = array_flip($choices);
238 $default = $values[$default] ?? $default;
239 }
240
241 $questionChoice = new ChoiceQuestion($question, $choices, $default);
242 $questionChoice->setMultiselect($multiSelect);
243
244 return $this->askQuestion($questionChoice);
245 }
246
247 public function progressStart(int $max = 0): void
248 {
249 $this->progressBar = $this->createProgressBar($max);
250 $this->progressBar->start();
251 }
252
253 public function progressAdvance(int $step = 1): void
254 {
255 $this->getProgressBar()->advance($step);
256 }
257
258 public function progressFinish(): void
259 {
260 $this->getProgressBar()->finish();
261 $this->newLine(2);
262 unset($this->progressBar);
263 }
264
265 public function createProgressBar(int $max = 0): ProgressBar
266 {
267 $progressBar = parent::createProgressBar($max);
268
269 if ('\\' !== \DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) {
270 $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591
271 $progressBar->setProgressCharacter('');
272 $progressBar->setBarCharacter('▓'); // dark shade character \u2593
273 }
274
275 return $progressBar;
276 }
277
278 /**
279 * @see ProgressBar::iterate()
280 *
281 * @template TKey
282 * @template TValue
283 *
284 * @param iterable<TKey, TValue> $iterable
285 * @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable
286 *
287 * @return iterable<TKey, TValue>
288 */
289 public function progressIterate(iterable $iterable, ?int $max = null): iterable
290 {
291 yield from $this->createProgressBar()->iterate($iterable, $max);
292
293 $this->newLine(2);
294 }
295
296 public function askQuestion(Question $question): mixed
297 {
298 if ($this->input->isInteractive()) {
299 $this->autoPrependBlock();
300 }
301
302 $this->questionHelper ??= new SymfonyQuestionHelper();
303
304 $answer = $this->questionHelper->ask($this->input, $this, $question);
305
306 if ($this->input->isInteractive()) {
307 if ($this->output instanceof ConsoleSectionOutput) {
308 // add the new line of the `return` to submit the input to ConsoleSectionOutput, because ConsoleSectionOutput is holding all it's lines.
309 // this is relevant when a `ConsoleSectionOutput::clear` is called.
310 $this->output->addNewLineOfInputSubmit();
311 }
312 $this->newLine();
313 $this->bufferedOutput->write("\n");
314 }
315
316 return $answer;
317 }
318
319 public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL): void
320 {
321 if (!is_iterable($messages)) {
322 $messages = [$messages];
323 }
324
325 foreach ($messages as $message) {
326 parent::writeln($message, $type);
327 $this->writeBuffer($message, true, $type);
328 }
329 }
330
331 public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL): void
332 {
333 if (!is_iterable($messages)) {
334 $messages = [$messages];
335 }
336
337 foreach ($messages as $message) {
338 parent::write($message, $newline, $type);
339 $this->writeBuffer($message, $newline, $type);
340 }
341 }
342
343 public function newLine(int $count = 1): void
344 {
345 parent::newLine($count);
346 $this->bufferedOutput->write(str_repeat("\n", $count));
347 }
348
349 /**
350 * Returns a new instance which makes use of stderr if available.
351 */
352 public function getErrorStyle(): self
353 {
354 return new self($this->input, $this->getErrorOutput());
355 }
356
357 public function createTable(): Table
358 {
359 $output = $this->output instanceof ConsoleOutputInterface ? $this->output->section() : $this->output;
360 $style = clone Table::getStyleDefinition('symfony-style-guide');
361 $style->setCellHeaderFormat('<info>%s</info>');
362
363 return (new Table($output))->setStyle($style);
364 }
365
366 private function getProgressBar(): ProgressBar
367 {
368 return $this->progressBar
369 ?? throw new RuntimeException('The ProgressBar is not started.');
370 }
371
372 private function autoPrependBlock(): void
373 {
374 $chars = substr(str_replace(\PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);
375
376 if (!isset($chars[0])) {
377 $this->newLine(); // empty history, so we should start with a new line.
378
379 return;
380 }
381 // Prepend new line for each non LF chars (This means no blank line was output before)
382 $this->newLine(2 - substr_count($chars, "\n"));
383 }
384
385 private function autoPrependText(): void
386 {
387 $fetched = $this->bufferedOutput->fetch();
388 // Prepend new line if last char isn't EOL:
389 if ($fetched && !str_ends_with($fetched, "\n")) {
390 $this->newLine();
391 }
392 }
393
394 private function writeBuffer(string $message, bool $newLine, int $type): void
395 {
396 // We need to know if the last chars are PHP_EOL
397 $this->bufferedOutput->write($message, $newLine, $type);
398 }
399
400 private function createBlock(iterable $messages, ?string $type = null, ?string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array
401 {
402 $indentLength = 0;
403 $prefixLength = Helper::width(Helper::removeDecoration($this->getFormatter(), $prefix));
404 $lines = [];
405
406 if (null !== $type) {
407 $type = sprintf('[%s] ', $type);
408 $indentLength = Helper::width($type);
409 $lineIndentation = str_repeat(' ', $indentLength);
410 }
411
412 // wrap and add newlines for each element
413 $outputWrapper = new OutputWrapper();
414 foreach ($messages as $key => $message) {
415 if ($escape) {
416 $message = OutputFormatter::escape($message);
417 }
418
419 $lines = array_merge(
420 $lines,
421 explode(\PHP_EOL, $outputWrapper->wrap(
422 $message,
423 $this->lineLength - $prefixLength - $indentLength,
424 \PHP_EOL
425 ))
426 );
427
428 if (\count($messages) > 1 && $key < \count($messages) - 1) {
429 $lines[] = '';
430 }
431 }
432
433 $firstLineIndex = 0;
434 if ($padding && $this->isDecorated()) {
435 $firstLineIndex = 1;
436 array_unshift($lines, '');
437 $lines[] = '';
438 }
439
440 foreach ($lines as $i => &$line) {
441 if (null !== $type) {
442 $line = $firstLineIndex === $i ? $type.$line : $lineIndentation.$line;
443 }
444
445 $line = $prefix.$line;
446 $line .= str_repeat(' ', max($this->lineLength - Helper::width(Helper::removeDecoration($this->getFormatter(), $line)), 0));
447
448 if ($style) {
449 $line = sprintf('<%s>%s</>', $style, $line);
450 }
451 }
452
453 return $lines;
454 }
455}
diff --git a/vendor/symfony/console/Terminal.php b/vendor/symfony/console/Terminal.php
new file mode 100644
index 0000000..9eb16aa
--- /dev/null
+++ b/vendor/symfony/console/Terminal.php
@@ -0,0 +1,228 @@
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;
13
14use Symfony\Component\Console\Output\AnsiColorMode;
15
16class Terminal
17{
18 public const DEFAULT_COLOR_MODE = AnsiColorMode::Ansi4;
19
20 private static ?AnsiColorMode $colorMode = null;
21 private static ?int $width = null;
22 private static ?int $height = null;
23 private static ?bool $stty = null;
24
25 /**
26 * About Ansi color types: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
27 * For more information about true color support with terminals https://github.com/termstandard/colors/.
28 */
29 public static function getColorMode(): AnsiColorMode
30 {
31 // Use Cache from previous run (or user forced mode)
32 if (null !== self::$colorMode) {
33 return self::$colorMode;
34 }
35
36 // Try with $COLORTERM first
37 if (\is_string($colorterm = getenv('COLORTERM'))) {
38 $colorterm = strtolower($colorterm);
39
40 if (str_contains($colorterm, 'truecolor')) {
41 self::setColorMode(AnsiColorMode::Ansi24);
42
43 return self::$colorMode;
44 }
45
46 if (str_contains($colorterm, '256color')) {
47 self::setColorMode(AnsiColorMode::Ansi8);
48
49 return self::$colorMode;
50 }
51 }
52
53 // Try with $TERM
54 if (\is_string($term = getenv('TERM'))) {
55 $term = strtolower($term);
56
57 if (str_contains($term, 'truecolor')) {
58 self::setColorMode(AnsiColorMode::Ansi24);
59
60 return self::$colorMode;
61 }
62
63 if (str_contains($term, '256color')) {
64 self::setColorMode(AnsiColorMode::Ansi8);
65
66 return self::$colorMode;
67 }
68 }
69
70 self::setColorMode(self::DEFAULT_COLOR_MODE);
71
72 return self::$colorMode;
73 }
74
75 /**
76 * Force a terminal color mode rendering.
77 */
78 public static function setColorMode(?AnsiColorMode $colorMode): void
79 {
80 self::$colorMode = $colorMode;
81 }
82
83 /**
84 * Gets the terminal width.
85 */
86 public function getWidth(): int
87 {
88 $width = getenv('COLUMNS');
89 if (false !== $width) {
90 return (int) trim($width);
91 }
92
93 if (null === self::$width) {
94 self::initDimensions();
95 }
96
97 return self::$width ?: 80;
98 }
99
100 /**
101 * Gets the terminal height.
102 */
103 public function getHeight(): int
104 {
105 $height = getenv('LINES');
106 if (false !== $height) {
107 return (int) trim($height);
108 }
109
110 if (null === self::$height) {
111 self::initDimensions();
112 }
113
114 return self::$height ?: 50;
115 }
116
117 /**
118 * @internal
119 */
120 public static function hasSttyAvailable(): bool
121 {
122 if (null !== self::$stty) {
123 return self::$stty;
124 }
125
126 // skip check if shell_exec function is disabled
127 if (!\function_exists('shell_exec')) {
128 return false;
129 }
130
131 return self::$stty = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null'));
132 }
133
134 private static function initDimensions(): void
135 {
136 if ('\\' === \DIRECTORY_SEPARATOR) {
137 $ansicon = getenv('ANSICON');
138 if (false !== $ansicon && preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim($ansicon), $matches)) {
139 // extract [w, H] from "wxh (WxH)"
140 // or [w, h] from "wxh"
141 self::$width = (int) $matches[1];
142 self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2];
143 } elseif (!sapi_windows_vt100_support(fopen('php://stdout', 'w')) && self::hasSttyAvailable()) {
144 // only use stty on Windows if the terminal does not support vt100 (e.g. Windows 7 + git-bash)
145 // testing for stty in a Windows 10 vt100-enabled console will implicitly disable vt100 support on STDOUT
146 self::initDimensionsUsingStty();
147 } elseif (null !== $dimensions = self::getConsoleMode()) {
148 // extract [w, h] from "wxh"
149 self::$width = (int) $dimensions[0];
150 self::$height = (int) $dimensions[1];
151 }
152 } else {
153 self::initDimensionsUsingStty();
154 }
155 }
156
157 /**
158 * Initializes dimensions using the output of an stty columns line.
159 */
160 private static function initDimensionsUsingStty(): void
161 {
162 if ($sttyString = self::getSttyColumns()) {
163 if (preg_match('/rows.(\d+);.columns.(\d+);/is', $sttyString, $matches)) {
164 // extract [w, h] from "rows h; columns w;"
165 self::$width = (int) $matches[2];
166 self::$height = (int) $matches[1];
167 } elseif (preg_match('/;.(\d+).rows;.(\d+).columns/is', $sttyString, $matches)) {
168 // extract [w, h] from "; h rows; w columns"
169 self::$width = (int) $matches[2];
170 self::$height = (int) $matches[1];
171 }
172 }
173 }
174
175 /**
176 * Runs and parses mode CON if it's available, suppressing any error output.
177 *
178 * @return int[]|null An array composed of the width and the height or null if it could not be parsed
179 */
180 private static function getConsoleMode(): ?array
181 {
182 $info = self::readFromProcess('mode CON');
183
184 if (null === $info || !preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
185 return null;
186 }
187
188 return [(int) $matches[2], (int) $matches[1]];
189 }
190
191 /**
192 * Runs and parses stty -a if it's available, suppressing any error output.
193 */
194 private static function getSttyColumns(): ?string
195 {
196 return self::readFromProcess(['stty', '-a']);
197 }
198
199 private static function readFromProcess(string|array $command): ?string
200 {
201 if (!\function_exists('proc_open')) {
202 return null;
203 }
204
205 $descriptorspec = [
206 1 => ['pipe', 'w'],
207 2 => ['pipe', 'w'],
208 ];
209
210 $cp = \function_exists('sapi_windows_cp_set') ? sapi_windows_cp_get() : 0;
211
212 $process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
213 if (!\is_resource($process)) {
214 return null;
215 }
216
217 $info = stream_get_contents($pipes[1]);
218 fclose($pipes[1]);
219 fclose($pipes[2]);
220 proc_close($process);
221
222 if ($cp) {
223 sapi_windows_cp_set($cp);
224 }
225
226 return $info;
227 }
228}
diff --git a/vendor/symfony/console/Tester/ApplicationTester.php b/vendor/symfony/console/Tester/ApplicationTester.php
new file mode 100644
index 0000000..cebb6f8
--- /dev/null
+++ b/vendor/symfony/console/Tester/ApplicationTester.php
@@ -0,0 +1,83 @@
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\Tester;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Input\ArrayInput;
16
17/**
18 * Eases the testing of console applications.
19 *
20 * When testing an application, don't forget to disable the auto exit flag:
21 *
22 * $application = new Application();
23 * $application->setAutoExit(false);
24 *
25 * @author Fabien Potencier <fabien@symfony.com>
26 */
27class ApplicationTester
28{
29 use TesterTrait;
30
31 public function __construct(
32 private Application $application,
33 ) {
34 }
35
36 /**
37 * Executes the application.
38 *
39 * Available options:
40 *
41 * * interactive: Sets the input interactive flag
42 * * decorated: Sets the output decorated flag
43 * * verbosity: Sets the output verbosity flag
44 * * capture_stderr_separately: Make output of stdOut and stdErr separately available
45 *
46 * @return int The command exit code
47 */
48 public function run(array $input, array $options = []): int
49 {
50 $prevShellVerbosity = getenv('SHELL_VERBOSITY');
51
52 try {
53 $this->input = new ArrayInput($input);
54 if (isset($options['interactive'])) {
55 $this->input->setInteractive($options['interactive']);
56 }
57
58 if ($this->inputs) {
59 $this->input->setStream(self::createStream($this->inputs));
60 }
61
62 $this->initOutput($options);
63
64 return $this->statusCode = $this->application->run($this->input, $this->output);
65 } finally {
66 // SHELL_VERBOSITY is set by Application::configureIO so we need to unset/reset it
67 // to its previous value to avoid one test's verbosity to spread to the following tests
68 if (false === $prevShellVerbosity) {
69 if (\function_exists('putenv')) {
70 @putenv('SHELL_VERBOSITY');
71 }
72 unset($_ENV['SHELL_VERBOSITY']);
73 unset($_SERVER['SHELL_VERBOSITY']);
74 } else {
75 if (\function_exists('putenv')) {
76 @putenv('SHELL_VERBOSITY='.$prevShellVerbosity);
77 }
78 $_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity;
79 $_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity;
80 }
81 }
82 }
83}
diff --git a/vendor/symfony/console/Tester/CommandCompletionTester.php b/vendor/symfony/console/Tester/CommandCompletionTester.php
new file mode 100644
index 0000000..76cbaf1
--- /dev/null
+++ b/vendor/symfony/console/Tester/CommandCompletionTester.php
@@ -0,0 +1,54 @@
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\Tester;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Completion\CompletionInput;
16use Symfony\Component\Console\Completion\CompletionSuggestions;
17
18/**
19 * Eases the testing of command completion.
20 *
21 * @author Jérôme Tamarelle <jerome@tamarelle.net>
22 */
23class CommandCompletionTester
24{
25 public function __construct(
26 private Command $command,
27 ) {
28 }
29
30 /**
31 * Create completion suggestions from input tokens.
32 */
33 public function complete(array $input): array
34 {
35 $currentIndex = \count($input);
36 if ('' === end($input)) {
37 array_pop($input);
38 }
39 array_unshift($input, $this->command->getName());
40
41 $completionInput = CompletionInput::fromTokens($input, $currentIndex);
42 $completionInput->bind($this->command->getDefinition());
43 $suggestions = new CompletionSuggestions();
44
45 $this->command->complete($completionInput, $suggestions);
46
47 $options = [];
48 foreach ($suggestions->getOptionSuggestions() as $option) {
49 $options[] = '--'.$option->getName();
50 }
51
52 return array_map('strval', array_merge($options, $suggestions->getValueSuggestions()));
53 }
54}
diff --git a/vendor/symfony/console/Tester/CommandTester.php b/vendor/symfony/console/Tester/CommandTester.php
new file mode 100644
index 0000000..d39cde7
--- /dev/null
+++ b/vendor/symfony/console/Tester/CommandTester.php
@@ -0,0 +1,74 @@
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\Tester;
13
14use Symfony\Component\Console\Command\Command;
15use Symfony\Component\Console\Input\ArrayInput;
16
17/**
18 * Eases the testing of console commands.
19 *
20 * @author Fabien Potencier <fabien@symfony.com>
21 * @author Robin Chalas <robin.chalas@gmail.com>
22 */
23class CommandTester
24{
25 use TesterTrait;
26
27 public function __construct(
28 private Command $command,
29 ) {
30 }
31
32 /**
33 * Executes the command.
34 *
35 * Available execution options:
36 *
37 * * interactive: Sets the input interactive flag
38 * * decorated: Sets the output decorated flag
39 * * verbosity: Sets the output verbosity flag
40 * * capture_stderr_separately: Make output of stdOut and stdErr separately available
41 *
42 * @param array $input An array of command arguments and options
43 * @param array $options An array of execution options
44 *
45 * @return int The command exit code
46 */
47 public function execute(array $input, array $options = []): int
48 {
49 // set the command name automatically if the application requires
50 // this argument and no command name was passed
51 if (!isset($input['command'])
52 && (null !== $application = $this->command->getApplication())
53 && $application->getDefinition()->hasArgument('command')
54 ) {
55 $input = array_merge(['command' => $this->command->getName()], $input);
56 }
57
58 $this->input = new ArrayInput($input);
59 // Use an in-memory input stream even if no inputs are set so that QuestionHelper::ask() does not rely on the blocking STDIN.
60 $this->input->setStream(self::createStream($this->inputs));
61
62 if (isset($options['interactive'])) {
63 $this->input->setInteractive($options['interactive']);
64 }
65
66 if (!isset($options['decorated'])) {
67 $options['decorated'] = false;
68 }
69
70 $this->initOutput($options);
71
72 return $this->statusCode = $this->command->run($this->input, $this->output);
73 }
74}
diff --git a/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php b/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php
new file mode 100644
index 0000000..09c6194
--- /dev/null
+++ b/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php
@@ -0,0 +1,43 @@
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\Tester\Constraint;
13
14use PHPUnit\Framework\Constraint\Constraint;
15use Symfony\Component\Console\Command\Command;
16
17final class CommandIsSuccessful extends Constraint
18{
19 public function toString(): string
20 {
21 return 'is successful';
22 }
23
24 protected function matches($other): bool
25 {
26 return Command::SUCCESS === $other;
27 }
28
29 protected function failureDescription($other): string
30 {
31 return 'the command '.$this->toString();
32 }
33
34 protected function additionalFailureDescription($other): string
35 {
36 $mapping = [
37 Command::FAILURE => 'Command failed.',
38 Command::INVALID => 'Command was invalid.',
39 ];
40
41 return $mapping[$other] ?? sprintf('Command returned exit status %d.', $other);
42 }
43}
diff --git a/vendor/symfony/console/Tester/TesterTrait.php b/vendor/symfony/console/Tester/TesterTrait.php
new file mode 100644
index 0000000..1ab7a70
--- /dev/null
+++ b/vendor/symfony/console/Tester/TesterTrait.php
@@ -0,0 +1,178 @@
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\Tester;
13
14use PHPUnit\Framework\Assert;
15use Symfony\Component\Console\Input\InputInterface;
16use Symfony\Component\Console\Output\ConsoleOutput;
17use Symfony\Component\Console\Output\OutputInterface;
18use Symfony\Component\Console\Output\StreamOutput;
19use Symfony\Component\Console\Tester\Constraint\CommandIsSuccessful;
20
21/**
22 * @author Amrouche Hamza <hamza.simperfit@gmail.com>
23 */
24trait TesterTrait
25{
26 private StreamOutput $output;
27 private array $inputs = [];
28 private bool $captureStreamsIndependently = false;
29 private InputInterface $input;
30 private int $statusCode;
31
32 /**
33 * Gets the display returned by the last execution of the command or application.
34 *
35 * @throws \RuntimeException If it's called before the execute method
36 */
37 public function getDisplay(bool $normalize = false): string
38 {
39 if (!isset($this->output)) {
40 throw new \RuntimeException('Output not initialized, did you execute the command before requesting the display?');
41 }
42
43 rewind($this->output->getStream());
44
45 $display = stream_get_contents($this->output->getStream());
46
47 if ($normalize) {
48 $display = str_replace(\PHP_EOL, "\n", $display);
49 }
50
51 return $display;
52 }
53
54 /**
55 * Gets the output written to STDERR by the application.
56 *
57 * @param bool $normalize Whether to normalize end of lines to \n or not
58 */
59 public function getErrorOutput(bool $normalize = false): string
60 {
61 if (!$this->captureStreamsIndependently) {
62 throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.');
63 }
64
65 rewind($this->output->getErrorOutput()->getStream());
66
67 $display = stream_get_contents($this->output->getErrorOutput()->getStream());
68
69 if ($normalize) {
70 $display = str_replace(\PHP_EOL, "\n", $display);
71 }
72
73 return $display;
74 }
75
76 /**
77 * Gets the input instance used by the last execution of the command or application.
78 */
79 public function getInput(): InputInterface
80 {
81 return $this->input;
82 }
83
84 /**
85 * Gets the output instance used by the last execution of the command or application.
86 */
87 public function getOutput(): OutputInterface
88 {
89 return $this->output;
90 }
91
92 /**
93 * Gets the status code returned by the last execution of the command or application.
94 *
95 * @throws \RuntimeException If it's called before the execute method
96 */
97 public function getStatusCode(): int
98 {
99 return $this->statusCode ?? throw new \RuntimeException('Status code not initialized, did you execute the command before requesting the status code?');
100 }
101
102 public function assertCommandIsSuccessful(string $message = ''): void
103 {
104 Assert::assertThat($this->statusCode, new CommandIsSuccessful(), $message);
105 }
106
107 /**
108 * Sets the user inputs.
109 *
110 * @param array $inputs An array of strings representing each input
111 * passed to the command input stream
112 *
113 * @return $this
114 */
115 public function setInputs(array $inputs): static
116 {
117 $this->inputs = $inputs;
118
119 return $this;
120 }
121
122 /**
123 * Initializes the output property.
124 *
125 * Available options:
126 *
127 * * decorated: Sets the output decorated flag
128 * * verbosity: Sets the output verbosity flag
129 * * capture_stderr_separately: Make output of stdOut and stdErr separately available
130 */
131 private function initOutput(array $options): void
132 {
133 $this->captureStreamsIndependently = $options['capture_stderr_separately'] ?? false;
134 if (!$this->captureStreamsIndependently) {
135 $this->output = new StreamOutput(fopen('php://memory', 'w', false));
136 if (isset($options['decorated'])) {
137 $this->output->setDecorated($options['decorated']);
138 }
139 if (isset($options['verbosity'])) {
140 $this->output->setVerbosity($options['verbosity']);
141 }
142 } else {
143 $this->output = new ConsoleOutput(
144 $options['verbosity'] ?? ConsoleOutput::VERBOSITY_NORMAL,
145 $options['decorated'] ?? null
146 );
147
148 $errorOutput = new StreamOutput(fopen('php://memory', 'w', false));
149 $errorOutput->setFormatter($this->output->getFormatter());
150 $errorOutput->setVerbosity($this->output->getVerbosity());
151 $errorOutput->setDecorated($this->output->isDecorated());
152
153 $reflectedOutput = new \ReflectionObject($this->output);
154 $strErrProperty = $reflectedOutput->getProperty('stderr');
155 $strErrProperty->setValue($this->output, $errorOutput);
156
157 $reflectedParent = $reflectedOutput->getParentClass();
158 $streamProperty = $reflectedParent->getProperty('stream');
159 $streamProperty->setValue($this->output, fopen('php://memory', 'w', false));
160 }
161 }
162
163 /**
164 * @return resource
165 */
166 private static function createStream(array $inputs)
167 {
168 $stream = fopen('php://memory', 'r+', false);
169
170 foreach ($inputs as $input) {
171 fwrite($stream, $input.\PHP_EOL);
172 }
173
174 rewind($stream);
175
176 return $stream;
177 }
178}
diff --git a/vendor/symfony/console/composer.json b/vendor/symfony/console/composer.json
new file mode 100644
index 0000000..0ed1bd9
--- /dev/null
+++ b/vendor/symfony/console/composer.json
@@ -0,0 +1,54 @@
1{
2 "name": "symfony/console",
3 "type": "library",
4 "description": "Eases the creation of beautiful and testable command line interfaces",
5 "keywords": ["console", "cli", "command-line", "terminal"],
6 "homepage": "https://symfony.com",
7 "license": "MIT",
8 "authors": [
9 {
10 "name": "Fabien Potencier",
11 "email": "fabien@symfony.com"
12 },
13 {
14 "name": "Symfony Community",
15 "homepage": "https://symfony.com/contributors"
16 }
17 ],
18 "require": {
19 "php": ">=8.2",
20 "symfony/polyfill-mbstring": "~1.0",
21 "symfony/service-contracts": "^2.5|^3",
22 "symfony/string": "^6.4|^7.0"
23 },
24 "require-dev": {
25 "symfony/config": "^6.4|^7.0",
26 "symfony/event-dispatcher": "^6.4|^7.0",
27 "symfony/http-foundation": "^6.4|^7.0",
28 "symfony/http-kernel": "^6.4|^7.0",
29 "symfony/dependency-injection": "^6.4|^7.0",
30 "symfony/lock": "^6.4|^7.0",
31 "symfony/messenger": "^6.4|^7.0",
32 "symfony/process": "^6.4|^7.0",
33 "symfony/stopwatch": "^6.4|^7.0",
34 "symfony/var-dumper": "^6.4|^7.0",
35 "psr/log": "^1|^2|^3"
36 },
37 "provide": {
38 "psr/log-implementation": "1.0|2.0|3.0"
39 },
40 "conflict": {
41 "symfony/dependency-injection": "<6.4",
42 "symfony/dotenv": "<6.4",
43 "symfony/event-dispatcher": "<6.4",
44 "symfony/lock": "<6.4",
45 "symfony/process": "<6.4"
46 },
47 "autoload": {
48 "psr-4": { "Symfony\\Component\\Console\\": "" },
49 "exclude-from-classmap": [
50 "/Tests/"
51 ]
52 },
53 "minimum-stability": "dev"
54}