diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_everywhere.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_everywhere.php.inc new file mode 100644 index 00000000000..119633d5c1b --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_everywhere.php.inc @@ -0,0 +1,64 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_class_constant_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_class_constant_only.php.inc new file mode 100644 index 00000000000..a63bf943894 --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_class_constant_only.php.inc @@ -0,0 +1,70 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_class_method_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_class_method_only.php.inc new file mode 100644 index 00000000000..1b01880c409 --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_class_method_only.php.inc @@ -0,0 +1,76 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_class_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_class_only.php.inc new file mode 100644 index 00000000000..d431d07d7ec --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_class_only.php.inc @@ -0,0 +1,70 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_enum_case_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_enum_case_only.php.inc new file mode 100644 index 00000000000..d604531e6b2 --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_enum_case_only.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_enum_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_enum_only.php.inc new file mode 100644 index 00000000000..092d3f46a3b --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_enum_only.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_function_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_function_only.php.inc new file mode 100644 index 00000000000..2235de7d1be --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_function_only.php.inc @@ -0,0 +1,70 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_interface_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_interface_only.php.inc new file mode 100644 index 00000000000..99858f1c688 --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_interface_only.php.inc @@ -0,0 +1,50 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_parameter_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_parameter_only.php.inc new file mode 100644 index 00000000000..63a53d52eec --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_parameter_only.php.inc @@ -0,0 +1,68 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_property_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_property_only.php.inc new file mode 100644 index 00000000000..7913e4c3e16 --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_property_only.php.inc @@ -0,0 +1,69 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_trait_only.php.inc b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_trait_only.php.inc new file mode 100644 index 00000000000..c2301e6d6cf --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Fixture/remove_attribute_from_trait_only.php.inc @@ -0,0 +1,70 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/RemoveAttributeRectorTest.php b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/RemoveAttributeRectorTest.php new file mode 100644 index 00000000000..eae958666fb --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/RemoveAttributeRectorTest.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/Removing/Rector/Attribute/RemoveAttributeRector/Source/Attribute/RemoveEverywhereAttribute.php b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Source/Attribute/RemoveEverywhereAttribute.php new file mode 100644 index 00000000000..1ac01cd7c0e --- /dev/null +++ b/rules-tests/Removing/Rector/Attribute/RemoveAttributeRector/Source/Attribute/RemoveEverywhereAttribute.php @@ -0,0 +1,12 @@ +ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromClassAttribute', + [Class_::class] + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromTraitAttribute', + [Trait_::class] + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromInterfaceAttribute', + [Interface_::class] + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromEnumAttribute', + [Enum_::class] + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromEnumCaseAttribute', + [EnumCase::class] + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromPropertyAttribute', + [Property::class] + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromClassConstantAttribute', + [ClassConst::class], + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromClassMethodAttribute', + [ClassMethod::class], + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromFunctionAttribute', + [Function_::class], + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveFromParameterAttribute', + [Param::class], + ), + ]); + + $rectorConfig->ruleWithConfiguration(RemoveAttributeRector::class, [ + new RemoveAttribute( + 'Rector\Tests\Removing\Rector\Attribute\RemoveAttributeRector\Source\Attribute\RemoveEverywhereAttribute', + ), + ]); +}; diff --git a/rules/Removing/Rector/Attribute/RemoveAttributeRector.php b/rules/Removing/Rector/Attribute/RemoveAttributeRector.php new file mode 100644 index 00000000000..6f2859393b0 --- /dev/null +++ b/rules/Removing/Rector/Attribute/RemoveAttributeRector.php @@ -0,0 +1,138 @@ + + */ + private array $removeAttributes = []; + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Removes attributes (from specific node types)', [ + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +#[Foo] +class SomeClass +{ +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +#[Foo] +class SomeClass +{ +} +CODE_SAMPLE + , + [new RemoveAttribute('Foo')] + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ + ArrowFunction::class, + ClassConst::class, + ClassLike::class, + ClassMethod::class, + Closure::class, + Const_::class, + EnumCase::class, + Function_::class, + Param::class, + Property::class, + PropertyHook::class, + ]; + } + + /** + * @param ArrowFunction|ClassConst|ClassLike|ClassMethod|Closure|Const_|EnumCase|Function_|Param|Property|PropertyHook $node + */ + public function refactor(Node $node): ?Node + { + $relevantRemoveAttributes = []; + foreach ($this->removeAttributes as $removeAttribute) { + if ($removeAttribute->getNodeTypes() === [] || in_array( + $node::class, + $removeAttribute->getNodeTypes(), + true + )) { + $relevantRemoveAttributes[] = $removeAttribute; + } + } + + if ($relevantRemoveAttributes === []) { + return null; + } + + $hasChanged = false; + + /** @var array $attrGroups */ + $attrGroups = $node->attrGroups; + + foreach ($attrGroups as $attrGroupKey => $attrGroup) { + foreach ($attrGroup->attrs as $key => $attribute) { + foreach ($relevantRemoveAttributes as $removeAttribute) { + if (! $this->isName($attribute, $removeAttribute->getClass())) { + continue; + } + + unset($attrGroup->attrs[$key]); + + $hasChanged = true; + } + } + + if ($attrGroup->attrs === []) { + unset($node->attrGroups[$attrGroupKey]); + + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function configure(array $configuration): void + { + Assert::allIsInstanceOf($configuration, RemoveAttribute::class); + + $this->removeAttributes = $configuration; + } +} diff --git a/rules/Removing/ValueObject/RemoveAttribute.php b/rules/Removing/ValueObject/RemoveAttribute.php new file mode 100644 index 00000000000..8c727908fd5 --- /dev/null +++ b/rules/Removing/ValueObject/RemoveAttribute.php @@ -0,0 +1,37 @@ +> $nodeTypes + */ + public function __construct( + private string $class, + private array $nodeTypes = [], + ) { + RectorAssert::className($class); + foreach ($nodeTypes as $nodeType) { + RectorAssert::className($nodeType); + } + } + + public function getClass(): string + { + return $this->class; + } + + /** + * @return list> + */ + public function getNodeTypes(): array + { + return $this->nodeTypes; + } +}