From 4d4820102fb34e1d220df81e342fe6d4e8e71199 Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Sun, 19 Sep 2021 10:30:37 +0200 Subject: [PATCH 1/9] feat: #43086 add blacklisting of reserved shortcuts for InputOptions options --- .../Component/Console/Input/InputOption.php | 11 +++++- .../Console/Tests/Input/InputOptionTest.php | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Input/InputOption.php b/src/Symfony/Component/Console/Input/InputOption.php index 2bec34fe1a395..d5949444055a7 100644 --- a/src/Symfony/Component/Console/Input/InputOption.php +++ b/src/Symfony/Component/Console/Input/InputOption.php @@ -52,6 +52,11 @@ class InputOption private $default; private $description; + /* @see Symfony\Component\Console\Application->getDefaultInputDefinition() */ + public const RESERVED_NAMES = ["help", "quiet", "version", "ansi", "no-ansi", "no-interaction", "env", "no-debug", "verbose"]; + public const RESERVED_COMMANDS = ["about", "help", "list"]; + public const RESERVED_SHORTCUTS = ["h", "q", "V", "n", "e", "v", "vv", "vvv"]; + /** * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts * @param int|null $mode The option mode: One of the VALUE_* constants @@ -79,6 +84,10 @@ public function __construct(string $name, $shortcut = null, int $mode = null, st } $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); $shortcuts = array_filter($shortcuts); + $used_reserved_shortcuts = array_intersect($shortcuts, self::RESERVED_SHORTCUTS); + if($used_reserved_shortcuts && !in_array($name, array_merge(self::RESERVED_NAMES, self::RESERVED_COMMANDS))) { + throw new InvalidArgumentException(sprintf('An option shortcut cannot include a reserved shortcut (%s).', implode('|', $used_reserved_shortcuts))); + } $shortcut = implode('|', $shortcuts); if (empty($shortcut)) { @@ -226,6 +235,6 @@ public function equals(self $option) && $option->isArray() === $this->isArray() && $option->isValueRequired() === $this->isValueRequired() && $option->isValueOptional() === $this->isValueOptional() - ; + ; } } diff --git a/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php b/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php index 8ab83d036fe05..b134c6769bc1e 100644 --- a/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php +++ b/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php @@ -57,6 +57,41 @@ public function testShortcut() $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default'); } + public function testInvalidShortcut() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (e).'); + $option = new InputOption('foo', 'e'); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (v).'); + $option = new InputOption('foo', 'x|v|z'); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (v).'); + $option = new InputOption('foo', ['x'|'v'|'z']); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (V).'); + $option = new InputOption('foo', 'V'); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (vvv).'); + $option = new InputOption('foo', 'vvv'); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (help).'); + $option = new InputOption('foo', 'help'); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (ansi).'); + $option = new InputOption('foo', 'ansi'); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (no-ansi).'); + $option = new InputOption('foo', 'no-ansi'); + } + public function testModes() { $option = new InputOption('foo', 'f'); From d6c54c7722f8f9450388ee6d3309c14ba24f4db6 Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Sun, 19 Sep 2021 10:47:15 +0200 Subject: [PATCH 2/9] feat: #43086 add blacklisting of reserved shortcuts for InputOptions in commands --- UPGRADE-5.4.md | 1 + src/Symfony/Component/Console/CHANGELOG.md | 1 + src/Symfony/Component/Console/Input/InputOption.php | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/UPGRADE-5.4.md b/UPGRADE-5.4.md index b3ec3bc0aa686..d747e969717fe 100644 --- a/UPGRADE-5.4.md +++ b/UPGRADE-5.4.md @@ -10,6 +10,7 @@ Console ------- * Deprecate `HelperSet::setCommand()` and `getCommand()` without replacement + * Add blacklisting of reserved shortcuts for `InputOptions` in commands. See `getDefaultInputDefinition()` in `Symfony\Component\Console\Application` Finder ------ diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index fedb08823e15b..0483372d1775e 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 5.4 --- + * Add blacklisting of reserved shortcuts for InputOptions in commands * Add `TesterTrait::assertCommandIsSuccessful()` to test command * Deprecate `HelperSet::setCommand()` and `getCommand()` without replacement diff --git a/src/Symfony/Component/Console/Input/InputOption.php b/src/Symfony/Component/Console/Input/InputOption.php index d5949444055a7..d52f85242a107 100644 --- a/src/Symfony/Component/Console/Input/InputOption.php +++ b/src/Symfony/Component/Console/Input/InputOption.php @@ -52,7 +52,6 @@ class InputOption private $default; private $description; - /* @see Symfony\Component\Console\Application->getDefaultInputDefinition() */ public const RESERVED_NAMES = ["help", "quiet", "version", "ansi", "no-ansi", "no-interaction", "env", "no-debug", "verbose"]; public const RESERVED_COMMANDS = ["about", "help", "list"]; public const RESERVED_SHORTCUTS = ["h", "q", "V", "n", "e", "v", "vv", "vvv"]; @@ -235,6 +234,6 @@ public function equals(self $option) && $option->isArray() === $this->isArray() && $option->isValueRequired() === $this->isValueRequired() && $option->isValueOptional() === $this->isValueOptional() - ; + ; } } From d0fb952b97df0b20d5d816fb435d883c34e63b8b Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Sun, 19 Sep 2021 11:04:42 +0200 Subject: [PATCH 3/9] feat: #43086 add blacklisting of reserved shortcuts for InputOptions in commands --- src/Symfony/Component/Console/Input/InputOption.php | 8 ++++---- .../Component/Console/Tests/Input/InputOptionTest.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Console/Input/InputOption.php b/src/Symfony/Component/Console/Input/InputOption.php index d52f85242a107..8dc26dff56b4f 100644 --- a/src/Symfony/Component/Console/Input/InputOption.php +++ b/src/Symfony/Component/Console/Input/InputOption.php @@ -52,9 +52,9 @@ class InputOption private $default; private $description; - public const RESERVED_NAMES = ["help", "quiet", "version", "ansi", "no-ansi", "no-interaction", "env", "no-debug", "verbose"]; - public const RESERVED_COMMANDS = ["about", "help", "list"]; - public const RESERVED_SHORTCUTS = ["h", "q", "V", "n", "e", "v", "vv", "vvv"]; + public const RESERVED_NAMES = ['help', 'quiet', 'version', 'ansi', 'no-ansi', 'no-interaction', 'env', 'no-debug', 'verbose']; + public const RESERVED_COMMANDS = ['about', 'help', 'list']; + public const RESERVED_SHORTCUTS = ['h', 'q', 'V', 'n', 'e', 'v', 'vv', 'vvv']; /** * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts @@ -84,7 +84,7 @@ public function __construct(string $name, $shortcut = null, int $mode = null, st $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); $shortcuts = array_filter($shortcuts); $used_reserved_shortcuts = array_intersect($shortcuts, self::RESERVED_SHORTCUTS); - if($used_reserved_shortcuts && !in_array($name, array_merge(self::RESERVED_NAMES, self::RESERVED_COMMANDS))) { + if ($used_reserved_shortcuts && !\in_array($name, array_merge(self::RESERVED_NAMES, self::RESERVED_COMMANDS))) { throw new InvalidArgumentException(sprintf('An option shortcut cannot include a reserved shortcut (%s).', implode('|', $used_reserved_shortcuts))); } $shortcut = implode('|', $shortcuts); diff --git a/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php b/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php index b134c6769bc1e..5c44e01db7194 100644 --- a/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php +++ b/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php @@ -69,7 +69,7 @@ public function testInvalidShortcut() $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (v).'); - $option = new InputOption('foo', ['x'|'v'|'z']); + $option = new InputOption('foo', ['x' | 'v' | 'z']); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('An option shortcut cannot include a reserved shortcut (V).'); From 114a13fe8b51651a897b4f2cd03a965e3e4467e2 Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Sun, 19 Sep 2021 11:15:36 +0200 Subject: [PATCH 4/9] feat: #43086 add blacklisting of reserved shortcuts for InputOptions in commands --- .../Console/Tests/ApplicationTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 81dfe7e9a2b5b..85bc76bd950b0 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -504,12 +504,12 @@ public function testDontRunAlternativeNamespaceName() $tester = new ApplicationTester($application); $tester->run(['command' => 'foos:bar1'], ['decorated' => false]); $this->assertSame(' - - There are no commands defined in the "foos" namespace. - - Did you mean this? - foo - + + There are no commands defined in the "foos" namespace. + + Did you mean this? + foo + ', $tester->getDisplay(true)); } @@ -1167,19 +1167,19 @@ public function testRunDispatchesExitCodeOneForExceptionCodeZero() public function testAddingOptionWithDuplicateShortcut() { $this->expectException(\LogicException::class); - $this->expectExceptionMessage('An option with shortcut "e" already exists.'); + $this->expectExceptionMessage('An option with shortcut "t" already exists.'); $dispatcher = new EventDispatcher(); $application = new Application(); $application->setAutoExit(false); $application->setCatchExceptions(false); $application->setDispatcher($dispatcher); - $application->getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'Environment')); + $application->getDefinition()->addOption(new InputOption('--test', '-t', InputOption::VALUE_REQUIRED, 'Test')); $application ->register('foo') ->setAliases(['f']) - ->setDefinition([new InputOption('survey', 'e', InputOption::VALUE_REQUIRED, 'My option with a shortcut.')]) + ->setDefinition([new InputOption('survey', 't', InputOption::VALUE_REQUIRED, 'My option with a shortcut.')]) ->setCode(function (InputInterface $input, OutputInterface $output) {}) ; From b456fb96a95cbefbc337b186fca1bad079d7ebb0 Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Sun, 19 Sep 2021 12:11:34 +0200 Subject: [PATCH 5/9] feat: #43086 add blacklisting of reserved shortcuts for InputOptions in commands --- .../Component/Console/Tests/ApplicationTest.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 85bc76bd950b0..e1d440786b2cc 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -504,12 +504,12 @@ public function testDontRunAlternativeNamespaceName() $tester = new ApplicationTester($application); $tester->run(['command' => 'foos:bar1'], ['decorated' => false]); $this->assertSame(' - - There are no commands defined in the "foos" namespace. - - Did you mean this? - foo - + + There are no commands defined in the "foos" namespace. + + Did you mean this? + foo + ', $tester->getDisplay(true)); } @@ -1198,6 +1198,7 @@ public function testAddingAlreadySetDefinitionElementData($def) $application = new Application(); $application->setAutoExit(false); $application->setCatchExceptions(false); + $application ->register('foo') ->setDefinition([$def]) @@ -1214,7 +1215,7 @@ public function getAddingAlreadySetDefinitionElementData() return [ [new InputArgument('command', InputArgument::REQUIRED)], [new InputOption('quiet', '', InputOption::VALUE_NONE)], - [new InputOption('query', 'q', InputOption::VALUE_NONE)], + //[new InputOption('quiet', 'q', InputOption::VALUE_NONE)], \InvalidArgumentException::class); ]; } From c17a7bf49e7080a4ea68de717a9a61ea01e54ef4 Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Sun, 19 Sep 2021 12:20:29 +0200 Subject: [PATCH 6/9] feat: #43086 add blacklisting of reserved shortcuts for InputOptions in commands --- src/Symfony/Component/Yaml/Command/LintCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Yaml/Command/LintCommand.php b/src/Symfony/Component/Yaml/Command/LintCommand.php index 92c54d997c133..735190f0e27c6 100644 --- a/src/Symfony/Component/Yaml/Command/LintCommand.php +++ b/src/Symfony/Component/Yaml/Command/LintCommand.php @@ -58,7 +58,7 @@ protected function configure() ->setDescription(self::$defaultDescription) ->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN') ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format') - ->addOption('exclude', 'e', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path(s) to exclude') + ->addOption('exclude', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path(s) to exclude') ->addOption('parse-tags', null, InputOption::VALUE_NEGATABLE, 'Parse custom tags', null) ->setHelp(<<%command.name% command lints a YAML file and outputs to STDOUT From 391ea464b47a2db1dfcdc5cb2277f44e8862d43e Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Sun, 19 Sep 2021 12:34:31 +0200 Subject: [PATCH 7/9] feat: #43086 add blacklisting of reserved shortcuts for InputOptions in commands --- src/Symfony/Component/Runtime/Tests/phpt/command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Runtime/Tests/phpt/command.php b/src/Symfony/Component/Runtime/Tests/phpt/command.php index f8ba65133e7db..1fe7a93c077e8 100644 --- a/src/Symfony/Component/Runtime/Tests/phpt/command.php +++ b/src/Symfony/Component/Runtime/Tests/phpt/command.php @@ -8,7 +8,7 @@ require __DIR__.'/autoload.php'; return function (Command $command, InputInterface $input, OutputInterface $output, array $context) { - $command->addOption('hello', 'e', InputOption::VALUE_REQUIRED, 'How should I greet?', 'OK'); + $command->addOption('hello', 'he', InputOption::VALUE_REQUIRED, 'How should I greet?', 'OK'); return $command->setCode(function () use ($input, $output, $context) { $output->write($input->getOption('hello').' Command '.$context['SOME_VAR']); From b4682dd4063610ddcf746c63a2354a70849c476c Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Sun, 19 Sep 2021 13:01:57 +0200 Subject: [PATCH 8/9] feat: has a conflict with text indent#43086 add blacklisting of reserved shortcuts for InputOptions in commands --- .../Component/Console/Tests/ApplicationTest.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index e1d440786b2cc..1e1b4a2a33f11 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -503,15 +503,10 @@ public function testDontRunAlternativeNamespaceName() $application->setAutoExit(false); $tester = new ApplicationTester($application); $tester->run(['command' => 'foos:bar1'], ['decorated' => false]); - $this->assertSame(' - - There are no commands defined in the "foos" namespace. - - Did you mean this? - foo - - -', $tester->getDisplay(true)); + $display = $tester->getDisplay(true); + $this->assertStringContainsString('There are no commands defined in the "foos" namespace.', $display); + $this->assertStringContainsString('Did you mean this?', $display); + $this->assertStringContainsString('foo', $display); } public function testCanRunAlternativeCommandName() From 277b9f40a14e8977b5a8b6e76333ef668f2a4ea3 Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Sun, 19 Sep 2021 15:29:43 +0200 Subject: [PATCH 9/9] feat: #43086 add blacklisting of reserved shortcuts for InputOptions in commands --- .../Component/Console/Tests/ApplicationTest.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 1e1b4a2a33f11..e1d440786b2cc 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -503,10 +503,15 @@ public function testDontRunAlternativeNamespaceName() $application->setAutoExit(false); $tester = new ApplicationTester($application); $tester->run(['command' => 'foos:bar1'], ['decorated' => false]); - $display = $tester->getDisplay(true); - $this->assertStringContainsString('There are no commands defined in the "foos" namespace.', $display); - $this->assertStringContainsString('Did you mean this?', $display); - $this->assertStringContainsString('foo', $display); + $this->assertSame(' + + There are no commands defined in the "foos" namespace. + + Did you mean this? + foo + + +', $tester->getDisplay(true)); } public function testCanRunAlternativeCommandName()