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
2 changes: 1 addition & 1 deletion rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
'*/Source/*',
'*/Source*/*',
'*/tests/*/Fixture*/Expected/*',
StringClassNameToClassConstantRector::class => [__DIR__ . '/config'],
StringClassNameToClassConstantRector::class => [__DIR__ . '/config', __DIR__ . '/src/Enum'],
UseClassKeywordForClassNameResolutionRector::class => [__DIR__ . '/config'],

RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ use Symfony\Component\Console\Output\OutputInterface;
)]
class OptionWithOptionalValue
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Option(name: 'some-array', mode: InputOption::VALUE_IS_ARRAY)]
array $someArray = ['third value'], #[\Symfony\Component\Console\Attribute\Option(name: 'no-default-array', mode: InputOption::VALUE_IS_ARRAY)]
array $noDefaultArray): int
public function __invoke(
#[\Symfony\Component\Console\Attribute\Option(name: 'some-array', mode: InputOption::VALUE_IS_ARRAY)]
array $someArray = ['third value'],
#[\Symfony\Component\Console\Attribute\Option(name: 'no-default-array', mode: InputOption::VALUE_IS_ARRAY)]
array $noDefaultArray
): int
{
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ use Symfony\Component\Console\Output\OutputInterface;
class NameFromConstant
{
private const string ARGUMENT_NAME = 'name';

public function __invoke(
#[\Symfony\Component\Console\Attribute\Argument(name: self::ARGUMENT_NAME, description: 'The name of the person to greet.')]
?string $name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ use Symfony\Component\Console\Output\OutputInterface;
)]
class NameWithHyphen
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument-with-hyphen', description: 'Argument description')]
?string $argumentWithHyphen, #[\Symfony\Component\Console\Attribute\Option(name: 'option-with-hyphen')]
$optionWithHyphen): int
public function __invoke(
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument-with-hyphen', description: 'Argument description')]
?string $argumentWithHyphen,
#[\Symfony\Component\Console\Attribute\Option(name: 'option-with-hyphen')]
$optionWithHyphen
): int
{
$argument = $argument_with_hyphen;
$option = $option_with_hyphen;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,16 @@ final class OptionNameConstant
private const ARGUMENT_NAME = 'some-argument';

private const OPTION_NAME = 'some-option';

public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: self::ARGUMENT_NAME)]
?string $someArgument, #[\Symfony\Component\Console\Attribute\Option(name: self::OPTION_NAME)]
$someOption): int
public function __invoke(
#[\Symfony\Component\Console\Attribute\Argument(name: self::ARGUMENT_NAME)]
?string $someArgument,
#[\Symfony\Component\Console\Attribute\Option(name: self::OPTION_NAME)]
$someOption
): int
{
$someArgument = $some_argument;

$someOption = $some_option;

// ...

return 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,16 @@ use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'some_name')]
final class SomeCommand
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument, #[\Symfony\Component\Console\Attribute\Option(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
bool $option = false): int
public function __invoke(
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument,
#[\Symfony\Component\Console\Attribute\Option(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
bool $option = false
): int
{
$someArgument = $argument;
$someOption = $option;

// ...

return 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,16 @@ use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'some_name')]
final class SomeCommandWithMethodChaining
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument, #[\Symfony\Component\Console\Attribute\Option(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
bool $option = false): int
public function __invoke(
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument,
#[\Symfony\Component\Console\Attribute\Option(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
bool $option = false
): int
{
$someArgument = $argument;
$someOption = $option;

// ...

return 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,16 @@ final class SomeCommandWithSetHelp
$this->setHelp('argument');
}

public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument, #[\Symfony\Component\Console\Attribute\Option(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
bool $option = false): int
public function __invoke(
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
string $argument,
#[\Symfony\Component\Console\Attribute\Option(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
bool $option = false
): int
{
$someArgument = $argument;
$someOption = $option;

// ...

return 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,16 @@ use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'some_name')]
final class WithOptionalArgument
{
public function __invoke(#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
?string $argument, #[\Symfony\Component\Console\Attribute\Option(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
bool $option = false): int
public function __invoke(
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument', description: 'Argument description')]
?string $argument,
#[\Symfony\Component\Console\Attribute\Option(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
bool $option = false
): int
{
$someArgument = $argument;
$someOption = $option;

// ...

return 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use Rector\PhpParser\Node\Value\ValueResolver;
use Rector\Symfony\Enum\SensioAttribute;

final class ParamConverterClassesResolver
final readonly class ParamConverterClassesResolver
{
public function __construct(
private AttributeFinder $attributeFinder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Rector\Symfony\CodeQuality\Rector\Class_;

use Exception;
use PhpParser\Node;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\PropertyFetch;
Expand All @@ -26,6 +27,7 @@
use Rector\VendorLocker\ParentClassMethodTypeOverrideGuard;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Throwable;

/**
* @see \Rector\Symfony\Tests\CodeQuality\Rector\Class_\ControllerMethodInjectionToConstructorRector\ControllerMethodInjectionToConstructorRectorTest
Expand Down Expand Up @@ -154,8 +156,8 @@ public function refactor(Node $node): ?Node
SymfonyClass::REQUEST,
FosClass::PARAM_FETCHER,
SymfonyClass::UUID,
\Throwable::class,
\Exception::class,
Throwable::class,
Exception::class,
...$entityClasses,
]
)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@

namespace Rector\Symfony\Symfony73\Rector\Class_;

use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use Rector\Doctrine\NodeAnalyzer\AttributeFinder;
use Rector\Privatization\NodeManipulator\VisibilityManipulator;
use Rector\Rector\AbstractRector;
use Rector\Symfony\Enum\CommandMethodName;
use Rector\Symfony\Enum\SymfonyAttribute;
Expand Down Expand Up @@ -46,7 +47,6 @@ public function __construct(
private readonly CommandOptionsResolver $commandOptionsResolver,
private readonly CommandInvokeParamsFactory $commandInvokeParamsFactory,
private readonly ConsoleOptionAndArgumentMethodCallVariableReplacer $consoleOptionAndArgumentMethodCallVariableReplacer,
private readonly VisibilityManipulator $visibilityManipulator,
private readonly OutputInputSymfonyStyleReplacer $outputInputSymfonyStyleReplacer,
private readonly CommandUnusedInputOutputRemover $commandUnusedInputOutputRemover
) {
Expand Down Expand Up @@ -141,48 +141,42 @@ public function refactor(Node $node): ?Class_
return null;
}

$executeClassMethod = $node->getMethod(CommandMethodName::EXECUTE);
if (! $executeClassMethod instanceof ClassMethod) {
return null;
}
foreach ($node->stmts as $key => $classStmt) {
if (! $classStmt instanceof ClassMethod) {
continue;
}

// 1. rename execute to __invoke
$executeClassMethod->name = new Identifier(MethodName::INVOKE);
$this->visibilityManipulator->makePublic($executeClassMethod);
if (! $this->isName($classStmt, CommandMethodName::EXECUTE)) {
continue;
}

// 2. fetch configure method to get arguments and options metadata
$configureClassMethod = $node->getMethod(CommandMethodName::CONFIGURE);
$executeClassMethod = $classStmt;

if ($configureClassMethod instanceof ClassMethod) {
// 3. create arguments and options parameters
$commandArguments = $this->commandArgumentsResolver->resolve($configureClassMethod);
$commandOptions = $this->commandOptionsResolver->resolve($configureClassMethod);
// 1. rename execute to __invoke
$invokeClassMethod = new ClassMethod(MethodName::INVOKE);
$invokeClassMethod->flags |= Modifiers::PUBLIC;
$invokeClassMethod->returnType = new Identifier('int');
$invokeClassMethod->stmts = $classStmt->stmts;

// 4. remove configure() method
$this->removeConfigureClassMethodIfNotUseful($node);
$invokeParams = $this->createInvokeParams($node);

// 5. decorate __invoke method with attributes
$invokeParams = $this->commandInvokeParamsFactory->createParams($commandArguments, $commandOptions);
} else {
$invokeParams = [];
}
$invokeClassMethod->params = array_merge($invokeParams, [$executeClassMethod->params[1]]);

$executeClassMethod->params = array_merge($invokeParams, [$executeClassMethod->params[1]]);
// 6. remove parent class
$node->extends = null;

// 6. remove parent class
$node->extends = null;
// 7. replace input->getArgument() and input->getOption() calls with direct variable access
$this->consoleOptionAndArgumentMethodCallVariableReplacer->replace($invokeClassMethod);

$this->removeOverrideAttributeAsDifferentMethod($executeClassMethod);
$this->outputInputSymfonyStyleReplacer->replace($invokeClassMethod);
$this->commandUnusedInputOutputRemover->remove($invokeClassMethod);

if ($configureClassMethod instanceof ClassMethod) {
// 7. replace input->getArgument() and input->getOption() calls with direct variable access
$this->consoleOptionAndArgumentMethodCallVariableReplacer->replace($executeClassMethod);
}
$node->stmts[$key] = $invokeClassMethod;

$this->outputInputSymfonyStyleReplacer->replace($executeClassMethod);
$this->commandUnusedInputOutputRemover->remove($executeClassMethod);
return $node;
}

return $node;
return null;
}

/**
Expand Down Expand Up @@ -256,19 +250,26 @@ private function isFluentArgumentOptionChain(MethodCall $methodCall): bool
return $current instanceof Variable && $this->isName($current, 'this');
}

private function removeOverrideAttributeAsDifferentMethod(ClassMethod $executeClassMethod): void
/**
* @return Param[]
*/
private function createInvokeParams(Class_ $class): array
{
foreach ($executeClassMethod->attrGroups as $attrGroupKey => $attrGroup) {
foreach ($attrGroup->attrs as $attributeKey => $attr) {
if ($this->isName($attr->name, 'Override')) {
unset($attrGroup->attrs[$attributeKey]);
}
}
// 1. fetch configure method to get arguments and options metadata
$configureClassMethod = $class->getMethod(CommandMethodName::CONFIGURE);

// is attribute empty? remove whole group
if ($attrGroup->attrs === []) {
unset($executeClassMethod->attrGroups[$attrGroupKey]);
}
if ($configureClassMethod instanceof ClassMethod) {
// 2. create arguments and options parameters
$commandArguments = $this->commandArgumentsResolver->resolve($configureClassMethod);
$commandOptions = $this->commandOptionsResolver->resolve($configureClassMethod);

// 3. remove configure() method
$this->removeConfigureClassMethodIfNotUseful($class);

// 4. decorate __invoke method with attributes
return $this->commandInvokeParamsFactory->createParams($commandArguments, $commandOptions);
}

return [];
}
}