Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttributeRector\Fixture;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand('app:my-command')]
final class MyCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->info('Great success!');

return 1;
}
}
?>
-----
<?php

namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandInputAttributeRector\Fixture;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand('app:my-command')]
final class MyCommand
{
public function __invoke(\Symfony\Component\Console\Style\SymfonyStyle $io): int
{
$io->info('Great success!');
return 1;
}
}
?>
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class NameWithHyphen
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument-with-hyphen', description: 'Argument description')]
?string $argument_with_hyphen, #[\Symfony\Component\Console\Attribute\Option]
$option_with_hyphen, OutputInterface $output): int
$option_with_hyphen): int
{
$argument = $argument_with_hyphen;
$option = $option_with_hyphen;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ final class SomeCommand
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument, #[\Symfony\Component\Console\Attribute\Option]
$option, OutputInterface $output): int
$option): int
{
$someArgument = $argument;
$someOption = $option;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ final class SomeCommandWithMethodChaining
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument, #[\Symfony\Component\Console\Attribute\Option]
$option, OutputInterface $output): int
$option): int
{
$someArgument = $argument;
$someOption = $option;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ final class SomeCommandWithSetHelp

public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument, #[\Symfony\Component\Console\Attribute\Option]
$option, OutputInterface $output): int
$option): int
{
$someArgument = $argument;
$someOption = $option;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,22 @@ use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'some_name')]
final class WithMultipleArgumentsOptionsFluent
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument1', description: 'Argument1 description')]
string $argument1, #[\Symfony\Component\Console\Attribute\Argument(name: 'argument2', description: 'Argument2 description')]
string $argument2, #[\Symfony\Component\Console\Attribute\Option]
$option1, #[\Symfony\Component\Console\Attribute\Option]
$option2, OutputInterface $output): int
public function __invoke(
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument1', description: 'Argument1 description')]
string $argument1,
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument2', description: 'Argument2 description')]
string $argument2,
#[\Symfony\Component\Console\Attribute\Option]
$option1,
#[\Symfony\Component\Console\Attribute\Option]
$option2
): int
{
$arg1 = $argument1;
$arg2 = $argument2;
$opt1 = $option1;
$opt2 = $option2;

// ...

return 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,22 @@ use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'some_name')]
final class WithMultipleArgumentsOptionsNotFluent
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument1', description: 'Argument1 description')]
string $argument1, #[\Symfony\Component\Console\Attribute\Argument(name: 'argument2', description: 'Argument2 description')]
string $argument2, #[\Symfony\Component\Console\Attribute\Option]
$option1, #[\Symfony\Component\Console\Attribute\Option]
$option2, OutputInterface $output): int
public function __invoke(
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument1', description: 'Argument1 description')]
string $argument1,
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument2', description: 'Argument2 description')]
string $argument2,
#[\Symfony\Component\Console\Attribute\Option]
$option1,
#[\Symfony\Component\Console\Attribute\Option]
$option2
): int
{
$arg1 = $argument1;
$arg2 = $argument2;
$opt1 = $option1;
$opt2 = $option2;

// ...

return 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ final class WithOptionalArgument
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
?string $argument, #[\Symfony\Component\Console\Attribute\Option]
$option, OutputInterface $output): int
$option): int
{
$someArgument = $argument;
$someOption = $option;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ final class WithOverride
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument,
#[\Symfony\Component\Console\Attribute\Option]
$option,
OutputInterface $output
$option
): int
{
$someArgument = $argument;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'some_name')]
final class WithoutConfigure
{
public function __invoke(OutputInterface $output): int
public function __invoke(): int
{
// ...

return 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Rector\Symfony\Symfony73\NodeTransformer;

use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\PhpParser\Node\BetterNodeFinder;

final readonly class CommandUnusedInputOutputRemover
{
/**
* @var string[]
*/
private const VARIABLE_NAMES = ['input', 'output'];

public function __construct(
private NodeNameResolver $nodeNameResolver,
private BetterNodeFinder $betterNodeFinder
) {

}

public function remove(ClassMethod $executeClassMethod): void
{
foreach (self::VARIABLE_NAMES as $variableName) {
$inputVariable = $this->betterNodeFinder->findVariableOfName($executeClassMethod->stmts, $variableName);

// is used → skip
if ($inputVariable instanceof Variable) {
continue;
}

$this->removeParameterByName($executeClassMethod, $variableName);
}
}

private function removeParameterByName(ClassMethod $classMethod, string $paramName): void
{
foreach ($classMethod->getParams() as $key => $param) {
if (! $this->nodeNameResolver->isName($param->var, $paramName)) {
continue;
}

unset($classMethod->params[$key]);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace Rector\Symfony\Symfony73\NodeTransformer;

use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser;
use Rector\PhpParser\Node\Value\ValueResolver;

final readonly class ConsoleOptionAndArgumentMethodCallVariableReplacer
{
public function __construct(
private NodeNameResolver $nodeNameResolver,
private SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
private ValueResolver $valueResolver,
) {

}

public function replace(ClassMethod $executeClassMethod): void
{
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($executeClassMethod->stmts, function (
Node $node
): ?Variable {
if (! $node instanceof MethodCall) {
return null;
}

if (! $this->nodeNameResolver->isName($node->var, 'input')) {
return null;
}

if (! $this->nodeNameResolver->isNames($node->name, ['getOption', 'getArgument'])) {
return null;
}

$firstArgValue = $node->getArgs()[0]
->value;

if ($firstArgValue instanceof ClassConstFetch || $firstArgValue instanceof ConstFetch) {
$variableName = $this->valueResolver->getValue($firstArgValue);
return new Variable(str_replace('-', '_', $variableName));
}

if (! $firstArgValue instanceof String_) {
// unable to resolve argument/option name
throw new ShouldNotHappenException();
}

$variableName = $firstArgValue->value;
return new Variable(str_replace('-', '_', $variableName));
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace Rector\Symfony\Symfony73\NodeTransformer;

use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\NodeVisitor;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser;
use Rector\PhpParser\Node\BetterNodeFinder;
use Rector\Symfony\Enum\SymfonyClass;

final readonly class OutputInputSymfonyStyleReplacer
{
public function __construct(
private NodeNameResolver $nodeNameResolver,
private BetterNodeFinder $betterNodeFinder,
private SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
) {
}

public function replace(ClassMethod $executeClassMethod): void
{
$symfonyStyleVariableName = $this->matchSymfonyStyleNewVariableName($executeClassMethod);

// nothing to update here
if ($symfonyStyleVariableName === null) {
return;
}

// 1. add symfony style param
$executeClassMethod->params[] = new Param(
new Variable($symfonyStyleVariableName),
null,
new FullyQualified(SymfonyClass::SYMFONY_STYLE)
);

// 2. remove new symfony style inside
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
$executeClassMethod,
function (Node $node): ?int {
if (! $node instanceof Expression) {
return null;
}

if (! $node->expr instanceof Assign) {
return null;
}

$assign = $node->expr;
if (! $this->isSymfonyStyleNewAssign($assign)) {
return null;
}

return NodeVisitor::REMOVE_NODE;
}
);
}

private function matchSymfonyStyleNewVariableName(ClassMethod $executeClassMethod): ?string
{
/** @var Assign[] $assigns */
$assigns = $this->betterNodeFinder->findInstancesOfScoped($executeClassMethod->stmts, Assign::class);

foreach ($assigns as $assign) {
if (! $this->isSymfonyStyleNewAssign($assign)) {
continue;
}

if (! $assign->var instanceof Variable) {
continue;
}

return $this->nodeNameResolver->getName($assign->var);
}

return null;
}

private function isSymfonyStyleNewAssign(Assign $assign): bool
{
if (! $assign->expr instanceof New_) {
return false;
}

$new = $assign->expr;

return $this->nodeNameResolver->isName($new->class, SymfonyClass::SYMFONY_STYLE);
}
}
Loading