summaryrefslogtreecommitdiff
path: root/vendor/symfony/console/Command/Command.php
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/Command/Command.php
parent94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff)
downloadAppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/symfony/console/Command/Command.php')
-rw-r--r--vendor/symfony/console/Command/Command.php664
1 files changed, 664 insertions, 0 deletions
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}