From e5c836e8a44a26edf9575ed7bba4241295076932 Mon Sep 17 00:00:00 2001 From: Floris Luiten Date: Tue, 9 Dec 2025 13:36:55 +0100 Subject: [PATCH 1/2] Setup test for order of optional parameters In PHP, optional parameters should be declared after required parameters, otherwise they will be implicitly treated as required parameters. --- ..._default_and_without_default_value.php.inc | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/with_default_and_without_default_value.php.inc diff --git a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/with_default_and_without_default_value.php.inc b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/with_default_and_without_default_value.php.inc new file mode 100644 index 000000000..58683c654 --- /dev/null +++ b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/with_default_and_without_default_value.php.inc @@ -0,0 +1,63 @@ +addOption('option', 'o', InputOption::VALUE_NONE, 'Option description', false); + $this->addOption('another', 'a', InputOption::VALUE_REQUIRED, 'No default value'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $someOption = $input->getOption('option'); + $another = $input->getOption('another'); + $output->writeln('Using output too'); + // ... + return 1; + } +} + +?> +----- +writeln('Using output too'); + // ... + return 1; + } +} + +?> From 2e8946a83909ea70464ddbad72134ff51e9db5d3 Mon Sep 17 00:00:00 2001 From: Floris Luiten Date: Tue, 9 Dec 2025 13:37:19 +0100 Subject: [PATCH 2/2] Potential fix --- .../Fixture/DefaultValue/option_with_array_type.php.inc | 8 ++++---- .../Class_/InvokableCommandInputAttributeRector.php | 8 +++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/DefaultValue/option_with_array_type.php.inc b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/DefaultValue/option_with_array_type.php.inc index 9ce6f0c21..74fe6c930 100644 --- a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/DefaultValue/option_with_array_type.php.inc +++ b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/DefaultValue/option_with_array_type.php.inc @@ -16,8 +16,8 @@ class OptionWithOptionalValue extends Command { protected function configure(): void { - $this->addOption('some-array', null, InputOption::VALUE_IS_ARRAY, '', ['third value']); $this->addOption('no-default-array', null, InputOption::VALUE_IS_ARRAY); + $this->addOption('some-array', null, InputOption::VALUE_IS_ARRAY, '', ['third value']); } protected function execute(InputInterface $input, OutputInterface $output): int { @@ -43,10 +43,10 @@ use Symfony\Component\Console\Output\OutputInterface; class OptionWithOptionalValue { public function __invoke( - #[\Symfony\Component\Console\Attribute\Option(name: 'some-array', mode: InputOption::VALUE_IS_ARRAY)] - array $someArray = ['third value'], #[\Symfony\Component\Console\Attribute\Option(name: 'no-default-array', mode: InputOption::VALUE_IS_ARRAY)] - array $noDefaultArray + array $noDefaultArray, + #[\Symfony\Component\Console\Attribute\Option(name: 'some-array', mode: InputOption::VALUE_IS_ARRAY)] + array $someArray = ['third value'] ): int { } diff --git a/rules/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector.php b/rules/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector.php index bc3a416cc..1cf68a534 100644 --- a/rules/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector.php +++ b/rules/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector.php @@ -160,7 +160,13 @@ public function refactor(Node $node): ?Class_ $invokeParams = $this->createInvokeParams($node); - $invokeClassMethod->params = array_merge($invokeParams, [$executeClassMethod->params[1]]); + $executeClassMethodParams = array_merge($invokeParams, [$executeClassMethod->params[1]]); + + // Ensure that optional parameters are listed last in the argument list + $invokeClassMethod->params = array_merge( + array_filter($executeClassMethodParams, fn(Param $param) => is_null($param->default)), + array_filter($executeClassMethodParams, fn(Param $param) => !is_null($param->default)), + ); // 6. remove parent class $node->extends = null;