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
Expand Up @@ -44,8 +44,8 @@ use Symfony\Component\Console\Output\OutputInterface;
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(name: 'option-with-hyphen')]
$option_with_hyphen): int
?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
@@ -0,0 +1,71 @@
<?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\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

#[AsCommand(name: 'some_name')]
final class OptionNameConstant extends Command
{
private const ARGUMENT_NAME = 'some-argument';

private const OPTION_NAME = 'some-option';

public function configure()
{
$this->addArgument(self::ARGUMENT_NAME);
$this->addOption(self::OPTION_NAME);
}

public function execute(InputInterface $input, OutputInterface $output): int
{
$someArgument = $input->getArgument(self::ARGUMENT_NAME);

$someOption = $input->getOption(self::OPTION_NAME);

// ...

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\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

#[AsCommand(name: 'some_name')]
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
{
$someArgument = $some_argument;

$someOption = $some_option;

// ...

return 1;
}
}

?>
5 changes: 5 additions & 0 deletions rules/Symfony73/NodeAnalyzer/CommandArgumentsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
namespace Rector\Symfony\Symfony73\NodeAnalyzer;

use PhpParser\Node\Stmt\ClassMethod;
use Rector\PhpParser\Node\Value\ValueResolver;
use Rector\Symfony\Symfony73\NodeFinder\MethodCallFinder;
use Rector\Symfony\Symfony73\ValueObject\CommandArgument;

final readonly class CommandArgumentsResolver
{
public function __construct(
private MethodCallFinder $methodCallFinder,
private ValueResolver $valueResolver
) {
}

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

$argumentName = $this->valueResolver->getValue($addArgumentArgs[0]->value);

$commandArguments[] = new CommandArgument(
$argumentName,
$addArgumentArgs[0]->value,
$addArgumentArgs[1]->value ?? null,
$addArgumentArgs[2]->value ?? null,
Expand Down
22 changes: 10 additions & 12 deletions rules/Symfony73/NodeAnalyzer/CommandOptionsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

namespace Rector\Symfony\Symfony73\NodeAnalyzer;

use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Exception\ShouldNotHappenException;
use Rector\PhpParser\Node\Value\ValueResolver;
use Rector\Symfony\Symfony73\NodeFinder\MethodCallFinder;
use Rector\Symfony\Symfony73\ValueObject\CommandOption;

final readonly class CommandOptionsResolver
{
public function __construct(
private MethodCallFinder $methodCallFinder,
private ValueResolver $valueResolver
) {
}

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

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

$shortcutExpr = $addOptionArgs[1]->value ?? null;
$modeExpr = $addOptionArgs[2]->value ?? null;
$descriptionExpr = $addOptionArgs[3]->value ?? null;

$commandOptions[] = new CommandOption($nameArgValue, $shortcutExpr, $modeExpr, $descriptionExpr);
$commandOptions[] = new CommandOption(
$optionName,
$addOptionArgs[0]->value,
$addOptionArgs[1]->value ?? null,
$addOptionArgs[2]->value ?? null,
$addOptionArgs[3]->value ?? null
);
}

return $commandOptions;
Expand Down
44 changes: 28 additions & 16 deletions rules/Symfony73/NodeFactory/CommandInvokeParamsFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
final readonly class CommandInvokeParamsFactory
{
public function __construct(
private ValueResolver $valueResolver
private ValueResolver $valueResolver,
) {
}

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

foreach ($commandArguments as $commandArgument) {
$argumentName = (string) $this->valueResolver->getValue($commandArgument->getName());
$variableName = str_replace('-', '_', $argumentName);

$variableName = $this->createCamelCase($commandArgument->getNameValue());
$argumentParam = new Param(new Variable($variableName));

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

$modeValue = $this->valueResolver->getValue($commandArgument->getMode());
if ($modeValue === null || $modeValue === 2) {
// is argument optional?
if ($commandArgument->getMode() === null) {
$argumentParam->type = new NullableType($argumentParam->type);
} elseif ($this->valueResolver->isValue($commandArgument->getMode(), 2)) {
$argumentParam->type = new NullableType($argumentParam->type);
}

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

$argumentArgs = [new Arg(value: $commandArgument->getName(), name: new Identifier('name'))];

if ($commandArgument->getDescription() instanceof Expr) {
$argumentArgs[] = new Arg(value: $commandArgument->getDescription(), name: new Identifier(
'description'
));
}

$argumentParam->attrGroups[] = new AttributeGroup([
new Attribute(
new FullyQualified(SymfonyAttribute::COMMAND_ARGUMENT),
[
new Arg(value: $commandArgument->getName(), name: new Identifier('name')),
new Arg(value: $commandArgument->getDescription(), name: new Identifier('description')),
]
),
new Attribute(new FullyQualified(SymfonyAttribute::COMMAND_ARGUMENT), $argumentArgs),
]);

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

foreach ($commandOptions as $commandOption) {
$optionName = $commandOption->getStringName();
$variableName = str_replace('-', '_', $optionName);

$variableName = $this->createCamelCase($commandOption->getNameValue());
$optionParam = new Param(new Variable($variableName));

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

return $optionParams;
}

private function createCamelCase(string $value): string
{
// Replace dashes/underscores with spaces
$value = str_replace(['-', '_'], ' ', strtolower($value));

// Capitalize each word, then remove spaces
$value = str_replace(' ', '', ucwords($value));

// Lowercase first character to make it camelCase
return lcfirst($value);
}
}
6 changes: 6 additions & 0 deletions rules/Symfony73/ValueObject/CommandArgument.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
final readonly class CommandArgument
{
public function __construct(
private string $nameValue,
private Expr $name,
private ?Expr $mode,
private ?Expr $description
) {
}

public function getNameValue(): string
{
return $this->nameValue;
}

public function getName(): Expr
{
return $this->name;
Expand Down
11 changes: 3 additions & 8 deletions rules/Symfony73/ValueObject/CommandOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
namespace Rector\Symfony\Symfony73\ValueObject;

use PhpParser\Node\Expr;
use PhpParser\Node\Scalar\String_;
use Rector\Exception\NotImplementedYetException;

final readonly class CommandOption
{
public function __construct(
private string $nameValue,
private Expr $name,
private ?Expr $shortcut,
private ?Expr $mode,
Expand Down Expand Up @@ -38,12 +37,8 @@ public function getDescription(): ?Expr
return $this->description;
}

public function getStringName(): string
public function getNameValue(): string
{
if ($this->name instanceof String_) {
return $this->name->value;
}

throw new NotImplementedYetException(sprintf('Add more options to "%s"', self::class));
return $this->nameValue;
}
}