Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Symfony 6.3] Add ParamAndEnvAttributeRector #641

Merged
merged 1 commit into from
Jul 5, 2024
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
9 changes: 7 additions & 2 deletions config/sets/symfony/symfony63.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\Name\RenameClassRector;
use Rector\Symfony\Symfony63\Rector\Class_\ParamAndEnvAttributeRector;
use Rector\Symfony\Symfony63\Rector\Class_\SignalableCommandInterfaceReturnTypeRector;

// @see https://github.com/symfony/symfony/blob/6.3/UPGRADE-6.3.md
Expand All @@ -24,6 +25,10 @@
],
);

// @see https://github.com/symfony/symfony/commit/1650e3861b5fcd931e5d3eb1dd84bad764020d8e
$rectorConfig->rule(SignalableCommandInterfaceReturnTypeRector::class);
$rectorConfig->rules([
// @see https://github.com/symfony/symfony/commit/1650e3861b5fcd931e5d3eb1dd84bad764020d8e
SignalableCommandInterfaceReturnTypeRector::class,
// @see https://symfony.com/blog/new-in-symfony-6-3-dependency-injection-improvements#new-options-for-autowire-attribute
ParamAndEnvAttributeRector::class,
]);
};
30 changes: 29 additions & 1 deletion docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 83 Rules Overview
# 84 Rules Overview

## ActionSuffixRemoverRector

Expand Down Expand Up @@ -1069,6 +1069,34 @@ Turns old option names to new ones in FormTypes in Form in Symfony

<br>

## ParamAndEnvAttributeRector

Make param/env use in #[Attribute] more precise

- class: [`Rector\Symfony\Symfony63\Rector\Class_\ParamAndEnvAttributeRector`](../rules/Symfony63/Rector/Class_/ParamAndEnvAttributeRector.php)

```diff
namespace App\Service;

use Symfony\Component\DependencyInjection\Attribute\Autowire;

class MessageGenerator
{
public function __construct(
- #[Autowire('%kernel.debug%')]
+ #[Autowire(param: 'kernel.debug')]
bool $debugMode,

- #[Autowire('%env(SOME_ENV_VAR)%')]
+ #[Autowire(env: 'SOME_ENV_VAR')]
string $senderName,
) {
}
}
```

<br>

## ParamConverterAttributeToMapEntityAttributeRector

Replace ParamConverter attribute with mappings with the MapEntity attribute
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Rector\Symfony\Tests\Symfony63\Rector\Class_\ParamAndEnvAttributeRector\Fixture;

use Symfony\Component\DependencyInjection\Attribute\Autowire;

final class SimpleConvert
{
public function __construct(
#[Autowire('%kernel.debug%')]
bool $debugMode,

#[Autowire('%env(SOME_ENV_VAR)%')]
string $senderName,
) {
}
}

?>
-----
<?php

namespace Rector\Symfony\Tests\Symfony63\Rector\Class_\ParamAndEnvAttributeRector\Fixture;

use Symfony\Component\DependencyInjection\Attribute\Autowire;

final class SimpleConvert
{
public function __construct(
#[Autowire(param: 'kernel.debug')]
bool $debugMode,

#[Autowire(env: 'SOME_ENV_VAR')]
string $senderName,
) {
}
}

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

namespace Rector\Symfony\Tests\Symfony63\Rector\Class_\ParamAndEnvAttributeRector\Fixture;

use Symfony\Component\DependencyInjection\Attribute\Autowire;

final class SkipAlreadyConverted
{
public function __construct(
#[Autowire(param: '%kernel.debug%')]
bool $debugMode,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Rector\Symfony\Tests\Symfony63\Rector\Class_\ParamAndEnvAttributeRector\Fixture;

use Symfony\Component\DependencyInjection\Attribute\Autowire;

final class SkipFormatMissMatch
{
public function __construct(
#[Autowire('%kernel_ebug')]
bool $debugMode,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Symfony\Tests\Symfony63\Rector\Class_\ParamAndEnvAttributeRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class ParamAndEnvAttributeRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Symfony\Symfony63\Rector\Class_\ParamAndEnvAttributeRector;

return RectorConfig::configure()
->withRules([ParamAndEnvAttributeRector::class]);
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

namespace Rector\Symfony\Configs\NodeVisitor;

use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Expr\Array_;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt;
use PhpParser\NodeVisitorAbstract;
Expand Down
10 changes: 3 additions & 7 deletions rules/Configs/Rector/Class_/AutowireAttributeRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

namespace Rector\Symfony\Configs\Rector\Class_;

use PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
Expand All @@ -19,6 +19,7 @@
use Rector\Exception\ShouldNotHappenException;
use Rector\Rector\AbstractRector;
use Rector\Symfony\Configs\NodeAnalyser\ConfigServiceArgumentsResolver;
use Rector\Symfony\Enum\SymfonyAttribute;
use Rector\ValueObject\MethodName;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
Expand All @@ -36,11 +37,6 @@ final class AutowireAttributeRector extends AbstractRector implements Configurab
*/
public const CONFIGS_DIRECTORY = 'configs_directory';

/**
* @var string
*/
private const AUTOWIRE_CLASS = 'Symfony\Component\DependencyInjection\Attribute\Autowire';

private ?string $configsDirectory = null;

public function __construct(
Expand Down Expand Up @@ -209,6 +205,6 @@ private function createAutowireAttribute(string|Expr $value, string $argName): A

$args = [new Arg($value, name: new Identifier($argName))];

return new Attribute(new FullyQualified(self::AUTOWIRE_CLASS), $args);
return new Attribute(new FullyQualified(SymfonyAttribute::AUTOWIRE), $args);
}
}
160 changes: 160 additions & 0 deletions rules/Symfony63/Rector/Class_/ParamAndEnvAttributeRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<?php

declare(strict_types=1);

namespace Rector\Symfony\Symfony63\Rector\Class_;

use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Identifier;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Rector\AbstractRector;
use Rector\Symfony\Enum\SymfonyAttribute;
use Rector\ValueObject\MethodName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Rector\Symfony\Tests\Symfony63\Rector\Class_\ParamAndEnvAttributeRector\ParamAndEnvAttributeRectorTest
*
* @see https://symfony.com/blog/new-in-symfony-6-3-dependency-injection-improvements#new-options-for-autowire-attribute
*/
final class ParamAndEnvAttributeRector extends AbstractRector
{
/**
* @var string
* @see https://regex101.com/r/7vwGbH/1
*/
private const PARAMETER_REGEX = '#%(?<param>[\w\.]+)%$#';

/**
* @var string
* @see https://regex101.com/r/7xpVRP/1
*/
private const ENV_REGEX = '#%env\((?<env>\w+)\)%$#';

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Make param/env use in #[Attribute] more precise',
[
new CodeSample(
<<<'CODE_SAMPLE'
namespace App\Service;

use Symfony\Component\DependencyInjection\Attribute\Autowire;

class MessageGenerator
{
public function __construct(
#[Autowire('%kernel.debug%')]
bool $debugMode,

#[Autowire('%env(SOME_ENV_VAR)%')]
string $senderName,
) {
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
namespace App\Service;

use Symfony\Component\DependencyInjection\Attribute\Autowire;

class MessageGenerator
{
public function __construct(
#[Autowire(param: 'kernel.debug')]
bool $debugMode,

#[Autowire(env: 'SOME_ENV_VAR')]
string $senderName,
) {
}
}
CODE_SAMPLE
),

]
);
}

public function getNodeTypes(): array
{
return [Class_::class];
}

/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
$classMethod = $node->getMethod(MethodName::CONSTRUCT);
if (! $classMethod instanceof ClassMethod) {
return null;
}

if ($classMethod->getParams() === []) {
return null;
}

$hasChanged = false;

foreach ($classMethod->params as $param) {
foreach ($param->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attribute) {
if (! $this->isName($attribute->name, SymfonyAttribute::AUTOWIRE)) {
continue;
}

foreach ($attribute->args as $attributeArg) {
if ($this->isAlreadyEnvParamNamed($attributeArg)) {
continue;
}

// we can handle only string values
if (! $attributeArg->value instanceof String_) {
continue;
}

$envMatch = Strings::match($attributeArg->value->value, self::ENV_REGEX);
if (isset($envMatch['env'])) {
$attributeArg->name = new Identifier('env');
$attributeArg->value = new String_($envMatch['env']);

$hasChanged = true;
continue;
}

$paramMatch = Strings::match($attributeArg->value->value, self::PARAMETER_REGEX);
if (isset($paramMatch['param'])) {
$attributeArg->name = new Identifier('param');
$attributeArg->value = new String_($paramMatch['param']);

$hasChanged = true;
}
}
}
}
}

if ($hasChanged) {
return $node;
}

return null;
}

private function isAlreadyEnvParamNamed(Arg $arg): bool
{
if (! $arg->name instanceof Identifier) {
return false;
}

return in_array($arg->name->toString(), ['env', 'param'], true);
}
}
Loading
Loading