From e4d4a9294fc0574295491ca5abbde714635cd563 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sun, 17 Feb 2019 12:40:42 +0100 Subject: [PATCH] [Console] Prevent ArgvInput::getFirstArgument() from returning an option value --- src/Symfony/Component/Console/Application.php | 7 +++++++ .../Component/Console/Input/ArgvInput.php | 19 ++++++++++++++++++- .../Console/Input/InputDefinition.php | 4 +++- .../Console/Tests/ApplicationTest.php | 13 +++++++++++++ .../Console/Tests/Input/ArgvInputTest.php | 4 ++++ 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 0d52007454022..6cc5f5c580ecf 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -202,6 +202,13 @@ public function doRun(InputInterface $input, OutputInterface $output) return 0; } + try { + // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument. + $input->bind($this->getDefinition()); + } catch (ExceptionInterface $e) { + // Errors must be ignored, full binding/validation happens later when the command is known. + } + $name = $this->getCommandName($input); if (true === $input->hasParameterOption(['--help', '-h'], true)) { if (!$name) { diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php index 142096ae62793..bdb525b2149b0 100644 --- a/src/Symfony/Component/Console/Input/ArgvInput.php +++ b/src/Symfony/Component/Console/Input/ArgvInput.php @@ -262,8 +262,25 @@ private function addLongOption($name, $value) */ public function getFirstArgument() { - foreach ($this->tokens as $token) { + $isOption = false; + foreach ($this->tokens as $i => $token) { if ($token && '-' === $token[0]) { + if (false !== strpos($token, '=') || !isset($this->tokens[$i + 1])) { + continue; + } + + $name = '-' === $token[1] ? substr($token, 2) : substr($token, 1); + if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) { + // noop + } elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) { + $isOption = true; + } + + continue; + } + + if ($isOption) { + $isOption = false; continue; } diff --git a/src/Symfony/Component/Console/Input/InputDefinition.php b/src/Symfony/Component/Console/Input/InputDefinition.php index 9dc1c09b94e3a..53f41d73f5ce2 100644 --- a/src/Symfony/Component/Console/Input/InputDefinition.php +++ b/src/Symfony/Component/Console/Input/InputDefinition.php @@ -338,8 +338,10 @@ public function getOptionDefaults() * @return string The InputOption name * * @throws InvalidArgumentException When option given does not exist + * + * @internal */ - private function shortcutToName($shortcut) + public function shortcutToName($shortcut) { if (!isset($this->shortcuts[$shortcut])) { throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 2cb3324630a08..ae9d130f502ab 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -884,6 +884,19 @@ public function testRun() $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); } + public function testRunWithGlobalOptionAndNoCommand() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->getDefinition()->addOption(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)); + + $output = new StreamOutput(fopen('php://memory', 'w', false)); + $input = new ArgvInput(['cli.php', '--foo', 'bar']); + + $this->assertSame(0, $application->run($input, $output)); + } + /** * Issue #9285. * diff --git a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php index 37caaf2d1cd72..dae7e5b939b0e 100644 --- a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php @@ -312,6 +312,10 @@ public function testGetFirstArgument() $input = new ArgvInput(['cli.php', '-fbbar', 'foo']); $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + + $input = new ArgvInput(['cli.php', '--foo', 'fooval', 'bar']); + $input->bind(new InputDefinition([new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('arg')])); + $this->assertSame('bar', $input->getFirstArgument()); } public function testHasParameterOption()