From 6948564fdacf0b86bcdb65d4550b77e454de519e Mon Sep 17 00:00:00 2001 From: flkasper Date: Wed, 1 May 2024 13:03:25 +0200 Subject: [PATCH 1/5] [Console] Fix issue 54729 of reserved keyword 'command' as argument name --- src/Symfony/Component/Console/Command/Command.php | 2 +- src/Symfony/Component/Console/Input/InputArgument.php | 5 ++++- .../Component/Console/Tests/Input/InputArgumentTest.php | 8 ++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 03da6db43f33..96a39463c3be 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -407,7 +407,7 @@ public function getNativeDefinition(): InputDefinition * * @return $this * - * @throws InvalidArgumentException When argument mode is not valid + * @throws InvalidArgumentException When argument name is 'command' or mode is not valid */ public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static { diff --git a/src/Symfony/Component/Console/Input/InputArgument.php b/src/Symfony/Component/Console/Input/InputArgument.php index a5d949277748..91d53109bdc9 100644 --- a/src/Symfony/Component/Console/Input/InputArgument.php +++ b/src/Symfony/Component/Console/Input/InputArgument.php @@ -50,7 +50,7 @@ class InputArgument * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion * - * @throws InvalidArgumentException When argument mode is not valid + * @throws InvalidArgumentException When argument name is 'command' or mode is not valid */ public function __construct( private string $name, @@ -59,6 +59,9 @@ public function __construct( string|bool|int|float|array|null $default = null, private \Closure|array $suggestedValues = [], ) { + if ('command' === $name && 'The command to execute' !== $description) { + throw new InvalidArgumentException('Reserved keyword "command" was used as argument name.'); + } if (null === $mode) { $mode = self::OPTIONAL; } elseif ($mode >= (self::IS_ARRAY << 1) || $mode < 1) { diff --git a/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php b/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php index 05447426cc46..8da53df6ef32 100644 --- a/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php +++ b/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Completion\Suggestion; +use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Input\InputArgument; @@ -26,6 +27,13 @@ public function testConstructor() $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); } + public function testReservedArgumentName(): void + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('Reserved keyword "command" was used as argument name.'); + new InputArgument('command'); + } + public function testModes() { $argument = new InputArgument('foo'); From 719fa86942fea27786bf7f9a7a8a64129a6c507c Mon Sep 17 00:00:00 2001 From: flkasper Date: Wed, 1 May 2024 13:08:54 +0200 Subject: [PATCH 2/5] [Console] Add changelog entry --- src/Symfony/Component/Console/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index 25d7f7179723..19fc62382359 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add `ArgvInput::getRawTokens()` + * Throws `InvalidArgumentException` if an argument was created with the reserved keyword `command` as name and is not the `command name` argument. 7.0 --- From d67fa0ca80e3d73e7feb0ddb5eb0a684433ce1db Mon Sep 17 00:00:00 2001 From: flkasper Date: Wed, 1 May 2024 13:30:15 +0200 Subject: [PATCH 3/5] Fix tests, remove void return type in test --- src/Symfony/Component/Console/Tests/ApplicationTest.php | 2 +- src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php | 2 +- .../Component/Console/Tests/Input/InputArgumentTest.php | 2 +- .../Component/Console/Tests/Tester/CommandTesterTest.php | 5 +++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index ca85c24b1f75..44a9cbfa81fd 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -1303,7 +1303,7 @@ public function testAddingAlreadySetDefinitionElementData($def) public static function getAddingAlreadySetDefinitionElementData(): array { return [ - [new InputArgument('command', InputArgument::REQUIRED)], + [new InputArgument('command', InputArgument::REQUIRED, 'The command to execute')], [new InputOption('quiet', '', InputOption::VALUE_NONE)], [new InputOption('query', 'q', InputOption::VALUE_NONE)], ]; diff --git a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php index 80ea06d67fda..cb1de392d14c 100644 --- a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php @@ -316,7 +316,7 @@ public static function provideInvalidInput(): array ], [ ['cli.php', 'acme:foo', 'bar'], - new InputDefinition([new InputArgument('command', InputArgument::REQUIRED)]), + new InputDefinition([new InputArgument('command', InputArgument::REQUIRED, 'The command to execute')]), 'No arguments expected for "acme:foo" command, got "bar"', ], [ diff --git a/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php b/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php index 8da53df6ef32..b68ceec376a3 100644 --- a/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php +++ b/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php @@ -27,7 +27,7 @@ public function testConstructor() $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); } - public function testReservedArgumentName(): void + public function testReservedArgumentName() { self::expectException(InvalidArgumentException::class); self::expectExceptionMessage('Reserved keyword "command" was used as argument name.'); diff --git a/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php b/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php index ce0a24b99fda..0db352d8cb85 100644 --- a/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php +++ b/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\Output; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\Question; @@ -30,7 +31,7 @@ class CommandTesterTest extends TestCase protected function setUp(): void { $this->command = new Command('foo'); - $this->command->addArgument('command'); + $this->command->addArgument('command', InputArgument::REQUIRED, 'The command to execute'); $this->command->addArgument('foo'); $this->command->setCode(function ($input, $output) { $output->writeln('foo'); }); @@ -231,7 +232,7 @@ public function testSymfonyStyleCommandWithInputs() public function testErrorOutput() { $command = new Command('foo'); - $command->addArgument('command'); + $command->addArgument('command', InputArgument::REQUIRED, 'The command to execute'); $command->addArgument('foo'); $command->setCode(function ($input, $output) { $output->getErrorOutput()->write('foo'); From d23ba0861532639f7feff4e400d0cfdf6ad82687 Mon Sep 17 00:00:00 2001 From: flkasper Date: Sat, 4 May 2024 19:06:16 +0200 Subject: [PATCH 4/5] Applied suggested wording changes --- src/Symfony/Component/Console/Command/Command.php | 2 +- src/Symfony/Component/Console/Input/InputArgument.php | 4 ++-- .../Component/Console/Tests/Input/InputArgumentTest.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 96a39463c3be..ca64f3031cbf 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -407,7 +407,7 @@ public function getNativeDefinition(): InputDefinition * * @return $this * - * @throws InvalidArgumentException When argument name is 'command' or mode is not valid + * @throws InvalidArgumentException When argument name is "command" or mode is not valid */ public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static { diff --git a/src/Symfony/Component/Console/Input/InputArgument.php b/src/Symfony/Component/Console/Input/InputArgument.php index 91d53109bdc9..419f3fe5cfbf 100644 --- a/src/Symfony/Component/Console/Input/InputArgument.php +++ b/src/Symfony/Component/Console/Input/InputArgument.php @@ -50,7 +50,7 @@ class InputArgument * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion * - * @throws InvalidArgumentException When argument name is 'command' or mode is not valid + * @throws InvalidArgumentException When argument name is "command" or mode is not valid */ public function __construct( private string $name, @@ -60,7 +60,7 @@ public function __construct( private \Closure|array $suggestedValues = [], ) { if ('command' === $name && 'The command to execute' !== $description) { - throw new InvalidArgumentException('Reserved keyword "command" was used as argument name.'); + throw new InvalidArgumentException('The "command" keyword cannot be used as the argument name of a command.'); } if (null === $mode) { $mode = self::OPTIONAL; diff --git a/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php b/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php index b68ceec376a3..9acfab6b4ac7 100644 --- a/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php +++ b/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php @@ -27,10 +27,10 @@ public function testConstructor() $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); } - public function testReservedArgumentName() + public function testCommandAsArgumentName() { self::expectException(InvalidArgumentException::class); - self::expectExceptionMessage('Reserved keyword "command" was used as argument name.'); + self::expectExceptionMessage('The "command" keyword cannot be used as the argument name of a command.'); new InputArgument('command'); } From a162b40cfbc7c35cdb23561cd0b7186afc759d71 Mon Sep 17 00:00:00 2001 From: flkasper Date: Sat, 4 May 2024 19:27:50 +0200 Subject: [PATCH 5/5] Replace string description of command argument by constant. --- src/Symfony/Component/Console/Application.php | 6 +++++- src/Symfony/Component/Console/Input/InputArgument.php | 3 ++- src/Symfony/Component/Console/Tests/ApplicationTest.php | 2 +- src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php | 3 ++- .../Component/Console/Tests/Tester/CommandTesterTest.php | 4 ++-- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 9c301471c658..15c088a48f1e 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -72,6 +72,10 @@ */ class Application implements ResetInterface { + /** + * Description of the command to be executed. + */ + public const COMMAND_ARGUMENT_DESCRIPTION = 'The command to execute'; private array $commands = []; private bool $wantHelps = false; private ?Command $runningCommand = null; @@ -1082,7 +1086,7 @@ protected function getCommandName(InputInterface $input): ?string protected function getDefaultInputDefinition(): InputDefinition { return new InputDefinition([ - new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + new InputArgument('command', InputArgument::REQUIRED, self::COMMAND_ARGUMENT_DESCRIPTION), new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display help for the given command. When no command is given display help for the '.$this->defaultCommand.' command'), new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), diff --git a/src/Symfony/Component/Console/Input/InputArgument.php b/src/Symfony/Component/Console/Input/InputArgument.php index 419f3fe5cfbf..89366d60b157 100644 --- a/src/Symfony/Component/Console/Input/InputArgument.php +++ b/src/Symfony/Component/Console/Input/InputArgument.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Console\Input; +use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; @@ -59,7 +60,7 @@ public function __construct( string|bool|int|float|array|null $default = null, private \Closure|array $suggestedValues = [], ) { - if ('command' === $name && 'The command to execute' !== $description) { + if ('command' === $name && Application::COMMAND_ARGUMENT_DESCRIPTION !== $description) { throw new InvalidArgumentException('The "command" keyword cannot be used as the argument name of a command.'); } if (null === $mode) { diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 44a9cbfa81fd..a4955710019e 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -1303,7 +1303,7 @@ public function testAddingAlreadySetDefinitionElementData($def) public static function getAddingAlreadySetDefinitionElementData(): array { return [ - [new InputArgument('command', InputArgument::REQUIRED, 'The command to execute')], + [new InputArgument('command', InputArgument::REQUIRED, Application::COMMAND_ARGUMENT_DESCRIPTION)], [new InputOption('quiet', '', InputOption::VALUE_NONE)], [new InputOption('query', 'q', InputOption::VALUE_NONE)], ]; diff --git a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php index cb1de392d14c..28a57965cf56 100644 --- a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Console\Tests\Input; use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputDefinition; @@ -316,7 +317,7 @@ public static function provideInvalidInput(): array ], [ ['cli.php', 'acme:foo', 'bar'], - new InputDefinition([new InputArgument('command', InputArgument::REQUIRED, 'The command to execute')]), + new InputDefinition([new InputArgument('command', InputArgument::REQUIRED, Application::COMMAND_ARGUMENT_DESCRIPTION)]), 'No arguments expected for "acme:foo" command, got "bar"', ], [ diff --git a/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php b/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php index 0db352d8cb85..d5d9fbf34de6 100644 --- a/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php +++ b/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php @@ -31,7 +31,7 @@ class CommandTesterTest extends TestCase protected function setUp(): void { $this->command = new Command('foo'); - $this->command->addArgument('command', InputArgument::REQUIRED, 'The command to execute'); + $this->command->addArgument('command', InputArgument::REQUIRED, Application::COMMAND_ARGUMENT_DESCRIPTION); $this->command->addArgument('foo'); $this->command->setCode(function ($input, $output) { $output->writeln('foo'); }); @@ -232,7 +232,7 @@ public function testSymfonyStyleCommandWithInputs() public function testErrorOutput() { $command = new Command('foo'); - $command->addArgument('command', InputArgument::REQUIRED, 'The command to execute'); + $command->addArgument('command', InputArgument::REQUIRED, Application::COMMAND_ARGUMENT_DESCRIPTION); $command->addArgument('foo'); $command->setCode(function ($input, $output) { $output->getErrorOutput()->write('foo');