123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725 |
- <?php
- namespace Symfony\Component\Console\Command;
- use Symfony\Component\Console\Application;
- use Symfony\Component\Console\Attribute\AsCommand;
- use Symfony\Component\Console\Completion\CompletionInput;
- use Symfony\Component\Console\Completion\CompletionSuggestions;
- use Symfony\Component\Console\Completion\Suggestion;
- use Symfony\Component\Console\Exception\ExceptionInterface;
- use Symfony\Component\Console\Exception\InvalidArgumentException;
- use Symfony\Component\Console\Exception\LogicException;
- use Symfony\Component\Console\Helper\HelperInterface;
- use Symfony\Component\Console\Helper\HelperSet;
- use Symfony\Component\Console\Input\InputArgument;
- use Symfony\Component\Console\Input\InputDefinition;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Input\InputOption;
- use Symfony\Component\Console\Output\OutputInterface;
- class Command
- {
-
- public const SUCCESS = 0;
- public const FAILURE = 1;
- public const INVALID = 2;
-
- protected static $defaultName;
-
- protected static $defaultDescription;
- private ?Application $application = null;
- private ?string $name = null;
- private ?string $processTitle = null;
- private array $aliases = [];
- private InputDefinition $definition;
- private bool $hidden = false;
- private string $help = '';
- private string $description = '';
- private ?InputDefinition $fullDefinition = null;
- private bool $ignoreValidationErrors = false;
- private ?\Closure $code = null;
- private array $synopsis = [];
- private array $usages = [];
- private ?HelperSet $helperSet = null;
- public static function getDefaultName(): ?string
- {
- $class = static::class;
- if ($attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) {
- return $attribute[0]->newInstance()->name;
- }
- $r = new \ReflectionProperty($class, 'defaultName');
- if ($class !== $r->class || null === static::$defaultName) {
- return null;
- }
- trigger_deprecation('symfony/console', '6.1', 'Relying on the static property "$defaultName" for setting a command name is deprecated. Add the "%s" attribute to the "%s" class instead.', AsCommand::class, static::class);
- return static::$defaultName;
- }
- public static function getDefaultDescription(): ?string
- {
- $class = static::class;
- if ($attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) {
- return $attribute[0]->newInstance()->description;
- }
- $r = new \ReflectionProperty($class, 'defaultDescription');
- if ($class !== $r->class || null === static::$defaultDescription) {
- return null;
- }
- trigger_deprecation('symfony/console', '6.1', 'Relying on the static property "$defaultDescription" for setting a command description is deprecated. Add the "%s" attribute to the "%s" class instead.', AsCommand::class, static::class);
- return static::$defaultDescription;
- }
-
- public function __construct(?string $name = null)
- {
- $this->definition = new InputDefinition();
- if (null === $name && null !== $name = static::getDefaultName()) {
- $aliases = explode('|', $name);
- if ('' === $name = array_shift($aliases)) {
- $this->setHidden(true);
- $name = array_shift($aliases);
- }
- $this->setAliases($aliases);
- }
- if (null !== $name) {
- $this->setName($name);
- }
- if ('' === $this->description) {
- $this->setDescription(static::getDefaultDescription() ?? '');
- }
- $this->configure();
- }
-
- public function ignoreValidationErrors()
- {
- $this->ignoreValidationErrors = true;
- }
-
- public function setApplication(?Application $application = null)
- {
- if (1 > \func_num_args()) {
- trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__);
- }
- $this->application = $application;
- if ($application) {
- $this->setHelperSet($application->getHelperSet());
- } else {
- $this->helperSet = null;
- }
- $this->fullDefinition = null;
- }
-
- public function setHelperSet(HelperSet $helperSet)
- {
- $this->helperSet = $helperSet;
- }
-
- public function getHelperSet(): ?HelperSet
- {
- return $this->helperSet;
- }
-
- public function getApplication(): ?Application
- {
- return $this->application;
- }
-
- public function isEnabled()
- {
- return true;
- }
-
- protected function configure()
- {
- }
-
- protected function execute(InputInterface $input, OutputInterface $output)
- {
- throw new LogicException('You must override the execute() method in the concrete command class.');
- }
-
- protected function interact(InputInterface $input, OutputInterface $output)
- {
- }
-
- protected function initialize(InputInterface $input, OutputInterface $output)
- {
- }
-
- public function run(InputInterface $input, OutputInterface $output): int
- {
-
- $this->mergeApplicationDefinition();
-
- try {
- $input->bind($this->getDefinition());
- } catch (ExceptionInterface $e) {
- if (!$this->ignoreValidationErrors) {
- throw $e;
- }
- }
- $this->initialize($input, $output);
- if (null !== $this->processTitle) {
- if (\function_exists('cli_set_process_title')) {
- if (!@cli_set_process_title($this->processTitle)) {
- if ('Darwin' === \PHP_OS) {
- $output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
- } else {
- cli_set_process_title($this->processTitle);
- }
- }
- } elseif (\function_exists('setproctitle')) {
- setproctitle($this->processTitle);
- } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
- $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
- }
- }
- if ($input->isInteractive()) {
- $this->interact($input, $output);
- }
-
-
-
- if ($input->hasArgument('command') && null === $input->getArgument('command')) {
- $input->setArgument('command', $this->getName());
- }
- $input->validate();
- if ($this->code) {
- $statusCode = ($this->code)($input, $output);
- } else {
- $statusCode = $this->execute($input, $output);
- if (!\is_int($statusCode)) {
- throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, "%s" returned.', static::class, get_debug_type($statusCode)));
- }
- }
- return is_numeric($statusCode) ? (int) $statusCode : 0;
- }
-
- public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
- {
- $definition = $this->getDefinition();
- if (CompletionInput::TYPE_OPTION_VALUE === $input->getCompletionType() && $definition->hasOption($input->getCompletionName())) {
- $definition->getOption($input->getCompletionName())->complete($input, $suggestions);
- } elseif (CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType() && $definition->hasArgument($input->getCompletionName())) {
- $definition->getArgument($input->getCompletionName())->complete($input, $suggestions);
- }
- }
-
- public function setCode(callable $code): static
- {
- if ($code instanceof \Closure) {
- $r = new \ReflectionFunction($code);
- if (null === $r->getClosureThis()) {
- set_error_handler(static function () {});
- try {
- if ($c = \Closure::bind($code, $this)) {
- $code = $c;
- }
- } finally {
- restore_error_handler();
- }
- }
- } else {
- $code = $code(...);
- }
- $this->code = $code;
- return $this;
- }
-
- public function mergeApplicationDefinition(bool $mergeArgs = true): void
- {
- if (null === $this->application) {
- return;
- }
- $this->fullDefinition = new InputDefinition();
- $this->fullDefinition->setOptions($this->definition->getOptions());
- $this->fullDefinition->addOptions($this->application->getDefinition()->getOptions());
- if ($mergeArgs) {
- $this->fullDefinition->setArguments($this->application->getDefinition()->getArguments());
- $this->fullDefinition->addArguments($this->definition->getArguments());
- } else {
- $this->fullDefinition->setArguments($this->definition->getArguments());
- }
- }
-
- public function setDefinition(array|InputDefinition $definition): static
- {
- if ($definition instanceof InputDefinition) {
- $this->definition = $definition;
- } else {
- $this->definition->setDefinition($definition);
- }
- $this->fullDefinition = null;
- return $this;
- }
-
- public function getDefinition(): InputDefinition
- {
- return $this->fullDefinition ?? $this->getNativeDefinition();
- }
-
- public function getNativeDefinition(): InputDefinition
- {
- return $this->definition ?? throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class));
- }
-
- public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null ): static
- {
- $suggestedValues = 5 <= \func_num_args() ? func_get_arg(4) : [];
- if (!\is_array($suggestedValues) && !$suggestedValues instanceof \Closure) {
- throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be array or \Closure, "%s" given.', __METHOD__, get_debug_type($suggestedValues)));
- }
- $this->definition->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues));
- $this->fullDefinition?->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues));
- return $this;
- }
-
- public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null ): static
- {
- $suggestedValues = 6 <= \func_num_args() ? func_get_arg(5) : [];
- if (!\is_array($suggestedValues) && !$suggestedValues instanceof \Closure) {
- throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be array or \Closure, "%s" given.', __METHOD__, get_debug_type($suggestedValues)));
- }
- $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues));
- $this->fullDefinition?->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues));
- return $this;
- }
-
- public function setName(string $name): static
- {
- $this->validateName($name);
- $this->name = $name;
- return $this;
- }
-
- public function setProcessTitle(string $title): static
- {
- $this->processTitle = $title;
- return $this;
- }
-
- public function getName(): ?string
- {
- return $this->name;
- }
-
- public function setHidden(bool $hidden = true): static
- {
- $this->hidden = $hidden;
- return $this;
- }
-
- public function isHidden(): bool
- {
- return $this->hidden;
- }
-
- public function setDescription(string $description): static
- {
- $this->description = $description;
- return $this;
- }
-
- public function getDescription(): string
- {
- return $this->description;
- }
-
- public function setHelp(string $help): static
- {
- $this->help = $help;
- return $this;
- }
-
- public function getHelp(): string
- {
- return $this->help;
- }
-
- public function getProcessedHelp(): string
- {
- $name = $this->name;
- $isSingleCommand = $this->application?->isSingleCommand();
- $placeholders = [
- '%command.name%',
- '%command.full_name%',
- ];
- $replacements = [
- $name,
- $isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name,
- ];
- return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription());
- }
-
- public function setAliases(iterable $aliases): static
- {
- $list = [];
- foreach ($aliases as $alias) {
- $this->validateName($alias);
- $list[] = $alias;
- }
- $this->aliases = \is_array($aliases) ? $aliases : $list;
- return $this;
- }
-
- public function getAliases(): array
- {
- return $this->aliases;
- }
-
- public function getSynopsis(bool $short = false): string
- {
- $key = $short ? 'short' : 'long';
- if (!isset($this->synopsis[$key])) {
- $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
- }
- return $this->synopsis[$key];
- }
-
- public function addUsage(string $usage): static
- {
- if (!str_starts_with($usage, $this->name)) {
- $usage = sprintf('%s %s', $this->name, $usage);
- }
- $this->usages[] = $usage;
- return $this;
- }
-
- public function getUsages(): array
- {
- return $this->usages;
- }
-
- public function getHelper(string $name): mixed
- {
- if (null === $this->helperSet) {
- 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));
- }
- return $this->helperSet->get($name);
- }
-
- private function validateName(string $name): void
- {
- if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
- throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
- }
- }
- }
|