diff --git a/config/sets/symfony/symfony-code-quality.php b/config/sets/symfony/symfony-code-quality.php index 9f0171f03..1255e98f0 100644 --- a/config/sets/symfony/symfony-code-quality.php +++ b/config/sets/symfony/symfony-code-quality.php @@ -9,6 +9,7 @@ use Rector\Symfony\CodeQuality\Rector\Class_\EventListenerToEventSubscriberRector; use Rector\Symfony\CodeQuality\Rector\Class_\InlineClassRoutePrefixRector; use Rector\Symfony\CodeQuality\Rector\Class_\LoadValidatorMetadataToAnnotationRector; +use Rector\Symfony\CodeQuality\Rector\Class_\SplitAndSecurityAttributeToIsGrantedRector; use Rector\Symfony\CodeQuality\Rector\ClassMethod\ActionSuffixRemoverRector; use Rector\Symfony\CodeQuality\Rector\ClassMethod\ParamTypeFromRouteRequiredRegexRector; use Rector\Symfony\CodeQuality\Rector\ClassMethod\RemoveUnusedRequestParamRector; @@ -16,7 +17,6 @@ use Rector\Symfony\CodeQuality\Rector\MethodCall\AssertSameResponseCodeWithDebugContentsRector; use Rector\Symfony\CodeQuality\Rector\MethodCall\LiteralGetToRequestClassConstantRector; use Rector\Symfony\Symfony26\Rector\MethodCall\RedirectToRouteRector; -use Rector\Symfony\Symfony62\Rector\Class_\SecurityAttributeToIsGrantedAttributeRector; return static function (RectorConfig $rectorConfig): void { $rectorConfig->rules([ @@ -43,5 +43,6 @@ // narrow attributes SingleConditionSecurityAttributeToIsGrantedRector::class, + SplitAndSecurityAttributeToIsGrantedRector::class, ]); }; diff --git a/rules-tests/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector/Fixture/skip_single_or.php.inc b/rules-tests/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector/Fixture/skip_single_or.php.inc new file mode 100644 index 000000000..a51b83fba --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector/Fixture/skip_single_or.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector/SplitAndSecurityAttributeToIsGrantedRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector/SplitAndSecurityAttributeToIsGrantedRectorTest.php new file mode 100644 index 000000000..244e8f5c2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector/SplitAndSecurityAttributeToIsGrantedRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector/config/configured_rule.php new file mode 100644 index 000000000..99695fc32 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(SplitAndSecurityAttributeToIsGrantedRector::class); +}; diff --git a/rules/CodeQuality/Rector/AttributeGroup/SingleConditionSecurityAttributeToIsGrantedRector.php b/rules/CodeQuality/Rector/AttributeGroup/SingleConditionSecurityAttributeToIsGrantedRector.php index 5e3f56532..e956e95e3 100644 --- a/rules/CodeQuality/Rector/AttributeGroup/SingleConditionSecurityAttributeToIsGrantedRector.php +++ b/rules/CodeQuality/Rector/AttributeGroup/SingleConditionSecurityAttributeToIsGrantedRector.php @@ -19,6 +19,8 @@ /** * @see https://github.com/symfony/symfony/pull/27305/ * @see https://stackoverflow.com/a/65439590/1348344 + * + * @see \Rector\Symfony\Tests\CodeQuality\Rector\AttributeGroup\SingleConditionSecurityAttributeToIsGrantedRector\SingleConditionSecurityAttributeToIsGrantedRectorTest */ final class SingleConditionSecurityAttributeToIsGrantedRector extends AbstractRector { diff --git a/rules/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector.php b/rules/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector.php new file mode 100644 index 000000000..34695110a --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/SplitAndSecurityAttributeToIsGrantedRector.php @@ -0,0 +1,125 @@ +attrGroups as $key => $attrGroup) { + foreach ($attrGroup->attrs as $attr) { + if (! $this->isName($attr->name, Security::class)) { + continue; + } + + $firstArgValue = $attr->args[0]->value; + if (! $firstArgValue instanceof String_) { + continue; + } + + $content = $firstArgValue->value; + + // unable to resolve with pure attributes + if (str_contains($content, ' or ')) { + continue; + } + + // we look for "and"s + if (! str_contains($content, ' and')) { + continue; + } + + $andItems = explode(' and ', $content); + + $accessRights = []; + + foreach ($andItems as $andItem) { + $matches = Strings::match($andItem, '#^(is_granted|has_role)\(\'(?[A-Za-z_]+)\'\)$#'); + if (! isset($matches['access_right'])) { + // all or nothing + return null; + } + + $accessRights[] = $matches['access_right']; + } + + unset($node->attrGroups[$key]); + + $hasChanged = true; + + foreach ($accessRights as $accessRight) { + $attributeGroup = new AttributeGroup([ + new Attribute(new FullyQualified(IsGranted::class), [ + new Arg(new String_($accessRight)), + ]), + ]); + + $node->attrGroups[] = $attributeGroup; + } + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } +}