Skip to content

Commit 19521bd

Browse files
authored
[code-quality] Add attribute-existance check to SecurityAttributeToIsGrantedAttributeRector, add to code-quality set (#713)
1 parent d446023 commit 19521bd

6 files changed

Lines changed: 62 additions & 14 deletions

File tree

config/sets/symfony/symfony-code-quality.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
declare(strict_types=1);
44

5+
use Rector\Symfony\Symfony62\Rector\Class_\SecurityAttributeToIsGrantedAttributeRector;
56
use Rector\Config\RectorConfig;
67
use Rector\Symfony\CodeQuality\Rector\BinaryOp\RequestIsMainRector;
78
use Rector\Symfony\CodeQuality\Rector\BinaryOp\ResponseStatusCodeRector;
@@ -38,5 +39,8 @@
3839

3940
// routing
4041
InlineClassRoutePrefixRector::class,
42+
43+
// narrow attributes
44+
SecurityAttributeToIsGrantedAttributeRector::class,
4145
]);
4246
};

rules/CodeQuality/Rector/Class_/EventListenerToEventSubscriberRector.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ private function changeListenerToSubscriberWithMethods(Class_ $class, array $eve
170170
*/
171171
private function hasAsListenerAttribute(Class_ $class): bool
172172
{
173-
if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, SymfonyAttribute::EVENT_LISTENER_ATTRIBUTE)) {
173+
if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, SymfonyAttribute::AS_EVENT_LISTENER)) {
174174
return true;
175175
}
176176

@@ -181,7 +181,7 @@ private function hasAsListenerAttribute(Class_ $class): bool
181181

182182
if ($this->phpAttributeAnalyzer->hasPhpAttribute(
183183
$classMethod,
184-
SymfonyAttribute::EVENT_LISTENER_ATTRIBUTE
184+
SymfonyAttribute::AS_EVENT_LISTENER
185185
)) {
186186
return true;
187187
}

rules/Symfony62/Rector/Class_/SecurityAttributeToIsGrantedAttributeRector.php

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
use PhpParser\Node\Scalar\String_;
1515
use PhpParser\Node\Stmt\Class_;
1616
use PhpParser\Node\Stmt\ClassMethod;
17+
use PHPStan\Reflection\ReflectionProvider;
1718
use Rector\Rector\AbstractRector;
19+
use Rector\Symfony\Enum\SensioAttribute;
20+
use Rector\Symfony\Enum\SymfonyAttribute;
1821
use Rector\ValueObject\PhpVersionFeature;
1922
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
2023
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@@ -28,15 +31,6 @@
2831
*/
2932
final class SecurityAttributeToIsGrantedAttributeRector extends AbstractRector implements MinPhpVersionInterface
3033
{
31-
/**
32-
* @var string
33-
*/
34-
private const SECURITY_ATTRIBUTE = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Security';
35-
36-
/**
37-
* @var string
38-
*/
39-
private const IS_GRANTED_ATTRIBUTE = 'Symfony\Component\Security\Http\Attribute\IsGranted';
4034

4135
/**
4236
* @var string
@@ -50,6 +44,11 @@ final class SecurityAttributeToIsGrantedAttributeRector extends AbstractRector i
5044
*/
5145
private const IS_GRANTED_AND_SUBJECT_REGEX = '#^is_granted\((\"|\')(?<role>[\w]+)(\"|\'),\s+(?<subject>\w+)\)$#';
5246

47+
public function __construct(
48+
private readonly ReflectionProvider $reflectionProvider
49+
) {
50+
}
51+
5352
public function provideMinPhpVersion(): int
5453
{
5554
return PhpVersionFeature::ATTRIBUTES;
@@ -113,15 +112,22 @@ public function getNodeTypes(): array
113112
*/
114113
public function refactor(Node $node): ?Node
115114
{
115+
if (! $this->hasSymfonySecurityAttribute()) {
116+
return null;
117+
}
118+
116119
$hasChanged = false;
117120

118121
foreach ($node->attrGroups as $attrGroup) {
119122
foreach ($attrGroup->attrs as $attribute) {
120-
if (! $this->isName($attribute->name, self::SECURITY_ATTRIBUTE)) {
123+
if (! $this->isName($attribute->name, SensioAttribute::SECURITY)) {
121124
continue;
122125
}
123126

124-
$attribute->name = new FullyQualified(self::IS_GRANTED_ATTRIBUTE);
127+
// 1. resolve closest existing name of IsGranted
128+
$isGrantedName = $this->resolveIsGrantedAttributeName();
129+
130+
$attribute->name = new FullyQualified($isGrantedName);
125131

126132
$firstArg = $attribute->args[0];
127133
$firstArg->name = new Identifier('attribute');
@@ -179,4 +185,26 @@ private function wrapToNewExpression(Expr $expr): New_|String_
179185

180186
return new New_(new FullyQualified('Symfony\Component\ExpressionLanguage\Expression'), $args);
181187
}
188+
189+
private function resolveIsGrantedAttributeName(): string
190+
{
191+
if ($this->reflectionProvider->hasClass(SymfonyAttribute::IS_GRANTED)) {
192+
return SymfonyAttribute::IS_GRANTED;
193+
}
194+
195+
// fallback to sensio, if available
196+
return SensioAttribute::IS_GRANTED;
197+
}
198+
199+
private function hasSymfonySecurityAttribute(): bool
200+
{
201+
// run only if the sensio attribute is available
202+
if (! $this->reflectionProvider->hasClass(SensioAttribute::SECURITY)) {
203+
return false;
204+
}
205+
206+
// must be attribute, not just annotation
207+
$securityClassReflection = $this->reflectionProvider->getClass(SensioAttribute::SECURITY);
208+
return $securityClassReflection->isAttributeClass();
209+
}
182210
}

src/Enum/SensioAttribute.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,14 @@ final class SensioAttribute
2525
* @var string
2626
*/
2727
public const TEMPLATE = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Template';
28+
29+
/**
30+
* @var string
31+
*/
32+
public const IS_GRANTED = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted';
33+
34+
/**
35+
* @var string
36+
*/
37+
public const SECURITY = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Security';
2838
}

src/Enum/SymfonyAttribute.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,15 @@ final class SymfonyAttribute
2929
/**
3030
* @var string
3131
*/
32-
public const EVENT_LISTENER_ATTRIBUTE = 'Symfony\Component\EventDispatcher\Attribute\AsEventListener';
32+
public const AS_EVENT_LISTENER = 'Symfony\Component\EventDispatcher\Attribute\AsEventListener';
3333

3434
/**
3535
* @var string
3636
*/
3737
public const ROUTE = 'Symfony\Component\Routing\Attribute\Route';
38+
39+
/**
40+
* @var string
41+
*/
42+
public const IS_GRANTED = 'Symfony\Component\Security\Http\Attribute\IsGranted';
3843
}

stubs/Symfony/Bundle/FrameworkExtraBundle/Configuration/Security.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
44

5+
#[\Attribute]
56
class Security
67
{
78
}

0 commit comments

Comments
 (0)