Skip to content

Commit 757db77

Browse files
authored
[Symfony73] Fix named arg different position crash on InvokableCommandInputAttributeRector (#905)
* [Symfony73] Fix named arg different position crash on InvokableCommandInputAttributeRector * fix * fix phpstan * final touch: ensure name arg always exists * really final touch: default expr
1 parent 8360bd1 commit 757db77

File tree

2 files changed

+82
-28
lines changed

2 files changed

+82
-28
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttributeRector\Fixture;
4+
5+
use Symfony\Component\Console\Attribute\AsCommand;
6+
use Symfony\Component\Console\Command\Command;
7+
use Symfony\Component\Console\Input\InputInterface;
8+
use Symfony\Component\Console\Input\InputOption;
9+
use Symfony\Component\Console\Output\OutputInterface;
10+
11+
#[AsCommand(
12+
name: 'ack:alarm',
13+
description: '',
14+
)]
15+
class WithNamedArgModeDifferentPosition extends Command
16+
{
17+
protected function configure(): void
18+
{
19+
$this
20+
->addOption(
21+
name: 'startDate',
22+
mode: InputOption::VALUE_REQUIRED,
23+
description: 'non empty description needed to reproduce the bug',
24+
);
25+
}
26+
27+
protected function execute(InputInterface $input, OutputInterface $output): int
28+
{
29+
return Command::SUCCESS;
30+
}
31+
}
32+
33+
?>
34+
-----
35+
<?php
36+
37+
namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttributeRector\Fixture;
38+
39+
use Symfony\Component\Console\Attribute\AsCommand;
40+
use Symfony\Component\Console\Command\Command;
41+
use Symfony\Component\Console\Input\InputInterface;
42+
use Symfony\Component\Console\Input\InputOption;
43+
use Symfony\Component\Console\Output\OutputInterface;
44+
45+
#[AsCommand(
46+
name: 'ack:alarm',
47+
description: '',
48+
)]
49+
class WithNamedArgModeDifferentPosition
50+
{
51+
public function __invoke(
52+
#[\Symfony\Component\Console\Attribute\Option(name: 'startDate', mode: InputOption::VALUE_REQUIRED, description: 'non empty description needed to reproduce the bug')]
53+
$startdate
54+
): int
55+
{
56+
return Command::SUCCESS;
57+
}
58+
}
59+
60+
?>

rules/Symfony73/NodeAnalyzer/CommandOptionsResolver.php

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use PhpParser\Node\Arg;
88
use PhpParser\Node\Expr;
9+
use PhpParser\Node\Expr\MethodCall;
910
use PhpParser\Node\Stmt\ClassMethod;
1011
use PHPStan\Type\Type;
1112
use Rector\NodeTypeResolver\NodeTypeResolver;
@@ -32,47 +33,43 @@ public function resolve(ClassMethod $configureClassMethod): array
3233
$commandOptions = [];
3334

3435
foreach ($addOptionMethodCalls as $addOptionMethodCall) {
35-
$addOptionArgs = $addOptionMethodCall->getArgs();
36+
$nameArg = $addOptionMethodCall->getArg('name', 0);
37+
if (! $nameArg instanceof Arg) {
38+
continue;
39+
}
3640

37-
$optionName = $this->valueResolver->getValue($addOptionArgs[0]->value);
38-
39-
$isImplicitBoolean = $this->isImplicitBoolean($addOptionArgs);
41+
$optionName = $this->valueResolver->getValue($nameArg->value);
42+
$isImplicitBoolean = $this->isImplicitBoolean($addOptionMethodCall);
4043

4144
$commandOptions[] = new CommandOption(
4245
$optionName,
43-
$addOptionArgs[0]->value,
44-
$addOptionArgs[1]->value ?? null,
45-
$addOptionArgs[2]->value ?? null,
46-
$addOptionArgs[3]->value ?? null,
47-
$addOptionArgs[4]->value ?? null,
48-
$this->isArrayMode($addOptionArgs),
46+
$nameArg->value,
47+
$addOptionMethodCall->getArg('shortcut', 1)?->value,
48+
$addOptionMethodCall->getArg('mode', 2)?->value,
49+
$addOptionMethodCall->getArg('description', 3)?->value,
50+
$addOptionMethodCall->getArg('default', 4)?->value,
51+
$this->isArrayMode($addOptionMethodCall),
4952
$isImplicitBoolean,
50-
$this->resolveDefaultType($addOptionArgs)
53+
$this->resolveDefaultType($addOptionMethodCall)
5154
);
5255
}
5356

5457
return $commandOptions;
5558
}
5659

57-
/**
58-
* @param Arg[] $args
59-
*/
60-
private function resolveDefaultType(array $args): ?Type
60+
private function resolveDefaultType(MethodCall $methodCall): ?Type
6161
{
62-
$defaultArg = $args[4] ?? null;
63-
if (! $defaultArg instanceof Arg) {
62+
$defaultExpr = $methodCall->getArg('default', 4)?->value;
63+
if (! $defaultExpr instanceof Expr) {
6464
return null;
6565
}
6666

67-
return $this->nodeTypeResolver->getType($defaultArg->value);
67+
return $this->nodeTypeResolver->getType($defaultExpr);
6868
}
6969

70-
/**
71-
* @param Arg[] $args
72-
*/
73-
private function isArrayMode(array $args): bool
70+
private function isArrayMode(MethodCall $methodCall): bool
7471
{
75-
$modeExpr = $args[2]->value ?? null;
72+
$modeExpr = $methodCall->getArg('mode', 2)?->value;
7673
if (! $modeExpr instanceof Expr) {
7774
return false;
7875
}
@@ -82,12 +79,9 @@ private function isArrayMode(array $args): bool
8279
return (bool) ($modeValue & 8);
8380
}
8481

85-
/**
86-
* @param Arg[] $args
87-
*/
88-
private function isImplicitBoolean(array $args): bool
82+
private function isImplicitBoolean(MethodCall $methodCall): bool
8983
{
90-
$modeExpr = $args[2]->value ?? null;
84+
$modeExpr = $methodCall->getArg('mode', 2)?->value;
9185
if (! $modeExpr instanceof Expr) {
9286
return false;
9387
}

0 commit comments

Comments
 (0)