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,28 @@
<?php

namespace Rector\Symfony\Tests\Symfony51\Rector\ClassMethod\RouteCollectionBuilderToRoutingConfiguratorRector\Fixture;

use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\RouteCollectionBuilder;

final class ConcreteMicroKernel extends Kernel
{
use MicroKernelTrait;

protected function configureRoutes(RouteCollectionBuilder $routes)
{
$routes->add('/admin', 'App\Controller\AdminController::dashboard', 'admin_dashboard');
}

public function registerBundles()
{
}

public function registerContainerConfiguration(LoaderInterface $loader)
{
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

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

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'app:hello',
)]
class ImplicitNoValueBoolOption extends Command
{
protected function configure(): void
{
$this->addOption('third', null, InputOption::VALUE_NONE);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
}
}

?>
-----
<?php

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

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'app:hello',
)]
class ImplicitNoValueBoolOption
{
public function __invoke(
#[\Symfony\Component\Console\Attribute\Option(name: 'third', mode: InputOption::VALUE_NONE)]
bool $third = false
): int
{
}
}

?>
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(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
$option): int
bool $option = false): 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(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
$option): int
bool $option = false): 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(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
$option): int
bool $option = false): int
{
$someArgument = $argument;
$someOption = $option;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ final class WithMultipleArgumentsOptionsFluent
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument2', description: 'Argument2 description')]
string $argument2,
#[\Symfony\Component\Console\Attribute\Option(name: 'option1', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option1 description')]
$option1,
bool $option1 = false,
#[\Symfony\Component\Console\Attribute\Option(name: 'option2', shortcut: 'p', mode: InputOption::VALUE_NONE, description: 'Option2 description')]
$option2
bool $option2 = false
): int
{
$arg1 = $argument1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ final class WithMultipleArgumentsOptionsNotFluent
#[\Symfony\Component\Console\Attribute\Argument(name: 'argument2', description: 'Argument2 description')]
string $argument2,
#[\Symfony\Component\Console\Attribute\Option(name: 'option1', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option1 description')]
$option1,
bool $option1 = false,
#[\Symfony\Component\Console\Attribute\Option(name: 'option2', shortcut: 'p', mode: InputOption::VALUE_NONE, description: 'Option2 description')]
$option2
bool $option2 = false
): int
{
$arg1 = $argument1;
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(name: 'option', shortcut: 'o', mode: InputOption::VALUE_NONE, description: 'Option description')]
$option): int
bool $option = false): int
{
$someArgument = $argument;
$someOption = $option;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ final class WithOverride
#[\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')]
$option
bool $option = false
): int
{
$someArgument = $argument;
Expand Down
20 changes: 19 additions & 1 deletion rules/Symfony73/NodeAnalyzer/CommandOptionsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public function resolve(ClassMethod $configureClassMethod): array

$optionName = $this->valueResolver->getValue($addOptionArgs[0]->value);

$isImplicitBoolean = $this->isImplicitBoolean($addOptionArgs);

$commandOptions[] = new CommandOption(
$optionName,
$addOptionArgs[0]->value,
Expand All @@ -44,6 +46,7 @@ public function resolve(ClassMethod $configureClassMethod): array
$addOptionArgs[3]->value ?? null,
$addOptionArgs[4]->value ?? null,
$this->isArrayMode($addOptionArgs),
$isImplicitBoolean,
$this->resolveDefaultType($addOptionArgs)
);
}
Expand Down Expand Up @@ -75,7 +78,22 @@ private function isArrayMode(array $args): bool
}

$modeValue = $this->valueResolver->getValue($modeExpr);
// binary check for InputOptions::VALUE_IS_ARRAY
// binary check for InputOption::VALUE_IS_ARRAY
return (bool) ($modeValue & 8);
}

/**
* @param Arg[] $args
*/
private function isImplicitBoolean(array $args): bool
{
$modeExpr = $args[2]->value ?? null;
if (! $modeExpr instanceof Expr) {
return false;
}

$modeValue = $this->valueResolver->getValue($modeExpr);
// binary check for InputOption::VALUE_NONE
return (bool) ($modeValue & 1);
}
}
9 changes: 9 additions & 0 deletions rules/Symfony73/NodeFactory/CommandInvokeParamsFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Rector\Symfony\Symfony73\NodeFactory;

use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Name;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Attribute;
Expand Down Expand Up @@ -99,6 +101,8 @@ private function createOptionParams(array $commandOptions): array

if ($commandOption->getDefault() instanceof Expr) {
$optionParam->default = $commandOption->getDefault();
} elseif ($commandOption->isImplicitBoolean()) {
$optionParam->default = new ConstFetch(new Name('false'));
}

$this->decorateParamType($optionParam, $commandOption);
Expand Down Expand Up @@ -165,6 +169,11 @@ private function decorateParamType(
Param $argumentParam,
CommandArgument|CommandOption $commandArgumentOrOption
): void {
if ($commandArgumentOrOption instanceof CommandOption && $commandArgumentOrOption->isImplicitBoolean()) {
$argumentParam->type = new Identifier('bool');
return;
}

if ($commandArgumentOrOption->isArray()) {
$argumentParam->type = new Identifier('array');
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ public function refactor(Node $node): ?Class_
if ($configureClassMethod instanceof ClassMethod) {
// 3. create arguments and options parameters
$commandArguments = $this->commandArgumentsResolver->resolve($configureClassMethod);

$commandOptions = $this->commandOptionsResolver->resolve($configureClassMethod);

// 4. remove configure() method
Expand All @@ -173,17 +172,7 @@ public function refactor(Node $node): ?Class_
// 6. remove parent class
$node->extends = null;

foreach ($executeClassMethod->attrGroups as $attrGroupKey => $attrGroup) {
foreach ($attrGroup->attrs as $attributeKey => $attr) {
if ($this->isName($attr->name, 'Override')) {
unset($attrGroup->attrs[$attributeKey]);
}
}

if ($attrGroup->attrs === []) {
unset($executeClassMethod->attrGroups[$attrGroupKey]);
}
}
$this->removeOverrideAttributeAsDifferentMethod($executeClassMethod);

if ($configureClassMethod instanceof ClassMethod) {
// 7. replace input->getArgument() and input->getOption() calls with direct variable access
Expand Down Expand Up @@ -266,4 +255,20 @@ private function isFluentArgumentOptionChain(MethodCall $methodCall): bool
// the left-most var must be $this
return $current instanceof Variable && $this->isName($current, 'this');
}

private function removeOverrideAttributeAsDifferentMethod(ClassMethod $executeClassMethod): void
{
foreach ($executeClassMethod->attrGroups as $attrGroupKey => $attrGroup) {
foreach ($attrGroup->attrs as $attributeKey => $attr) {
if ($this->isName($attr->name, 'Override')) {
unset($attrGroup->attrs[$attributeKey]);
}
}

// is attribute empty? remove whole group
if ($attrGroup->attrs === []) {
unset($executeClassMethod->attrGroups[$attrGroupKey]);
}
}
}
}
6 changes: 6 additions & 0 deletions rules/Symfony73/ValueObject/CommandOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function __construct(
private ?Expr $description,
private ?Expr $default,
private bool $isArray,
private bool $isImplicitBoolean,
private ?Type $defaultType
) {
}
Expand Down Expand Up @@ -60,4 +61,9 @@ public function isArray(): bool
{
return $this->isArray;
}

public function isImplicitBoolean(): bool
{
return $this->isImplicitBoolean;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ class ReplaceNewCookieParameterValue
}

?>

Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ class ReplaceNewCookieAndCreateParameterValue
}

?>

Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ class ReplaceCookieWithSameSiteParameterValue
}

?>

2 changes: 1 addition & 1 deletion tests/Set/Symfony73/Fixture/command_remove_config.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ 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')]
$option): int
bool $option = false): int
{
$someArgument = $argument;
$someOption = $option;
Expand Down