Skip to content

Commit 7b29d0e

Browse files
committed
pass symfony style via execute() if created inside
1 parent ff5c699 commit 7b29d0e

File tree

6 files changed

+145
-22
lines changed

6 files changed

+145
-22
lines changed

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Rector\Tests\Symfony\Symfony73\Rector\Class_\InvokableCommandInputAttributeRector\Fixture;
3+
namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttributeRector\Fixture;
44

55
use Symfony\Component\Console\Attribute\AsCommand;
66
use Symfony\Component\Console\Command\Command;
@@ -11,7 +11,6 @@ use Symfony\Component\Console\Style\SymfonyStyle;
1111
#[AsCommand('app:my-command')]
1212
final class MyCommand extends Command
1313
{
14-
#[\Override]
1514
protected function execute(InputInterface $input, OutputInterface $output): int
1615
{
1716
$io = new SymfonyStyle($input, $output);
@@ -24,18 +23,20 @@ final class MyCommand extends Command
2423
-----
2524
<?php
2625

27-
namespace Rector\Tests\Symfony\Symfony73\Rector\Class_\InvokableCommandInputAttributeRector\Fixture;
26+
namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttributeRector\Fixture;
2827

2928
use Symfony\Component\Console\Attribute\AsCommand;
30-
29+
use Symfony\Component\Console\Command\Command;
30+
use Symfony\Component\Console\Input\InputInterface;
31+
use Symfony\Component\Console\Output\OutputInterface;
32+
use Symfony\Component\Console\Style\SymfonyStyle;
33+
3134
#[AsCommand('app:my-command')]
3235
final class MyCommand
3336
{
34-
#[\Override]
3537
public function __invoke(\Symfony\Component\Console\Style\SymfonyStyle $io): int
3638
{
3739
$io->info('Great success!');
38-
3940
return 1;
4041
}
4142
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Symfony\Symfony73\NodeTransformer;
6+
7+
use PhpParser\Node\Expr\Variable;
8+
use PhpParser\Node\Stmt\ClassMethod;
9+
use Rector\NodeNameResolver\NodeNameResolver;
10+
use Rector\PhpParser\Node\BetterNodeFinder;
11+
12+
final readonly class CommandUnusedInputOutputRemover
13+
{
14+
/**
15+
* @var string[]
16+
*/
17+
private const VARIABLE_NAMES = ['input', 'output'];
18+
19+
public function __construct(
20+
private NodeNameResolver $nodeNameResolver,
21+
private BetterNodeFinder $betterNodeFinder
22+
) {
23+
24+
}
25+
26+
public function remove(ClassMethod $executeClassMethod): void
27+
{
28+
foreach (self::VARIABLE_NAMES as $variableName) {
29+
$inputVariable = $this->betterNodeFinder->findVariableOfName($executeClassMethod->stmts, $variableName);
30+
31+
// is used → skip
32+
if ($inputVariable instanceof Variable) {
33+
continue;
34+
}
35+
36+
$this->removeParameterByName($executeClassMethod, $variableName);
37+
}
38+
}
39+
40+
private function removeParameterByName(ClassMethod $classMethod, string $paramName): void
41+
{
42+
foreach ($classMethod->getParams() as $key => $param) {
43+
if (! $this->nodeNameResolver->isName($param->var, $paramName)) {
44+
continue;
45+
}
46+
47+
unset($classMethod->params[$key]);
48+
}
49+
}
50+
}

rules/Symfony73/NodeTransformer/OutputInputSymfonyStyleReplacer.php

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,94 @@
44

55
namespace Rector\Symfony\Symfony73\NodeTransformer;
66

7+
use PhpParser\Node\Expr\Assign;
8+
use PhpParser\Node\Expr\New_;
9+
use PhpParser\Node\Expr\Variable;
10+
use PhpParser\Node\Name\FullyQualified;
11+
use PhpParser\Node\Param;
712
use PhpParser\Node\Stmt\ClassMethod;
13+
use PhpParser\Node\Stmt\Expression;
14+
use PhpParser\NodeVisitor;
815
use Rector\NodeNameResolver\NodeNameResolver;
9-
use Rector\PhpParser\Node\Value\ValueResolver;
16+
use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser;
17+
use Rector\PhpParser\Node\BetterNodeFinder;
18+
use Rector\Symfony\Enum\SymfonyClass;
1019

1120
final readonly class OutputInputSymfonyStyleReplacer
1221
{
1322
public function __construct(
1423
private NodeNameResolver $nodeNameResolver,
15-
private ValueResolver $valueResolver,
24+
private BetterNodeFinder $betterNodeFinder,
25+
private SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
1626
) {
1727
}
1828

1929
public function replace(ClassMethod $executeClassMethod): void
2030
{
21-
dump(123);
22-
die;
31+
$symfonyStyleVariableName = $this->matchSymfonyStyleNewVariableName($executeClassMethod);
32+
33+
// nothing to update here
34+
if ($symfonyStyleVariableName === null) {
35+
return;
36+
}
37+
38+
// 1. add symfony style param
39+
$executeClassMethod->params[] = new Param(
40+
new Variable($symfonyStyleVariableName),
41+
null,
42+
new FullyQualified(SymfonyClass::SYMFONY_STYLE)
43+
);
44+
45+
// 2. remove new symfony style inside
46+
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
47+
$executeClassMethod,
48+
function (\PhpParser\Node $node) {
49+
if (! $node instanceof Expression) {
50+
return null;
51+
}
52+
53+
if (! $node->expr instanceof Assign) {
54+
return null;
55+
}
56+
57+
$assign = $node->expr;
58+
if (! $this->isSymfonyStyleNewAssign($assign)) {
59+
return null;
60+
}
61+
62+
return NodeVisitor::REMOVE_NODE;
63+
}
64+
);
65+
}
66+
67+
private function matchSymfonyStyleNewVariableName(ClassMethod $executeClassMethod): ?string
68+
{
69+
/** @var Assign[] $assigns */
70+
$assigns = $this->betterNodeFinder->findInstancesOfScoped($executeClassMethod->stmts, Assign::class);
71+
72+
foreach ($assigns as $assign) {
73+
if (! $this->isSymfonyStyleNewAssign($assign)) {
74+
continue;
75+
}
76+
77+
if (! $assign->var instanceof Variable) {
78+
continue;
79+
}
80+
81+
return $this->nodeNameResolver->getName($assign->var);
82+
}
83+
84+
return null;
85+
}
86+
87+
private function isSymfonyStyleNewAssign(Assign $assign): bool
88+
{
89+
if (! $assign->expr instanceof New_) {
90+
return false;
91+
}
92+
93+
$new = $assign->expr;
94+
95+
return $this->nodeNameResolver->isName($new->class, SymfonyClass::SYMFONY_STYLE);
2396
}
2497
}

rules/Symfony73/Rector/Class_/ConstraintOptionsToNamedArgumentsRector.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66

77
use PhpParser\Node;
88
use PhpParser\Node\Arg;
9-
<<<<<<< HEAD
109
use PhpParser\Node\ArrayItem;
11-
=======
12-
>>>>>>> cf6f011c (fixup! fixup! apply rector)
1310
use PhpParser\Node\Expr;
1411
use PhpParser\Node\Expr\Array_;
1512
use PhpParser\Node\Expr\New_;
@@ -91,8 +88,6 @@ public function refactor(Node $node): ?Node
9188
$namedArgs = [];
9289

9390
foreach ($array->items as $item) {
94-
<<<<<<< HEAD
95-
<<<<<<< HEAD
9691
if (! $item instanceof ArrayItem) {
9792
continue;
9893
}
@@ -103,12 +98,6 @@ public function refactor(Node $node): ?Node
10398
return null;
10499
}
105100

106-
=======
107-
if (! $item instanceof ArrayItem || !$item->key instanceof Expr) {
108-
>>>>>>> 80213bcd (apply rector)
109-
=======
110-
if (! $item instanceof ArrayItem || ! $item->key instanceof Expr) {
111-
>>>>>>> cf6f011c (fixup! fixup! apply rector)
112101
continue;
113102
}
114103

rules/Symfony73/Rector/Class_/InvokableCommandInputAttributeRector.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Rector\Symfony\Enum\SymfonyClass;
2121
use Rector\Symfony\Symfony73\NodeAnalyzer\CommandArgumentsAndOptionsResolver;
2222
use Rector\Symfony\Symfony73\NodeFactory\CommandInvokeParamsFactory;
23+
use Rector\Symfony\Symfony73\NodeTransformer\CommandUnusedInputOutputRemover;
2324
use Rector\Symfony\Symfony73\NodeTransformer\ConsoleOptionAndArgumentMethodCallVariableReplacer;
2425
use Rector\Symfony\Symfony73\NodeTransformer\OutputInputSymfonyStyleReplacer;
2526
use Rector\ValueObject\MethodName;
@@ -44,7 +45,8 @@ public function __construct(
4445
private readonly CommandInvokeParamsFactory $commandInvokeParamsFactory,
4546
private readonly ConsoleOptionAndArgumentMethodCallVariableReplacer $consoleOptionAndArgumentMethodCallVariableReplacer,
4647
private readonly VisibilityManipulator $visibilityManipulator,
47-
private readonly OutputInputSymfonyStyleReplacer $outputInputSymfonyStyleReplacer
48+
private readonly OutputInputSymfonyStyleReplacer $outputInputSymfonyStyleReplacer,
49+
private readonly CommandUnusedInputOutputRemover $commandUnusedInputOutputRemover
4850
) {
4951
}
5052

@@ -189,6 +191,9 @@ public function refactor(Node $node): ?Class_
189191
$this->consoleOptionAndArgumentMethodCallVariableReplacer->replace($executeClassMethod);
190192
}
191193

194+
$this->outputInputSymfonyStyleReplacer->replace($executeClassMethod);
195+
$this->commandUnusedInputOutputRemover->remove($executeClassMethod);
196+
192197
return $node;
193198
}
194199

src/Enum/SymfonyClass.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,9 @@ final class SymfonyClass
140140
* @var string
141141
*/
142142
public const PARAMETER_BAG = 'Symfony\Component\HttpFoundation\ParameterBag';
143+
144+
/**
145+
* @var string
146+
*/
147+
public const SYMFONY_STYLE = 'Symfony\Component\Console\Style\SymfonyStyle';
143148
}

0 commit comments

Comments
 (0)