Skip to content

Commit 635b415

Browse files
authored
[console] [7.3] Add const names in InvokableCommandInputAttributeRector + fix -_ naming to camel case (#840)
* [console] [7.3] Add const names in InvokableCommandInputAttributeRector * fix names to camelCase * fix names to camelCase
1 parent eec47b7 commit 635b415

7 files changed

Lines changed: 125 additions & 38 deletions

File tree

rules-tests/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector/Fixture/name_with_hyphen.php.inc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ use Symfony\Component\Console\Output\OutputInterface;
4444
class NameWithHyphen
4545
{
4646
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument-with-hyphen', description: 'Argument description')]
47-
?string $argument_with_hyphen, #[\Symfony\Component\Console\Attribute\Option(name: 'option-with-hyphen')]
48-
$option_with_hyphen): int
47+
?string $argumentWithHyphen, #[\Symfony\Component\Console\Attribute\Option(name: 'option-with-hyphen')]
48+
$optionWithHyphen): int
4949
{
5050
$argument = $argument_with_hyphen;
5151
$option = $option_with_hyphen;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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\Output\OutputInterface;
9+
use Symfony\Component\Console\Input\InputArgument;
10+
use Symfony\Component\Console\Input\InputOption;
11+
12+
#[AsCommand(name: 'some_name')]
13+
final class OptionNameConstant extends Command
14+
{
15+
private const ARGUMENT_NAME = 'some-argument';
16+
17+
private const OPTION_NAME = 'some-option';
18+
19+
public function configure()
20+
{
21+
$this->addArgument(self::ARGUMENT_NAME);
22+
$this->addOption(self::OPTION_NAME);
23+
}
24+
25+
public function execute(InputInterface $input, OutputInterface $output): int
26+
{
27+
$someArgument = $input->getArgument(self::ARGUMENT_NAME);
28+
29+
$someOption = $input->getOption(self::OPTION_NAME);
30+
31+
// ...
32+
33+
return 1;
34+
}
35+
}
36+
37+
?>
38+
-----
39+
<?php
40+
41+
namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttributeRector\Fixture;
42+
43+
use Symfony\Component\Console\Attribute\AsCommand;
44+
use Symfony\Component\Console\Command\Command;
45+
use Symfony\Component\Console\Input\InputInterface;
46+
use Symfony\Component\Console\Output\OutputInterface;
47+
use Symfony\Component\Console\Input\InputArgument;
48+
use Symfony\Component\Console\Input\InputOption;
49+
50+
#[AsCommand(name: 'some_name')]
51+
final class OptionNameConstant
52+
{
53+
private const ARGUMENT_NAME = 'some-argument';
54+
55+
private const OPTION_NAME = 'some-option';
56+
57+
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: self::ARGUMENT_NAME)]
58+
?string $someArgument, #[\Symfony\Component\Console\Attribute\Option(name: self::OPTION_NAME)]
59+
$someOption): int
60+
{
61+
$someArgument = $some_argument;
62+
63+
$someOption = $some_option;
64+
65+
// ...
66+
67+
return 1;
68+
}
69+
}
70+
71+
?>

rules/Symfony73/NodeAnalyzer/CommandArgumentsResolver.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
namespace Rector\Symfony\Symfony73\NodeAnalyzer;
66

77
use PhpParser\Node\Stmt\ClassMethod;
8+
use Rector\PhpParser\Node\Value\ValueResolver;
89
use Rector\Symfony\Symfony73\NodeFinder\MethodCallFinder;
910
use Rector\Symfony\Symfony73\ValueObject\CommandArgument;
1011

1112
final readonly class CommandArgumentsResolver
1213
{
1314
public function __construct(
1415
private MethodCallFinder $methodCallFinder,
16+
private ValueResolver $valueResolver
1517
) {
1618
}
1719

@@ -26,7 +28,10 @@ public function resolve(ClassMethod $configureClassMethod): array
2628
foreach ($addArgumentMethodCalls as $addArgumentMethodCall) {
2729
$addArgumentArgs = $addArgumentMethodCall->getArgs();
2830

31+
$argumentName = $this->valueResolver->getValue($addArgumentArgs[0]->value);
32+
2933
$commandArguments[] = new CommandArgument(
34+
$argumentName,
3035
$addArgumentArgs[0]->value,
3136
$addArgumentArgs[1]->value ?? null,
3237
$addArgumentArgs[2]->value ?? null,

rules/Symfony73/NodeAnalyzer/CommandOptionsResolver.php

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44

55
namespace Rector\Symfony\Symfony73\NodeAnalyzer;
66

7-
use PhpParser\Node\Scalar\String_;
87
use PhpParser\Node\Stmt\ClassMethod;
9-
use Rector\Exception\ShouldNotHappenException;
8+
use Rector\PhpParser\Node\Value\ValueResolver;
109
use Rector\Symfony\Symfony73\NodeFinder\MethodCallFinder;
1110
use Rector\Symfony\Symfony73\ValueObject\CommandOption;
1211

1312
final readonly class CommandOptionsResolver
1413
{
1514
public function __construct(
1615
private MethodCallFinder $methodCallFinder,
16+
private ValueResolver $valueResolver
1717
) {
1818
}
1919

@@ -29,17 +29,15 @@ public function resolve(ClassMethod $configureClassMethod): array
2929
foreach ($addOptionMethodCalls as $addOptionMethodCall) {
3030
$addOptionArgs = $addOptionMethodCall->getArgs();
3131

32-
$nameArgValue = $addOptionArgs[0]->value;
33-
if (! $nameArgValue instanceof String_) {
34-
// we need string value, otherwise param will not have a name
35-
throw new ShouldNotHappenException('Option name is required');
36-
}
32+
$optionName = $this->valueResolver->getValue($addOptionArgs[0]->value);
3733

38-
$shortcutExpr = $addOptionArgs[1]->value ?? null;
39-
$modeExpr = $addOptionArgs[2]->value ?? null;
40-
$descriptionExpr = $addOptionArgs[3]->value ?? null;
41-
42-
$commandOptions[] = new CommandOption($nameArgValue, $shortcutExpr, $modeExpr, $descriptionExpr);
34+
$commandOptions[] = new CommandOption(
35+
$optionName,
36+
$addOptionArgs[0]->value,
37+
$addOptionArgs[1]->value ?? null,
38+
$addOptionArgs[2]->value ?? null,
39+
$addOptionArgs[3]->value ?? null
40+
);
4341
}
4442

4543
return $commandOptions;

rules/Symfony73/NodeFactory/CommandInvokeParamsFactory.php

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
final readonly class CommandInvokeParamsFactory
2222
{
2323
public function __construct(
24-
private ValueResolver $valueResolver
24+
private ValueResolver $valueResolver,
2525
) {
2626
}
2727

@@ -47,29 +47,31 @@ private function createArgumentParams(array $commandArguments): array
4747
$argumentParams = [];
4848

4949
foreach ($commandArguments as $commandArgument) {
50-
$argumentName = (string) $this->valueResolver->getValue($commandArgument->getName());
51-
$variableName = str_replace('-', '_', $argumentName);
52-
50+
$variableName = $this->createCamelCase($commandArgument->getNameValue());
5351
$argumentParam = new Param(new Variable($variableName));
5452

5553
$argumentParam->type = new Identifier('string');
5654

57-
$modeValue = $this->valueResolver->getValue($commandArgument->getMode());
58-
if ($modeValue === null || $modeValue === 2) {
55+
// is argument optional?
56+
if ($commandArgument->getMode() === null) {
57+
$argumentParam->type = new NullableType($argumentParam->type);
58+
} elseif ($this->valueResolver->isValue($commandArgument->getMode(), 2)) {
5959
$argumentParam->type = new NullableType($argumentParam->type);
6060
}
6161

6262
// @todo fill type or default value
6363
// @todo default string, multiple values array
6464

65+
$argumentArgs = [new Arg(value: $commandArgument->getName(), name: new Identifier('name'))];
66+
67+
if ($commandArgument->getDescription() instanceof Expr) {
68+
$argumentArgs[] = new Arg(value: $commandArgument->getDescription(), name: new Identifier(
69+
'description'
70+
));
71+
}
72+
6573
$argumentParam->attrGroups[] = new AttributeGroup([
66-
new Attribute(
67-
new FullyQualified(SymfonyAttribute::COMMAND_ARGUMENT),
68-
[
69-
new Arg(value: $commandArgument->getName(), name: new Identifier('name')),
70-
new Arg(value: $commandArgument->getDescription(), name: new Identifier('description')),
71-
]
72-
),
74+
new Attribute(new FullyQualified(SymfonyAttribute::COMMAND_ARGUMENT), $argumentArgs),
7375
]);
7476

7577
$argumentParams[] = $argumentParam;
@@ -87,9 +89,7 @@ private function createOptionParams(array $commandOptions): array
8789
$optionParams = [];
8890

8991
foreach ($commandOptions as $commandOption) {
90-
$optionName = $commandOption->getStringName();
91-
$variableName = str_replace('-', '_', $optionName);
92-
92+
$variableName = $this->createCamelCase($commandOption->getNameValue());
9393
$optionParam = new Param(new Variable($variableName));
9494

9595
$optionArgs = [new Arg(value: $commandOption->getName(), name: new Identifier('name'))];
@@ -115,4 +115,16 @@ private function createOptionParams(array $commandOptions): array
115115

116116
return $optionParams;
117117
}
118+
119+
private function createCamelCase(string $value): string
120+
{
121+
// Replace dashes/underscores with spaces
122+
$value = str_replace(['-', '_'], ' ', strtolower($value));
123+
124+
// Capitalize each word, then remove spaces
125+
$value = str_replace(' ', '', ucwords($value));
126+
127+
// Lowercase first character to make it camelCase
128+
return lcfirst($value);
129+
}
118130
}

rules/Symfony73/ValueObject/CommandArgument.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,18 @@
99
final readonly class CommandArgument
1010
{
1111
public function __construct(
12+
private string $nameValue,
1213
private Expr $name,
1314
private ?Expr $mode,
1415
private ?Expr $description
1516
) {
1617
}
1718

19+
public function getNameValue(): string
20+
{
21+
return $this->nameValue;
22+
}
23+
1824
public function getName(): Expr
1925
{
2026
return $this->name;

rules/Symfony73/ValueObject/CommandOption.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55
namespace Rector\Symfony\Symfony73\ValueObject;
66

77
use PhpParser\Node\Expr;
8-
use PhpParser\Node\Scalar\String_;
9-
use Rector\Exception\NotImplementedYetException;
108

119
final readonly class CommandOption
1210
{
1311
public function __construct(
12+
private string $nameValue,
1413
private Expr $name,
1514
private ?Expr $shortcut,
1615
private ?Expr $mode,
@@ -38,12 +37,8 @@ public function getDescription(): ?Expr
3837
return $this->description;
3938
}
4039

41-
public function getStringName(): string
40+
public function getNameValue(): string
4241
{
43-
if ($this->name instanceof String_) {
44-
return $this->name->value;
45-
}
46-
47-
throw new NotImplementedYetException(sprintf('Add more options to "%s"', self::class));
42+
return $this->nameValue;
4843
}
4944
}

0 commit comments

Comments
 (0)