From 45c381f14f41aa16fdfc5bb0d2c5e306f612069e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 14 May 2025 16:55:32 +0700 Subject: [PATCH 1/2] [Symfony 7.3] handle name from constant on InvokableCommandInputAttributeRector --- .../Fixture/name_from_constant.php.inc | 0 .../Fixture/some_command.php.inc | 57 ++++++++++--------- .../InvokableCommandInputAttributeRector.php | 8 ++- 3 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/name_from_constant.php.inc diff --git a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/name_from_constant.php.inc b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/name_from_constant.php.inc new file mode 100644 index 00000000..e69de29b diff --git a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/some_command.php.inc b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/some_command.php.inc index a8cc0abe..f5a8234b 100644 --- a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/some_command.php.inc +++ b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/some_command.php.inc @@ -4,28 +4,31 @@ namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttr use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; -#[AsCommand(name: 'some_name')] -final class SomeCommand extends Command +#[AsCommand( + name: 'app:hello', + description: 'Greet the user.', +)] +class NameFromConstant extends Command { - public function configure() + private const string ARGUMENT_NAME = 'name'; + + #[\Override] + protected function configure(): void { - $this->addArgument('argument', InputArgument::REQUIRED, 'Argument description'); - $this->addOption('option', 'o', InputOption::VALUE_NONE, 'Option description'); + $this->addArgument(self::ARGUMENT_NAME, InputArgument::OPTIONAL, 'The name of the person to greet.'); } - public function execute(InputInterface $input, OutputInterface $output): int + #[\Override] + protected function execute(InputInterface $input, OutputInterface $output): int { - $someArgument = $input->getArgument('argument'); - $someOption = $input->getOption('option'); + $name = $input->getArgument(self::ARGUMENT_NAME); + $output->writeln("Hello {$name}!"); - // ... - - return 1; + return Command::SUCCESS; } } @@ -37,25 +40,25 @@ namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttr use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; -#[AsCommand(name: 'some_name')] -final class SomeCommand +#[AsCommand( + name: 'app:hello', + description: 'Greet the user.', +)] +class NameFromConstant { - public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')] - string $argument, #[\Symfony\Component\Console\Attribute\Command\Option] - $option, OutputInterface $output): int - { - $someArgument = $argument; - $someOption = $option; - - // ... + private const string ARGUMENT_NAME = 'name'; - return 1; + protected function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: self::ARGUMENT_NAME, description: 'The name of the person to greet.')] + ?string $name, OutputInterface $output): int + { + $name = self::ARGUMENT_NAME; + $output->writeln("Hello {$name}!"); + return Command::SUCCESS; } } -?> +?> \ No newline at end of file diff --git a/rules/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector.php b/rules/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector.php index 21286b50..b2315588 100644 --- a/rules/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector.php +++ b/rules/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector.php @@ -6,6 +6,8 @@ use PhpParser\Node; use PhpParser\Node\Attribute; +use PhpParser\Node\Expr\ClassConstFetch; +use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; @@ -235,7 +237,7 @@ private function removeConfigureClassMethod(Class_ $class): void private function replaceInputArgumentOptionFetchWithVariables(ClassMethod $executeClassMethod): void { - $this->traverseNodesWithCallable($executeClassMethod->stmts, function (Node $node): ?Variable { + $this->traverseNodesWithCallable($executeClassMethod->stmts, function (Node $node): null|Variable|ClassConstFetch|ConstFetch { if (! $node instanceof MethodCall) { return null; } @@ -251,6 +253,10 @@ private function replaceInputArgumentOptionFetchWithVariables(ClassMethod $execu $firstArgValue = $node->getArgs()[0] ->value; + if ($firstArgValue instanceof ClassConstFetch || $firstArgValue instanceof ConstFetch) { + return $firstArgValue; + } + if (! $firstArgValue instanceof String_) { // unable to resolve argument/option name throw new ShouldNotHappenException(); From 1989d717397d6917135d975e2e0c91bfce694f5c Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 14 May 2025 16:55:59 +0700 Subject: [PATCH 2/2] [Symfony 7.3] handle name from constant on InvokableCommandInputAttributeRector --- .../Fixture/name_from_constant.php.inc | 64 +++++++++++++++++++ .../Fixture/some_command.php.inc | 57 ++++++++--------- 2 files changed, 91 insertions(+), 30 deletions(-) diff --git a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/name_from_constant.php.inc b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/name_from_constant.php.inc index e69de29b..f5a8234b 100644 --- a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/name_from_constant.php.inc +++ b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/name_from_constant.php.inc @@ -0,0 +1,64 @@ +addArgument(self::ARGUMENT_NAME, InputArgument::OPTIONAL, 'The name of the person to greet.'); + } + + #[\Override] + protected function execute(InputInterface $input, OutputInterface $output): int + { + $name = $input->getArgument(self::ARGUMENT_NAME); + $output->writeln("Hello {$name}!"); + + return Command::SUCCESS; + } +} + +?> +----- +writeln("Hello {$name}!"); + return Command::SUCCESS; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/some_command.php.inc b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/some_command.php.inc index f5a8234b..a8cc0abe 100644 --- a/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/some_command.php.inc +++ b/rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/some_command.php.inc @@ -4,31 +4,28 @@ namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttr use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; -#[AsCommand( - name: 'app:hello', - description: 'Greet the user.', -)] -class NameFromConstant extends Command +#[AsCommand(name: 'some_name')] +final class SomeCommand extends Command { - private const string ARGUMENT_NAME = 'name'; - - #[\Override] - protected function configure(): void + public function configure() { - $this->addArgument(self::ARGUMENT_NAME, InputArgument::OPTIONAL, 'The name of the person to greet.'); + $this->addArgument('argument', InputArgument::REQUIRED, 'Argument description'); + $this->addOption('option', 'o', InputOption::VALUE_NONE, 'Option description'); } - #[\Override] - protected function execute(InputInterface $input, OutputInterface $output): int + public function execute(InputInterface $input, OutputInterface $output): int { - $name = $input->getArgument(self::ARGUMENT_NAME); - $output->writeln("Hello {$name}!"); + $someArgument = $input->getArgument('argument'); + $someOption = $input->getOption('option'); - return Command::SUCCESS; + // ... + + return 1; } } @@ -40,25 +37,25 @@ namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttr use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; -#[AsCommand( - name: 'app:hello', - description: 'Greet the user.', -)] -class NameFromConstant +#[AsCommand(name: 'some_name')] +final class SomeCommand { - private const string ARGUMENT_NAME = 'name'; - - protected function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: self::ARGUMENT_NAME, description: 'The name of the person to greet.')] - ?string $name, OutputInterface $output): int + public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')] + string $argument, #[\Symfony\Component\Console\Attribute\Command\Option] + $option, OutputInterface $output): int { - $name = self::ARGUMENT_NAME; - $output->writeln("Hello {$name}!"); - return Command::SUCCESS; + $someArgument = $argument; + $someOption = $option; + + // ... + + return 1; } } -?> \ No newline at end of file +?>