diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 6c557a777ea78..000921fba4de2 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -45,9 +45,8 @@ class Command private $hidden = false; private $help = ''; private $description = ''; + private $fullDefinition; private $ignoreValidationErrors = false; - private $applicationDefinitionMerged = false; - private $applicationDefinitionMergedWithArgs = false; private $code; private $synopsis = []; private $usages = []; @@ -98,6 +97,8 @@ public function setApplication(Application $application = null) } else { $this->helperSet = null; } + + $this->fullDefinition = null; } public function setHelperSet(HelperSet $helperSet) @@ -205,16 +206,12 @@ protected function initialize(InputInterface $input, OutputInterface $output) */ public function run(InputInterface $input, OutputInterface $output) { - // force the creation of the synopsis before the merge with the app definition - $this->getSynopsis(true); - $this->getSynopsis(false); - // add the application arguments and options $this->mergeApplicationDefinition(); // bind the input against the command specific arguments/options try { - $input->bind($this->definition); + $input->bind($this->getDefinition()); } catch (ExceptionInterface $e) { if (!$this->ignoreValidationErrors) { throw $e; @@ -302,20 +299,19 @@ public function setCode(callable $code) */ public function mergeApplicationDefinition(bool $mergeArgs = true) { - if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) { + if (null === $this->application) { return; } - $this->definition->addOptions($this->application->getDefinition()->getOptions()); - - $this->applicationDefinitionMerged = true; + $this->fullDefinition = new InputDefinition(); + $this->fullDefinition->setOptions($this->definition->getOptions()); + $this->fullDefinition->addOptions($this->application->getDefinition()->getOptions()); if ($mergeArgs) { - $currentArguments = $this->definition->getArguments(); - $this->definition->setArguments($this->application->getDefinition()->getArguments()); - $this->definition->addArguments($currentArguments); - - $this->applicationDefinitionMergedWithArgs = true; + $this->fullDefinition->setArguments($this->application->getDefinition()->getArguments()); + $this->fullDefinition->addArguments($this->definition->getArguments()); + } else { + $this->fullDefinition->setArguments($this->definition->getArguments()); } } @@ -334,7 +330,7 @@ public function setDefinition($definition) $this->definition->setDefinition($definition); } - $this->applicationDefinitionMerged = false; + $this->fullDefinition = null; return $this; } @@ -346,11 +342,7 @@ public function setDefinition($definition) */ public function getDefinition() { - if (null === $this->definition) { - throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class)); - } - - return $this->definition; + return $this->fullDefinition ?? $this->getNativeDefinition(); } /** @@ -365,7 +357,11 @@ public function getDefinition() */ public function getNativeDefinition() { - return $this->getDefinition(); + if (null === $this->definition) { + throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class)); + } + + return $this->definition; } /** @@ -381,6 +377,9 @@ public function getNativeDefinition() public function addArgument(string $name, int $mode = null, string $description = '', $default = null) { $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + if (null !== $this->fullDefinition) { + $this->fullDefinition->addArgument(new InputArgument($name, $mode, $description, $default)); + } return $this; } @@ -399,6 +398,9 @@ public function addArgument(string $name, int $mode = null, string $description public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null) { $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + if (null !== $this->fullDefinition) { + $this->fullDefinition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + } return $this; } diff --git a/src/Symfony/Component/Console/Command/ListCommand.php b/src/Symfony/Component/Console/Command/ListCommand.php index 8af952652872a..2ded1fe33e57d 100644 --- a/src/Symfony/Component/Console/Command/ListCommand.php +++ b/src/Symfony/Component/Console/Command/ListCommand.php @@ -13,7 +13,6 @@ use Symfony\Component\Console\Helper\DescriptorHelper; 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; @@ -32,7 +31,11 @@ protected function configure() { $this ->setName('list') - ->setDefinition($this->createDefinition()) + ->setDefinition([ + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + ]) ->setDescription('Lists commands') ->setHelp(<<<'EOF' The %command.name% command lists all commands: @@ -55,14 +58,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ - public function getNativeDefinition() - { - return $this->createDefinition(); - } - /** * {@inheritdoc} */ @@ -77,13 +72,4 @@ protected function execute(InputInterface $input, OutputInterface $output) return 0; } - - private function createDefinition(): InputDefinition - { - return new InputDefinition([ - new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), - new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), - new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), - ]); - } } diff --git a/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php index 131fef1f1c3b1..ebae9a234e787 100644 --- a/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php @@ -141,7 +141,6 @@ private function getInputDefinitionData(InputDefinition $definition): array private function getCommandData(Command $command): array { - $command->getSynopsis(); $command->mergeApplicationDefinition(false); return [ @@ -149,7 +148,7 @@ private function getCommandData(Command $command): array 'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()), 'description' => $command->getDescription(), 'help' => $command->getProcessedHelp(), - 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()), + 'definition' => $this->getInputDefinitionData($command->getDefinition()), 'hidden' => $command->isHidden(), ]; } diff --git a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php index 8b3e182efc358..483a8338dfc1f 100644 --- a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php @@ -118,7 +118,6 @@ protected function describeInputDefinition(InputDefinition $definition, array $o */ protected function describeCommand(Command $command, array $options = []) { - $command->getSynopsis(); $command->mergeApplicationDefinition(false); $this->write( @@ -136,9 +135,10 @@ protected function describeCommand(Command $command, array $options = []) $this->write($help); } - if ($command->getNativeDefinition()) { + $definition = $command->getDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { $this->write("\n\n"); - $this->describeInputDefinition($command->getNativeDefinition()); + $this->describeInputDefinition($definition); } } diff --git a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php index bde620bfa7f86..cecfba0e66abb 100644 --- a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php @@ -136,8 +136,6 @@ protected function describeInputDefinition(InputDefinition $definition, array $o */ protected function describeCommand(Command $command, array $options = []) { - $command->getSynopsis(true); - $command->getSynopsis(false); $command->mergeApplicationDefinition(false); if ($description = $command->getDescription()) { @@ -154,7 +152,7 @@ protected function describeCommand(Command $command, array $options = []) } $this->writeText("\n"); - $definition = $command->getNativeDefinition(); + $definition = $command->getDefinition(); if ($definition->getOptions() || $definition->getArguments()) { $this->writeText("\n"); $this->describeInputDefinition($definition, $options); diff --git a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php index 3d5dce1d6aff9..24035f5a3b58b 100644 --- a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php @@ -49,7 +49,6 @@ public function getCommandDocument(Command $command): \DOMDocument $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($commandXML = $dom->createElement('command')); - $command->getSynopsis(); $command->mergeApplicationDefinition(false); $commandXML->setAttribute('id', $command->getName()); @@ -68,7 +67,7 @@ public function getCommandDocument(Command $command): \DOMDocument $commandXML->appendChild($helpXML = $dom->createElement('help')); $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); - $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition()); + $definitionXML = $this->getInputDefinitionDocument($command->getDefinition()); $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); return $dom; diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 3b203327424de..104c0af9b3611 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -1009,6 +1009,12 @@ public function testRun() $tester->run(['command' => 'list', '-vvv' => true]); $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + $tester->run(['command' => 'help', '--help' => true], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run5.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed'); + + $tester->run(['command' => 'help', '-h' => true], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run5.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed'); + $application = new Application(); $application->setAutoExit(false); $application->setCatchExceptions(false); diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_1.json b/src/Symfony/Component/Console/Tests/Fixtures/application_1.json index 29faa8262dceb..f069ad009073a 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/application_1.json +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_1.json @@ -139,6 +139,69 @@ "is_multiple": false, "description": "The output format (txt, xml, json, or md)", "default": "txt" + }, + "help": { + "name": "--help", + "shortcut": "-h", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Display this help message", + "default": false + }, + "quiet": { + "name": "--quiet", + "shortcut": "-q", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, + "verbose": { + "name": "--verbose", + "shortcut": "-v|-vv|-vvv", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug", + "default": false + }, + "version": { + "name": "--version", + "shortcut": "-V", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Display this application version", + "default": false + }, + "ansi": { + "name": "--ansi", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Force ANSI output", + "default": false + }, + "no-ansi": { + "name": "--no-ansi", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Disable ANSI output", + "default": false + }, + "no-interaction": { + "name": "--no-interaction", + "shortcut": "-n", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not ask any interactive question", + "default": false } } } diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_1.md b/src/Symfony/Component/Console/Tests/Fixtures/application_1.md index b46c975a79082..a67a4f18f0428 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/application_1.md +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_1.md @@ -170,3 +170,66 @@ The output format (txt, xml, json, or md) * Is value required: yes * Is multiple: no * Default: `'txt'` + +#### `--help|-h` + +Display this help message + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--quiet|-q` + +Do not output any message + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--verbose|-v|-vv|-vvv` + +Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--version|-V` + +Display this application version + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--ansi` + +Force ANSI output + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--no-ansi` + +Disable ANSI output + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--no-interaction|-n` + +Do not ask any interactive question + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_1.xml b/src/Symfony/Component/Console/Tests/Fixtures/application_1.xml index a0bd076c5a9f3..b190fd109282b 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/application_1.xml +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_1.xml @@ -92,6 +92,27 @@ txt + + + + + + + diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_2.json b/src/Symfony/Component/Console/Tests/Fixtures/application_2.json index 4777a60b52a3e..42180e3edc48f 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/application_2.json +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_2.json @@ -143,6 +143,69 @@ "is_multiple": false, "description": "The output format (txt, xml, json, or md)", "default": "txt" + }, + "help": { + "name": "--help", + "shortcut": "-h", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Display this help message", + "default": false + }, + "quiet": { + "name": "--quiet", + "shortcut": "-q", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, + "verbose": { + "name": "--verbose", + "shortcut": "-v|-vv|-vvv", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug", + "default": false + }, + "version": { + "name": "--version", + "shortcut": "-V", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Display this application version", + "default": false + }, + "ansi": { + "name": "--ansi", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Force ANSI output", + "default": false + }, + "no-ansi": { + "name": "--no-ansi", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Disable ANSI output", + "default": false + }, + "no-interaction": { + "name": "--no-interaction", + "shortcut": "-n", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not ask any interactive question", + "default": false } } } diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_2.md b/src/Symfony/Component/Console/Tests/Fixtures/application_2.md index 5b4896c0825c7..4809f2d441cbf 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/application_2.md +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_2.md @@ -184,6 +184,69 @@ The output format (txt, xml, json, or md) * Is multiple: no * Default: `'txt'` +#### `--help|-h` + +Display this help message + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--quiet|-q` + +Do not output any message + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--verbose|-v|-vv|-vvv` + +Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--version|-V` + +Display this application version + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--ansi` + +Force ANSI output + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--no-ansi` + +Disable ANSI output + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + +#### `--no-interaction|-n` + +Do not ask any interactive question + +* Accept value: no +* Is value required: no +* Is multiple: no +* Default: `false` + `descriptor:command1` --------------------- diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_2.xml b/src/Symfony/Component/Console/Tests/Fixtures/application_2.xml index 5f0f98bd9f15d..b1acb69b0e297 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/application_2.xml +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_2.xml @@ -92,6 +92,27 @@ txt + + + + + + +