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
4 changes: 4 additions & 0 deletions config/sets/symfony/symfony-code-quality.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

declare(strict_types=1);

use Rector\Symfony\Symfony62\Rector\Class_\SecurityAttributeToIsGrantedAttributeRector;
use Rector\Config\RectorConfig;
use Rector\Symfony\CodeQuality\Rector\BinaryOp\RequestIsMainRector;
use Rector\Symfony\CodeQuality\Rector\BinaryOp\ResponseStatusCodeRector;
Expand Down Expand Up @@ -38,5 +39,8 @@

// routing
InlineClassRoutePrefixRector::class,

// narrow attributes
SecurityAttributeToIsGrantedAttributeRector::class,
]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ private function changeListenerToSubscriberWithMethods(Class_ $class, array $eve
*/
private function hasAsListenerAttribute(Class_ $class): bool
{
if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, SymfonyAttribute::EVENT_LISTENER_ATTRIBUTE)) {
if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, SymfonyAttribute::AS_EVENT_LISTENER)) {
return true;
}

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

if ($this->phpAttributeAnalyzer->hasPhpAttribute(
$classMethod,
SymfonyAttribute::EVENT_LISTENER_ATTRIBUTE
SymfonyAttribute::AS_EVENT_LISTENER
)) {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Rector\AbstractRector;
use Rector\Symfony\Enum\SensioAttribute;
use Rector\Symfony\Enum\SymfonyAttribute;
use Rector\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
Expand All @@ -28,15 +31,6 @@
*/
final class SecurityAttributeToIsGrantedAttributeRector extends AbstractRector implements MinPhpVersionInterface
{
/**
* @var string
*/
private const SECURITY_ATTRIBUTE = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Security';

/**
* @var string
*/
private const IS_GRANTED_ATTRIBUTE = 'Symfony\Component\Security\Http\Attribute\IsGranted';

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

public function __construct(
private readonly ReflectionProvider $reflectionProvider
) {
}

public function provideMinPhpVersion(): int
{
return PhpVersionFeature::ATTRIBUTES;
Expand Down Expand Up @@ -113,15 +112,22 @@ public function getNodeTypes(): array
*/
public function refactor(Node $node): ?Node
{
if (! $this->hasSymfonySecurityAttribute()) {
return null;
}

$hasChanged = false;

foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attribute) {
if (! $this->isName($attribute->name, self::SECURITY_ATTRIBUTE)) {
if (! $this->isName($attribute->name, SensioAttribute::SECURITY)) {
continue;
}

$attribute->name = new FullyQualified(self::IS_GRANTED_ATTRIBUTE);
// 1. resolve closest existing name of IsGranted
$isGrantedName = $this->resolveIsGrantedAttributeName();

$attribute->name = new FullyQualified($isGrantedName);

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

return new New_(new FullyQualified('Symfony\Component\ExpressionLanguage\Expression'), $args);
}

private function resolveIsGrantedAttributeName(): string
{
if ($this->reflectionProvider->hasClass(SymfonyAttribute::IS_GRANTED)) {
return SymfonyAttribute::IS_GRANTED;
}

// fallback to sensio, if available
return SensioAttribute::IS_GRANTED;
}

private function hasSymfonySecurityAttribute(): bool
{
// run only if the sensio attribute is available
if (! $this->reflectionProvider->hasClass(SensioAttribute::SECURITY)) {
return false;
}

// must be attribute, not just annotation
$securityClassReflection = $this->reflectionProvider->getClass(SensioAttribute::SECURITY);
return $securityClassReflection->isAttributeClass();
}
}
10 changes: 10 additions & 0 deletions src/Enum/SensioAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,14 @@ final class SensioAttribute
* @var string
*/
public const TEMPLATE = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Template';

/**
* @var string
*/
public const IS_GRANTED = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted';

/**
* @var string
*/
public const SECURITY = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Security';
}
7 changes: 6 additions & 1 deletion src/Enum/SymfonyAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ final class SymfonyAttribute
/**
* @var string
*/
public const EVENT_LISTENER_ATTRIBUTE = 'Symfony\Component\EventDispatcher\Attribute\AsEventListener';
public const AS_EVENT_LISTENER = 'Symfony\Component\EventDispatcher\Attribute\AsEventListener';

/**
* @var string
*/
public const ROUTE = 'Symfony\Component\Routing\Attribute\Route';

/**
* @var string
*/
public const IS_GRANTED = 'Symfony\Component\Security\Http\Attribute\IsGranted';
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;

#[\Attribute]
class Security
{
}
Loading