From 1b8c349044e503501c74e5dd58107669f3ae8765 Mon Sep 17 00:00:00 2001 From: Andy Postnikov Date: Sun, 29 Oct 2023 03:09:58 +0100 Subject: [PATCH 01/27] WIP: Rule to convert action to attributes --- config/drupal-10/drupal-10.2-deprecations.php | 4 + .../ActionAnnotationToAttributeRector.php | 171 ++++++++++++++++++ src/Set/Drupal10SetList.php | 2 +- 3 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 src/Rector/Deprecation/ActionAnnotationToAttributeRector.php diff --git a/config/drupal-10/drupal-10.2-deprecations.php b/config/drupal-10/drupal-10.2-deprecations.php index 869a4838..4fb03f20 100644 --- a/config/drupal-10/drupal-10.2-deprecations.php +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use DrupalRector\Rector\Deprecation\ActionAnnotationToAttributeRector; use DrupalRector\Rector\Deprecation\FunctionToStaticRector; use DrupalRector\Rector\Deprecation\MethodToMethodWithCheckRector; use DrupalRector\Rector\ValueObject\FunctionToStaticConfiguration; @@ -24,4 +25,7 @@ new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'getResource', 'getImage'), new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'setResource', 'setImage'), ]); + + // @see https://www.drupal.org/node/3395575 + $rectorConfig->rule(\DrupalRector\Rector\Deprecation\ActionAnnotationToAttributeRector::class); }; diff --git a/src/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Rector/Deprecation/ActionAnnotationToAttributeRector.php new file mode 100644 index 00000000..d4f975c6 --- /dev/null +++ b/src/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -0,0 +1,171 @@ +phpDocTagRemover = $phpDocTagRemover; + $this->docBlockUpdater = $docBlockUpdater; + $this->phpDocInfoFactory = $phpDocInfoFactory; + $this->arrayParser = $arrayParser; + $this->tokenIteratorFactory = $tokenIteratorFactory; + } + public function getRuleDefinition() : RuleDefinition + { + return new RuleDefinition('Change annotations with value to attribute', [new CodeSample(<<<'CODE_SAMPLE' + +namespace Drupal\Core\Action\Plugin\Action; + +use Drupal\Core\Session\AccountInterface; + +/** + * Publishes an entity. + * + * @Action( + * id = "entity:publish_action", + * action_label = @Translation("Publish"), + * deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver", + * ) + */ +class PublishAction extends EntityActionBase { +CODE_SAMPLE +, <<<'CODE_SAMPLE' + +namespace Drupal\Core\Action\Plugin\Action; + +use Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver; +use Drupal\Core\Action\Attribute\Action; +use Drupal\Core\Session\AccountInterface; +use Drupal\Core\StringTranslation\TranslatableMarkup; + +/** + * Publishes an entity. + */ +#[Action( + id: 'entity:publish_action', + action_label: new TranslatableMarkup('Publish'), + deriver: EntityPublishedActionDeriver::class +)] +class PublishAction extends EntityActionBase { +CODE_SAMPLE +)]); + } + /** + * @return array> + */ + public function getNodeTypes() : array + { + return [Class_::class]; + } + public function provideMinPhpVersion() : int + { + return PhpVersion::PHP_81; + } + /** + * @param Class_|ClassMethod $node + */ + public function refactor(Node $node) : ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (!$phpDocInfo instanceof PhpDocInfo) { + return null; + } + /** @var PhpDocTagNode[] $tagsByName */ + $tagsByName = $phpDocInfo->getTagsByName('Action'); + if ($tagsByName === []) { + return null; + } + $hasChanged = \false; + foreach ($tagsByName as $valueNode) { + if (!$valueNode->value instanceof GenericTagValueNode) { + continue; + } + $stringValue = $valueNode->value->value; + $stringValue = '{' . trim($stringValue, '()') . '}'; + $tokenIterator = $this->tokenIteratorFactory->create($stringValue); + $data = $this->arrayParser->parseCurlyArray($tokenIterator, $node); + $attribute = $this->createAttribute($data); + $node->attrGroups[] = new AttributeGroup([$attribute]); + // cleanup + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $valueNode); + $hasChanged = \true; + } + if ($hasChanged) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + return $node; + } + return null; + } + private function createAttribute(array $parsedArgs) : Attribute + { + $fullyQualified = new FullyQualified(Action::class); + $args = []; + foreach ($parsedArgs as $value) { + if ($value->key === 'label') { + $arg = new Node\Expr\New_(new Node\Name(TranslatableMarkup::class), [new Arg(new String_($value->value->values[0]->value->value))]); + } + else { + $arg = new String_($value->value->value); + } + $args[] = new Arg($arg, \false, \false, [], new Node\Identifier($value->key)); + } + return new Attribute($fullyQualified, $args); + } +} diff --git a/src/Set/Drupal10SetList.php b/src/Set/Drupal10SetList.php index 768e2eba..9c9b1856 100644 --- a/src/Set/Drupal10SetList.php +++ b/src/Set/Drupal10SetList.php @@ -11,5 +11,5 @@ final class Drupal10SetList implements SetListInterface public const DRUPAL_10 = __DIR__.'/../../config/drupal-10/drupal-10-all-deprecations.php'; public const DRUPAL_100 = __DIR__.'/../../config/drupal-10/drupal-10.0-deprecations.php'; public const DRUPAL_101 = __DIR__.'/../../config/drupal-10/drupal-10.1-deprecations.php'; - public const DRUPAL_102 = __DIR__.'/../../config/drupal-10/drupal-10.2-deprecations.php'; + public const DRUPAL_102 = __DIR__ . '/../../config/drupal-10/drupal-10.2-deprecations.php'; } From 4ec11777430237504a4f54e414bc9569dcf5aac5 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 09:36:45 +0100 Subject: [PATCH 02/27] Rebase on main and move rector to Drupal10 namespace --- .../Rector/Deprecation/ActionAnnotationToAttributeRector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{ => Drupal10}/Rector/Deprecation/ActionAnnotationToAttributeRector.php (99%) diff --git a/src/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php similarity index 99% rename from src/Rector/Deprecation/ActionAnnotationToAttributeRector.php rename to src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index d4f975c6..42342fc5 100644 --- a/src/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -1,6 +1,6 @@ Date: Sun, 10 Dec 2023 09:37:14 +0100 Subject: [PATCH 03/27] Add basic unit test for ActionAnnotationToAttributeRector. --- .../ActionAnnotationToAttributeRectorTest.php | 35 +++++++++++++++++++ .../config/configured_rule.php | 12 +++++++ .../fixture/basic_fixture.php.inc | 27 ++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/ActionAnnotationToAttributeRectorTest.php create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/ActionAnnotationToAttributeRectorTest.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/ActionAnnotationToAttributeRectorTest.php new file mode 100644 index 00000000..f29f94cf --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/ActionAnnotationToAttributeRectorTest.php @@ -0,0 +1,35 @@ +doTestFile($filePath); + } + + /** + * @return Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + // must be implemented + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php new file mode 100644 index 00000000..f053fd2f --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php @@ -0,0 +1,12 @@ + + +----- + From 407fb522d93a524c1a62114f4d8f32fcf06668a5 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 09:42:37 +0100 Subject: [PATCH 04/27] Fix codestyle --- config/drupal-10/drupal-10.2-deprecations.php | 2 +- .../ActionAnnotationToAttributeRector.php | 45 ++++++++++++------- src/Set/Drupal10SetList.php | 2 +- .../config/configured_rule.php | 1 - 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/config/drupal-10/drupal-10.2-deprecations.php b/config/drupal-10/drupal-10.2-deprecations.php index 4fb03f20..c27e3f6c 100644 --- a/config/drupal-10/drupal-10.2-deprecations.php +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -27,5 +27,5 @@ ]); // @see https://www.drupal.org/node/3395575 - $rectorConfig->rule(\DrupalRector\Rector\Deprecation\ActionAnnotationToAttributeRector::class); + $rectorConfig->rule(ActionAnnotationToAttributeRector::class); }; diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index 42342fc5..2ba83478 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -1,4 +1,6 @@ -phpDocTagRemover = $phpDocTagRemover; @@ -67,7 +75,8 @@ public function __construct(PhpDocTagRemover $phpDocTagRemover, DocBlockUpdater $this->arrayParser = $arrayParser; $this->tokenIteratorFactory = $tokenIteratorFactory; } - public function getRuleDefinition() : RuleDefinition + + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Change annotations with value to attribute', [new CodeSample(<<<'CODE_SAMPLE' @@ -86,7 +95,7 @@ public function getRuleDefinition() : RuleDefinition */ class PublishAction extends EntityActionBase { CODE_SAMPLE -, <<<'CODE_SAMPLE' + , <<<'CODE_SAMPLE' namespace Drupal\Core\Action\Plugin\Action; @@ -105,23 +114,26 @@ class PublishAction extends EntityActionBase { )] class PublishAction extends EntityActionBase { CODE_SAMPLE -)]); + )]); } + /** * @return array> */ - public function getNodeTypes() : array + public function getNodeTypes(): array { return [Class_::class]; } - public function provideMinPhpVersion() : int + + public function provideMinPhpVersion(): int { return PhpVersion::PHP_81; } + /** * @param Class_|ClassMethod $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?Node { $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); if (!$phpDocInfo instanceof PhpDocInfo) { @@ -138,7 +150,7 @@ public function refactor(Node $node) : ?Node continue; } $stringValue = $valueNode->value->value; - $stringValue = '{' . trim($stringValue, '()') . '}'; + $stringValue = '{'.trim($stringValue, '()').'}'; $tokenIterator = $this->tokenIteratorFactory->create($stringValue); $data = $this->arrayParser->parseCurlyArray($tokenIterator, $node); $attribute = $this->createAttribute($data); @@ -149,23 +161,26 @@ public function refactor(Node $node) : ?Node } if ($hasChanged) { $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + return $node; } + return null; } - private function createAttribute(array $parsedArgs) : Attribute + + private function createAttribute(array $parsedArgs): Attribute { $fullyQualified = new FullyQualified(Action::class); $args = []; foreach ($parsedArgs as $value) { if ($value->key === 'label') { $arg = new Node\Expr\New_(new Node\Name(TranslatableMarkup::class), [new Arg(new String_($value->value->values[0]->value->value))]); - } - else { + } else { $arg = new String_($value->value->value); } $args[] = new Arg($arg, \false, \false, [], new Node\Identifier($value->key)); } + return new Attribute($fullyQualified, $args); } } diff --git a/src/Set/Drupal10SetList.php b/src/Set/Drupal10SetList.php index 9c9b1856..768e2eba 100644 --- a/src/Set/Drupal10SetList.php +++ b/src/Set/Drupal10SetList.php @@ -11,5 +11,5 @@ final class Drupal10SetList implements SetListInterface public const DRUPAL_10 = __DIR__.'/../../config/drupal-10/drupal-10-all-deprecations.php'; public const DRUPAL_100 = __DIR__.'/../../config/drupal-10/drupal-10.0-deprecations.php'; public const DRUPAL_101 = __DIR__.'/../../config/drupal-10/drupal-10.1-deprecations.php'; - public const DRUPAL_102 = __DIR__ . '/../../config/drupal-10/drupal-10.2-deprecations.php'; + public const DRUPAL_102 = __DIR__.'/../../config/drupal-10/drupal-10.2-deprecations.php'; } diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php index f053fd2f..f2d6a510 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php @@ -3,7 +3,6 @@ declare(strict_types=1); use DrupalRector\Drupal10\Rector\Deprecation\ActionAnnotationToAttributeRector; -use DrupalRector\Rector\ValueObject\DrupalIntroducedVersionConfiguration; use DrupalRector\Tests\Rector\Deprecation\DeprecationBase; use Rector\Config\RectorConfig; From d936f393825287e841b48a5939d648785d3441db Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 09:47:16 +0100 Subject: [PATCH 05/27] Fix namespace in config for ActionAnnotationToAttributeRector --- config/drupal-10/drupal-10.2-deprecations.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/drupal-10/drupal-10.2-deprecations.php b/config/drupal-10/drupal-10.2-deprecations.php index c27e3f6c..7dd9ae8f 100644 --- a/config/drupal-10/drupal-10.2-deprecations.php +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -2,7 +2,7 @@ declare(strict_types=1); -use DrupalRector\Rector\Deprecation\ActionAnnotationToAttributeRector; +use DrupalRector\Drupal10\Rector\Deprecation\ActionAnnotationToAttributeRector; use DrupalRector\Rector\Deprecation\FunctionToStaticRector; use DrupalRector\Rector\Deprecation\MethodToMethodWithCheckRector; use DrupalRector\Rector\ValueObject\FunctionToStaticConfiguration; From bbe8c0397853b725d10bb3d6bce49caacdf3aced Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 09:57:47 +0100 Subject: [PATCH 06/27] The label should be "action_label", --- .../Deprecation/ActionAnnotationToAttributeRector.php | 9 ++++----- .../fixture/basic_fixture.php.inc | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index 2ba83478..59318144 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -4,8 +4,6 @@ namespace DrupalRector\Drupal10\Rector\Deprecation; -use Drupal\Core\Action\Attribute\Action; -use Drupal\Core\StringTranslation\TranslatableMarkup; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Attribute; @@ -16,6 +14,7 @@ use PhpParser\Node\Stmt\ClassMethod; use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; +use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocInfo\TokenIteratorFactory; @@ -170,11 +169,11 @@ public function refactor(Node $node): ?Node private function createAttribute(array $parsedArgs): Attribute { - $fullyQualified = new FullyQualified(Action::class); + $fullyQualified = new FullyQualified('Drupal\Core\Action\Attribute\Action'); $args = []; foreach ($parsedArgs as $value) { - if ($value->key === 'label') { - $arg = new Node\Expr\New_(new Node\Name(TranslatableMarkup::class), [new Arg(new String_($value->value->values[0]->value->value))]); + if ($value->key === 'action_label') { + $arg = new Node\Expr\New_(new Node\Name('Drupal\Core\StringTranslation\TranslatableMarkup'), [new Arg(new String_($value->value->values[0]->value->value))]); } else { $arg = new String_($value->value->value); } diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc index 659e5203..4b4d78d9 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc @@ -5,7 +5,7 @@ * * @Action( * id = "action_example_basic_action", - * label = @Translation("Action Example: A basic example action that does nothing"), + * action_label = @Translation("Action Example: A basic example action that does nothing"), * type = "system" * ) */ @@ -20,7 +20,7 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface /** * A basic example action that does nothing. */ -#[\Drupal\Core\Action\Attribute\Action(id: 'action_example_basic_action', label: new Drupal\Core\StringTranslation\TranslatableMarkup('Action Example: A basic example action that does nothing'), type: 'system')] +#[\Drupal\Core\Action\Attribute\Action(id: 'action_example_basic_action', action_label: new Drupal\Core\StringTranslation\TranslatableMarkup('Action Example: A basic example action that does nothing'), type: 'system')] class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { } From b383c0497577c45222edd7af793ed667ca61b698 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 09:57:54 +0100 Subject: [PATCH 07/27] Fix phpstan --- .../Rector/Deprecation/ActionAnnotationToAttributeRector.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index 59318144..bd769964 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -167,6 +167,11 @@ public function refactor(Node $node): ?Node return null; } + /** + * @param array|ArrayItemNode[] $parsedArgs + * + * @return \PhpParser\Node\Attribute + */ private function createAttribute(array $parsedArgs): Attribute { $fullyQualified = new FullyQualified('Drupal\Core\Action\Attribute\Action'); From c0fbf2e7c81de5c304c0c1ea8c5febe1a6549d1e Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 10:39:40 +0100 Subject: [PATCH 08/27] Add test for other possible translation attribute arguments --- .../multiple_translation_arguments.php.inc | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc new file mode 100644 index 00000000..bcc8d7ba --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc @@ -0,0 +1,27 @@ + + +----- + 'Argument'], ['context' => 'Validation']), type: 'system')] +class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { + +} +?> From 47c1bccfd3b649ff1adbbcbd5833f0579704a78c Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 10:39:58 +0100 Subject: [PATCH 09/27] Add test for situation where the attribute already exists --- .../existing_attribute_fixture.php.inc | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc new file mode 100644 index 00000000..8b197dfe --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc @@ -0,0 +1,28 @@ + + +----- + From 0947c657f6a7c067c3c5826f85c0a574046693d5 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 21:10:57 +0100 Subject: [PATCH 10/27] If action attribute is already present, do not add it again. --- .../ActionAnnotationToAttributeRector.php | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index bd769964..ac86333b 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -143,17 +143,32 @@ public function refactor(Node $node): ?Node if ($tagsByName === []) { return null; } + + $hasAttribute = false; + foreach ($node->attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attr) { + if ($attr->name->toString() === 'Drupal\Core\Action\Attribute\Action') { + $hasAttribute = true; + break 2; + } + } + } + $hasChanged = \false; foreach ($tagsByName as $valueNode) { if (!$valueNode->value instanceof GenericTagValueNode) { continue; } - $stringValue = $valueNode->value->value; - $stringValue = '{'.trim($stringValue, '()').'}'; - $tokenIterator = $this->tokenIteratorFactory->create($stringValue); - $data = $this->arrayParser->parseCurlyArray($tokenIterator, $node); - $attribute = $this->createAttribute($data); - $node->attrGroups[] = new AttributeGroup([$attribute]); + + if ($hasAttribute === false) { + $stringValue = $valueNode->value->value; + $stringValue = '{'.trim($stringValue, '()').'}'; + $tokenIterator = $this->tokenIteratorFactory->create($stringValue); + $data = $this->arrayParser->parseCurlyArray($tokenIterator, $node); + $attribute = $this->createAttribute($data); + $node->attrGroups[] = new AttributeGroup([$attribute]); + } + // cleanup $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $valueNode); $hasChanged = \true; From 75a061e28d49f4050fb0e553c9e0557f2e0345d7 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 22:00:48 +0100 Subject: [PATCH 11/27] ActionAnnotationToAttributeRector now also parses extra @Translate arguments. --- .../ActionAnnotationToAttributeRector.php | 46 ++++++++++++++++++- .../multiple_translation_arguments.php.inc | 2 +- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index ac86333b..201fad79 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -15,6 +15,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode; +use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocInfo\TokenIteratorFactory; @@ -169,7 +170,7 @@ public function refactor(Node $node): ?Node $node->attrGroups[] = new AttributeGroup([$attribute]); } - // cleanup + // @todo This cleanup needs some extra logic for BC $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $valueNode); $hasChanged = \true; } @@ -193,7 +194,7 @@ private function createAttribute(array $parsedArgs): Attribute $args = []; foreach ($parsedArgs as $value) { if ($value->key === 'action_label') { - $arg = new Node\Expr\New_(new Node\Name('Drupal\Core\StringTranslation\TranslatableMarkup'), [new Arg(new String_($value->value->values[0]->value->value))]); + $arg = $this->convertTranslateAnnotation($value); } else { $arg = new String_($value->value->value); } @@ -202,4 +203,45 @@ private function createAttribute(array $parsedArgs): Attribute return new Attribute($fullyQualified, $args); } + + public function convertTranslateAnnotation(ArrayItemNode $value): ?Node\Expr\New_ { + // Check the annotation type, this will be helpful later. + if (!$value->value instanceof DoctrineAnnotationTagValueNode || $value->value->identifierTypeNode->name === 'Translation') { + return null; + } + + $valueArg = null; + $argumentArg = null; + $contextArg = null; + + // Loop through the values of the annotation, just to make 100% sure we have the correct argument order + foreach ($value->value->values as $translateValue) { + if ($translateValue->key === null) { + $valueArg = $this->nodeFactory->createArg($translateValue->value->value); + } + if ($translateValue->key === 'context') { + $contextArg = $this->nodeFactory->createArg(['context' => $translateValue->value->value]); + } + if ($translateValue->key === 'arguments') { + $argumentArg = []; + foreach ($translateValue->value->values as $argumentValue) { + $argumentArg[$argumentValue->key->value] = $argumentValue->value->value; + } + $argumentArg = $this->nodeFactory->createArg($argumentArg); + } + } + + $argArray = []; + if ($valueArg !== null) { + $argArray[] = $valueArg; + } + if ($argumentArg !== null) { + $argArray[] = $argumentArg; + } + if ($contextArg !== null) { + $argArray[] = $contextArg; + } + + return new Node\Expr\New_(new Node\Name('Drupal\Core\StringTranslation\TranslatableMarkup'), $argArray); + } } diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc index bcc8d7ba..8b1ef848 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc @@ -20,7 +20,7 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface /** * A basic example action that does nothing. */ -#[\Drupal\Core\Action\Attribute\Action(id: 'action_example_basic_action', action_label: new Drupal\Core\StringTranslation\TranslatableMarkup('Action Example: A basic example action that does nothing', ['@argument' => 'Argument'], ['context' => 'Validation']), type: 'system')] +#[\Drupal\Core\Action\Attribute\Action(id: 'action_example_basic_action', action_label: new Drupal\Core\StringTranslation\TranslatableMarkup('Action Example: A basic example action that does nothing and has an @argument', ['@argument' => 'Argument'], ['context' => 'Validation']), type: 'system')] class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { } From 283d96b7ffc9d54494a6ef65aec4d8083a95c440 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 10 Dec 2023 22:03:27 +0100 Subject: [PATCH 12/27] Fix code-style --- .../Rector/Deprecation/ActionAnnotationToAttributeRector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index 201fad79..32f7e8f7 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -204,7 +204,8 @@ private function createAttribute(array $parsedArgs): Attribute return new Attribute($fullyQualified, $args); } - public function convertTranslateAnnotation(ArrayItemNode $value): ?Node\Expr\New_ { + public function convertTranslateAnnotation(ArrayItemNode $value): ?Node\Expr\New_ + { // Check the annotation type, this will be helpful later. if (!$value->value instanceof DoctrineAnnotationTagValueNode || $value->value->identifierTypeNode->name === 'Translation') { return null; From 98faa8d9200222b0494552928419fe82ceb2049a Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 11 Dec 2023 16:03:06 +0100 Subject: [PATCH 13/27] Refactor some methods in AbstractDrupalCoreRector --- src/Rector/AbstractDrupalCoreRector.php | 42 ++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/Rector/AbstractDrupalCoreRector.php b/src/Rector/AbstractDrupalCoreRector.php index 55bf1777..3bf5272f 100644 --- a/src/Rector/AbstractDrupalCoreRector.php +++ b/src/Rector/AbstractDrupalCoreRector.php @@ -58,10 +58,8 @@ protected function isInBackwardsCompatibleCall(Node $node): bool public function refactor(Node $node) { - $drupalVersion = str_replace(['.x-dev', '-dev'], '.0', \Drupal::VERSION); - foreach ($this->configuration as $configuration) { - if (version_compare($drupalVersion, $configuration->getIntroducedVersion(), '<')) { + if ($this->rectorShouldApplyToDrupalVersion($configuration)) { continue; } @@ -82,7 +80,7 @@ public function refactor(Node $node) // The reason for this is that will start supplying patches for // Drupal 10 when 10.0 is already out of support. This means that // we will not support running drupal-rector on Drupal 10.0.x. - if (version_compare($drupalVersion, '10.1.0', '<') || version_compare($configuration->getIntroducedVersion(), '10.0.0', '<')) { + if ($this->supportBackwardsCompatibility($configuration) === false) { return $result; } @@ -115,4 +113,40 @@ private function createBcCallOnCallLike(Node\Expr\CallLike $node, Node\Expr\Call new ArrowFunction(['expr' => $clonedNode]), ]); } + + /** + * @param \DrupalRector\Contract\VersionedConfigurationInterface $configuration + * + * @return bool|int + */ + public function rectorShouldApplyToDrupalVersion(VersionedConfigurationInterface $configuration) { + return version_compare($this->installedDrupalVersion(), $configuration->getIntroducedVersion(), '<'); + } + + /** + * @phpstan-return non-empty-string + */ + public function installedDrupalVersion(): string { + return str_replace([ + '.x-dev', + '-dev' + ], '.0', \Drupal::VERSION); + } + + /** + * Check if Drupal version and the introduced version support backward + * compatible calls. Although it was introduced in Drupal 10.1 we + * also supply these patches for changes introduced in Drupal 10.0. + * The reason for this is that will start supplying patches for + * Drupal 10 when 10.0 is already out of support. This means that + * we will not support running drupal-rector on Drupal 10.0.x. + * + * @param \DrupalRector\Contract\VersionedConfigurationInterface $configuration + * + * @return bool + */ + public function supportBackwardsCompatibility(VersionedConfigurationInterface $configuration): bool { + return !(version_compare($this->installedDrupalVersion(), '10.1.0', '<') || version_compare($configuration->getIntroducedVersion(), '10.0.0', '<')); + } + } From 331b6318860fae5320820125e4b6f25cf94f1eb4 Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 11 Dec 2023 16:39:41 +0100 Subject: [PATCH 14/27] Fix some logic in AbstractDrupalCoreRector so its more readable (and return types match the method names). --- src/Rector/AbstractDrupalCoreRector.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Rector/AbstractDrupalCoreRector.php b/src/Rector/AbstractDrupalCoreRector.php index 3bf5272f..30db74ee 100644 --- a/src/Rector/AbstractDrupalCoreRector.php +++ b/src/Rector/AbstractDrupalCoreRector.php @@ -59,7 +59,7 @@ protected function isInBackwardsCompatibleCall(Node $node): bool public function refactor(Node $node) { foreach ($this->configuration as $configuration) { - if ($this->rectorShouldApplyToDrupalVersion($configuration)) { + if ($this->rectorShouldApplyToDrupalVersion($configuration) === false) { continue; } @@ -119,17 +119,19 @@ private function createBcCallOnCallLike(Node\Expr\CallLike $node, Node\Expr\Call * * @return bool|int */ - public function rectorShouldApplyToDrupalVersion(VersionedConfigurationInterface $configuration) { - return version_compare($this->installedDrupalVersion(), $configuration->getIntroducedVersion(), '<'); + public function rectorShouldApplyToDrupalVersion(VersionedConfigurationInterface $configuration) + { + return version_compare($this->installedDrupalVersion(), $configuration->getIntroducedVersion(), '>='); } /** * @phpstan-return non-empty-string */ - public function installedDrupalVersion(): string { + public function installedDrupalVersion(): string + { return str_replace([ '.x-dev', - '-dev' + '-dev', ], '.0', \Drupal::VERSION); } @@ -145,8 +147,8 @@ public function installedDrupalVersion(): string { * * @return bool */ - public function supportBackwardsCompatibility(VersionedConfigurationInterface $configuration): bool { + public function supportBackwardsCompatibility(VersionedConfigurationInterface $configuration): bool + { return !(version_compare($this->installedDrupalVersion(), '10.1.0', '<') || version_compare($configuration->getIntroducedVersion(), '10.0.0', '<')); } - } From 99225899bcc38727746c08ba4d5faa4641d32370 Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 11 Dec 2023 16:40:23 +0100 Subject: [PATCH 15/27] Introduce DrupalIntroducedAndRemovalVersionConfiguration and refactor ActionAnnotationToAttributeRector to use AbstractDrupalCoreRector (mostly) --- .../ActionAnnotationToAttributeRector.php | 96 ++++++++++++------- ...troducedAndRemovalVersionConfiguration.php | 30 ++++++ 2 files changed, 89 insertions(+), 37 deletions(-) create mode 100644 src/Drupal10/Rector/ValueObject/DrupalIntroducedAndRemovalVersionConfiguration.php diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index 32f7e8f7..52cc1ecb 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -4,6 +4,10 @@ namespace DrupalRector\Drupal10\Rector\Deprecation; +use DrupalRector\Contract\VersionedConfigurationInterface; +use DrupalRector\Drupal10\Rector\ValueObject\DrupalIntroducedAndRemovalVersionConfiguration; +use DrupalRector\Rector\AbstractDrupalCoreRector; +use DrupalRector\Rector\ValueObject\DrupalIntroducedVersionConfiguration; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Attribute; @@ -13,7 +17,6 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; -use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode; use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; @@ -22,7 +25,6 @@ use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser; use Rector\Comments\NodeDocBlock\DocBlockUpdater; -use Rector\Core\Rector\AbstractRector; use Rector\Core\ValueObject\PhpVersion; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use RectorPrefix202310\PHPUnit\Framework\Attributes\Ticket; @@ -34,38 +36,22 @@ * * @see \Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\TicketAnnotationToAttributeRector\TicketAnnotationToAttributeRectorTest */ -final class ActionAnnotationToAttributeRector extends AbstractRector implements MinPhpVersionInterface +final class ActionAnnotationToAttributeRector extends AbstractDrupalCoreRector implements MinPhpVersionInterface { /** - * @readonly - * - * @var \Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover - */ - private $phpDocTagRemover; - /** - * @readonly - * - * @var \Rector\Comments\NodeDocBlock\DocBlockUpdater + * @var array|DrupalIntroducedAndRemovalVersionConfiguration[] */ - private $docBlockUpdater; - /** - * @readonly - * - * @var \Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser - */ - private $arrayParser; - /** - * @readonly - * - * @var \Rector\BetterPhpDocParser\PhpDocInfo\TokenIteratorFactory - */ - private $tokenIteratorFactory; - /** - * @readonly - * - * @var \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory - */ - private $phpDocInfoFactory; + protected array $configuration = []; + + private PhpDocTagRemover $phpDocTagRemover; + + private DocBlockUpdater $docBlockUpdater; + + private ArrayParser $arrayParser; + + private TokenIteratorFactory $tokenIteratorFactory; + + private PhpDocInfoFactory $phpDocInfoFactory; public function __construct(PhpDocTagRemover $phpDocTagRemover, DocBlockUpdater $docBlockUpdater, PhpDocInfoFactory $phpDocInfoFactory, ArrayParser $arrayParser, TokenIteratorFactory $tokenIteratorFactory) { @@ -76,6 +62,17 @@ public function __construct(PhpDocTagRemover $phpDocTagRemover, DocBlockUpdater $this->tokenIteratorFactory = $tokenIteratorFactory; } + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!($value instanceof DrupalIntroducedAndRemovalVersionConfiguration)) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + + parent::configure($configuration); + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Change annotations with value to attribute', [new CodeSample(<<<'CODE_SAMPLE' @@ -134,12 +131,36 @@ public function provideMinPhpVersion(): int * @param Class_|ClassMethod $node */ public function refactor(Node $node): ?Node + { + foreach ($this->configuration as $configuration) { + if ($this->rectorShouldApplyToDrupalVersion($configuration) === false) { + continue; + } + + $result = $this->refactorWithConfiguration($node, $configuration); + + // Skip if no result. + if ($result === null) { + continue; + } + + return $result; + } + + return null; + } + + /** + * @param Class_|ClassMethod $node + * @param DrupalIntroducedAndRemovalVersionConfiguration $configuration + */ + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node { $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); if (!$phpDocInfo instanceof PhpDocInfo) { return null; } - /** @var PhpDocTagNode[] $tagsByName */ + $tagsByName = $phpDocInfo->getTagsByName('Action'); if ($tagsByName === []) { return null; @@ -155,7 +176,7 @@ public function refactor(Node $node): ?Node } } - $hasChanged = \false; + $docBlockHasChanged = \false; foreach ($tagsByName as $valueNode) { if (!$valueNode->value instanceof GenericTagValueNode) { continue; @@ -170,11 +191,12 @@ public function refactor(Node $node): ?Node $node->attrGroups[] = new AttributeGroup([$attribute]); } - // @todo This cleanup needs some extra logic for BC - $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $valueNode); - $hasChanged = \true; + if (version_compare($this->installedDrupalVersion(), $configuration->getRemoveVersion(), '>=')) { + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $valueNode); + $docBlockHasChanged = \true; + } } - if ($hasChanged) { + if ($docBlockHasChanged) { $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); return $node; diff --git a/src/Drupal10/Rector/ValueObject/DrupalIntroducedAndRemovalVersionConfiguration.php b/src/Drupal10/Rector/ValueObject/DrupalIntroducedAndRemovalVersionConfiguration.php new file mode 100644 index 00000000..3c7399ac --- /dev/null +++ b/src/Drupal10/Rector/ValueObject/DrupalIntroducedAndRemovalVersionConfiguration.php @@ -0,0 +1,30 @@ +introducedVersion = $introducedVersion; + $this->removeVersion = $removeVersion; + } + + public function getIntroducedVersion(): string + { + return $this->introducedVersion; + } + + public function getRemoveVersion(): string + { + return $this->removeVersion; + } +} From 6dad2676ff5b4194570036995dcabf1e7d508f3f Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 11 Dec 2023 16:41:34 +0100 Subject: [PATCH 16/27] Add tests for BC path and update default tests to expect no BC path --- ...yActionAnnotationToAttributeRectorTest.php | 40 +++++++++++++++++++ .../config/configured_rule.php | 5 ++- .../configured_rule_simulate_next_major.php | 14 +++++++ .../bc_basic_fixture.php.inc | 27 +++++++++++++ .../bc_existing_attribute_fixture.php.inc | 28 +++++++++++++ .../bc_multiple_translation_arguments.php.inc | 27 +++++++++++++ .../fixture/basic_fixture.php.inc | 6 +++ .../existing_attribute_fixture.php.inc | 6 +++ .../multiple_translation_arguments.php.inc | 6 +++ 9 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/BackwardsCompatibilityActionAnnotationToAttributeRectorTest.php create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule_simulate_next_major.php create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_basic_fixture.php.inc create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_existing_attribute_fixture.php.inc create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_multiple_translation_arguments.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/BackwardsCompatibilityActionAnnotationToAttributeRectorTest.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/BackwardsCompatibilityActionAnnotationToAttributeRectorTest.php new file mode 100644 index 00000000..194f05a8 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/BackwardsCompatibilityActionAnnotationToAttributeRectorTest.php @@ -0,0 +1,40 @@ +doTestFile($filePath); + } + + /** + * @return Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-next-major'); + } + + public function provideConfigFilePath(): string + { + // must be implemented + return __DIR__.'/config/configured_rule_simulate_next_major.php'; + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php index f2d6a510..2bfff066 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php @@ -3,9 +3,12 @@ declare(strict_types=1); use DrupalRector\Drupal10\Rector\Deprecation\ActionAnnotationToAttributeRector; +use DrupalRector\Drupal10\Rector\ValueObject\DrupalIntroducedAndRemovalVersionConfiguration; use DrupalRector\Tests\Rector\Deprecation\DeprecationBase; use Rector\Config\RectorConfig; return static function (RectorConfig $rectorConfig): void { - DeprecationBase::addClass(ActionAnnotationToAttributeRector::class, $rectorConfig, false); + DeprecationBase::addClass(ActionAnnotationToAttributeRector::class, $rectorConfig, false, [ + new DrupalIntroducedAndRemovalVersionConfiguration('10.2.0', '11.0.0'), + ]); }; diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule_simulate_next_major.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule_simulate_next_major.php new file mode 100644 index 00000000..4bbf6710 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule_simulate_next_major.php @@ -0,0 +1,14 @@ + + +----- + diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_existing_attribute_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_existing_attribute_fixture.php.inc new file mode 100644 index 00000000..8b197dfe --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_existing_attribute_fixture.php.inc @@ -0,0 +1,28 @@ + + +----- + diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_multiple_translation_arguments.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_multiple_translation_arguments.php.inc new file mode 100644 index 00000000..8b1ef848 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_multiple_translation_arguments.php.inc @@ -0,0 +1,27 @@ + + +----- + 'Argument'], ['context' => 'Validation']), type: 'system')] +class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { + +} +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc index 4b4d78d9..b2d29312 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc @@ -19,6 +19,12 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface /** * A basic example action that does nothing. + * + * @Action( + * id = "action_example_basic_action", + * action_label = @Translation("Action Example: A basic example action that does nothing"), + * type = "system" + * ) */ #[\Drupal\Core\Action\Attribute\Action(id: 'action_example_basic_action', action_label: new Drupal\Core\StringTranslation\TranslatableMarkup('Action Example: A basic example action that does nothing'), type: 'system')] class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc index 8b197dfe..b61b3c69 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc @@ -20,6 +20,12 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface /** * A basic example action that does nothing. + * + * @Action( + * id = "action_example_basic_action", + * action_label = @Translation("Action Example: A basic example action that does nothing"), + * type = "system" + * ) */ #[\Drupal\Core\Action\Attribute\Action(id: 'action_example_basic_action', action_label: new Drupal\Core\StringTranslation\TranslatableMarkup('Action Example: A basic example action that does nothing'), type: 'system')] class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc index 8b1ef848..0a17041b 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc @@ -19,6 +19,12 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface /** * A basic example action that does nothing. + * + * @Action( + * id = "action_example_basic_action", + * action_label = @Translation("Action Example: A basic example action that does nothing and has an @argument", context = "Validation", arguments = {"@argument" = "Argument"}), + * type = "system" + * ) */ #[\Drupal\Core\Action\Attribute\Action(id: 'action_example_basic_action', action_label: new Drupal\Core\StringTranslation\TranslatableMarkup('Action Example: A basic example action that does nothing and has an @argument', ['@argument' => 'Argument'], ['context' => 'Validation']), type: 'system')] class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { From 651332f808c01ba1c9de2df079442e494ca9ffec Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 11 Dec 2023 21:19:30 +0100 Subject: [PATCH 17/27] Fix convertTranslateAnnotation early return. Also simplify a little by taking the actual annotation instead of its container. --- .../Deprecation/ActionAnnotationToAttributeRector.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index 52cc1ecb..50133cc3 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -216,7 +216,7 @@ private function createAttribute(array $parsedArgs): Attribute $args = []; foreach ($parsedArgs as $value) { if ($value->key === 'action_label') { - $arg = $this->convertTranslateAnnotation($value); + $arg = $this->convertTranslateAnnotation($value->value); } else { $arg = new String_($value->value->value); } @@ -226,10 +226,10 @@ private function createAttribute(array $parsedArgs): Attribute return new Attribute($fullyQualified, $args); } - public function convertTranslateAnnotation(ArrayItemNode $value): ?Node\Expr\New_ + public function convertTranslateAnnotation(DoctrineAnnotationTagValueNode $value): ?Node\Expr\New_ { // Check the annotation type, this will be helpful later. - if (!$value->value instanceof DoctrineAnnotationTagValueNode || $value->value->identifierTypeNode->name === 'Translation') { + if ($value->identifierTypeNode->name !== '@Translation') { return null; } @@ -238,7 +238,7 @@ public function convertTranslateAnnotation(ArrayItemNode $value): ?Node\Expr\New $contextArg = null; // Loop through the values of the annotation, just to make 100% sure we have the correct argument order - foreach ($value->value->values as $translateValue) { + foreach ($value->values as $translateValue) { if ($translateValue->key === null) { $valueArg = $this->nodeFactory->createArg($translateValue->value->value); } From 773255a33776a978f3d7aa5e3aa917c100c47ca2 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 14 Jan 2024 16:21:07 +0100 Subject: [PATCH 18/27] Fix style --- .../Rector/Deprecation/ActionAnnotationToAttributeRector.php | 4 ++-- src/Rector/AbstractDrupalCoreRector.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index 50133cc3..93291603 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -25,7 +25,7 @@ use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser; use Rector\Comments\NodeDocBlock\DocBlockUpdater; -use Rector\Core\ValueObject\PhpVersion; +use Rector\ValueObject\PhpVersion; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use RectorPrefix202310\PHPUnit\Framework\Attributes\Ticket; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -208,7 +208,7 @@ public function refactorWithConfiguration(Node $node, VersionedConfigurationInte /** * @param array|ArrayItemNode[] $parsedArgs * - * @return \PhpParser\Node\Attribute + * @return Attribute */ private function createAttribute(array $parsedArgs): Attribute { diff --git a/src/Rector/AbstractDrupalCoreRector.php b/src/Rector/AbstractDrupalCoreRector.php index 30db74ee..89ac7c5b 100644 --- a/src/Rector/AbstractDrupalCoreRector.php +++ b/src/Rector/AbstractDrupalCoreRector.php @@ -115,7 +115,7 @@ private function createBcCallOnCallLike(Node\Expr\CallLike $node, Node\Expr\Call } /** - * @param \DrupalRector\Contract\VersionedConfigurationInterface $configuration + * @param VersionedConfigurationInterface $configuration * * @return bool|int */ @@ -143,7 +143,7 @@ public function installedDrupalVersion(): string * Drupal 10 when 10.0 is already out of support. This means that * we will not support running drupal-rector on Drupal 10.0.x. * - * @param \DrupalRector\Contract\VersionedConfigurationInterface $configuration + * @param VersionedConfigurationInterface $configuration * * @return bool */ From f249f01851f9c4ad6c674aaf6736d38e9bca0533 Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 8 Mar 2024 10:01:14 +0100 Subject: [PATCH 19/27] Remove prefixed class from use statements. --- .../Rector/Deprecation/ActionAnnotationToAttributeRector.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php index 93291603..f2c14612 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -27,7 +27,6 @@ use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\ValueObject\PhpVersion; use Rector\VersionBonding\Contract\MinPhpVersionInterface; -use RectorPrefix202310\PHPUnit\Framework\Attributes\Ticket; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; From 262dffa0be23ef9b70e42dbe8c85199c0a5425e9 Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 8 Mar 2024 10:37:51 +0100 Subject: [PATCH 20/27] Allow rule to be configured so we can use it on multiple rules. Rename configuration to more targetted. --- config/drupal-10/drupal-10.2-deprecations.php | 11 +++++++-- ...or.php => AnnotationToAttributeRector.php} | 24 +++++++++---------- ...=> AnnotationToAttributeConfiguration.php} | 20 ++++++++++++++-- .../config/configured_rule.php | 8 +++---- .../configured_rule_simulate_next_major.php | 8 +++---- 5 files changed, 47 insertions(+), 24 deletions(-) rename src/Drupal10/Rector/Deprecation/{ActionAnnotationToAttributeRector.php => AnnotationToAttributeRector.php} (89%) rename src/Drupal10/Rector/ValueObject/{DrupalIntroducedAndRemovalVersionConfiguration.php => AnnotationToAttributeConfiguration.php} (54%) diff --git a/config/drupal-10/drupal-10.2-deprecations.php b/config/drupal-10/drupal-10.2-deprecations.php index 7dd9ae8f..5bc5bf51 100644 --- a/config/drupal-10/drupal-10.2-deprecations.php +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -2,7 +2,8 @@ declare(strict_types=1); -use DrupalRector\Drupal10\Rector\Deprecation\ActionAnnotationToAttributeRector; +use DrupalRector\Drupal10\Rector\Deprecation\AnnotationToAttributeRector; +use DrupalRector\Drupal10\Rector\ValueObject\AnnotationToAttributeConfiguration; use DrupalRector\Rector\Deprecation\FunctionToStaticRector; use DrupalRector\Rector\Deprecation\MethodToMethodWithCheckRector; use DrupalRector\Rector\ValueObject\FunctionToStaticConfiguration; @@ -27,5 +28,11 @@ ]); // @see https://www.drupal.org/node/3395575 - $rectorConfig->rule(ActionAnnotationToAttributeRector::class); + $rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttributeConfiguration('10.2.0', '12.0.0', 'Action', 'Drupal\Core\Action\Attribute\Action'), + ]); + // This rule is disabled from now to first see how it performs on core. + $rectorConfig->skip([ + AnnotationToAttributeRector::class, + ]); }; diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php similarity index 89% rename from src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php rename to src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php index f2c14612..a52493be 100644 --- a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php @@ -5,7 +5,7 @@ namespace DrupalRector\Drupal10\Rector\Deprecation; use DrupalRector\Contract\VersionedConfigurationInterface; -use DrupalRector\Drupal10\Rector\ValueObject\DrupalIntroducedAndRemovalVersionConfiguration; +use DrupalRector\Drupal10\Rector\ValueObject\AnnotationToAttributeConfiguration; use DrupalRector\Rector\AbstractDrupalCoreRector; use DrupalRector\Rector\ValueObject\DrupalIntroducedVersionConfiguration; use PhpParser\Node; @@ -35,10 +35,10 @@ * * @see \Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\TicketAnnotationToAttributeRector\TicketAnnotationToAttributeRectorTest */ -final class ActionAnnotationToAttributeRector extends AbstractDrupalCoreRector implements MinPhpVersionInterface +final class AnnotationToAttributeRector extends AbstractDrupalCoreRector implements MinPhpVersionInterface { /** - * @var array|DrupalIntroducedAndRemovalVersionConfiguration[] + * @var array|AnnotationToAttributeConfiguration[] */ protected array $configuration = []; @@ -64,7 +64,7 @@ public function __construct(PhpDocTagRemover $phpDocTagRemover, DocBlockUpdater public function configure(array $configuration): void { foreach ($configuration as $value) { - if (!($value instanceof DrupalIntroducedAndRemovalVersionConfiguration)) { + if (!($value instanceof AnnotationToAttributeConfiguration)) { throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); } } @@ -150,8 +150,8 @@ public function refactor(Node $node): ?Node } /** - * @param Class_|ClassMethod $node - * @param DrupalIntroducedAndRemovalVersionConfiguration $configuration + * @param Class_|ClassMethod $node + * @param AnnotationToAttributeConfiguration $configuration */ public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node { @@ -160,7 +160,7 @@ public function refactorWithConfiguration(Node $node, VersionedConfigurationInte return null; } - $tagsByName = $phpDocInfo->getTagsByName('Action'); + $tagsByName = $phpDocInfo->getTagsByName($configuration->getAnnotation()); if ($tagsByName === []) { return null; } @@ -168,7 +168,7 @@ public function refactorWithConfiguration(Node $node, VersionedConfigurationInte $hasAttribute = false; foreach ($node->attrGroups as $attrGroup) { foreach ($attrGroup->attrs as $attr) { - if ($attr->name->toString() === 'Drupal\Core\Action\Attribute\Action') { + if ($attr->name->toString() === $configuration->getAttributeClass()) { $hasAttribute = true; break 2; } @@ -186,7 +186,7 @@ public function refactorWithConfiguration(Node $node, VersionedConfigurationInte $stringValue = '{'.trim($stringValue, '()').'}'; $tokenIterator = $this->tokenIteratorFactory->create($stringValue); $data = $this->arrayParser->parseCurlyArray($tokenIterator, $node); - $attribute = $this->createAttribute($data); + $attribute = $this->createAttribute($configuration->getAttributeClass(), $data); $node->attrGroups[] = new AttributeGroup([$attribute]); } @@ -209,12 +209,12 @@ public function refactorWithConfiguration(Node $node, VersionedConfigurationInte * * @return Attribute */ - private function createAttribute(array $parsedArgs): Attribute + private function createAttribute(string $attributeClass, array $parsedArgs): Attribute { - $fullyQualified = new FullyQualified('Drupal\Core\Action\Attribute\Action'); + $fullyQualified = new FullyQualified($attributeClass); $args = []; foreach ($parsedArgs as $value) { - if ($value->key === 'action_label') { + if ($value->value instanceof DoctrineAnnotationTagValueNode) { $arg = $this->convertTranslateAnnotation($value->value); } else { $arg = new String_($value->value->value); diff --git a/src/Drupal10/Rector/ValueObject/DrupalIntroducedAndRemovalVersionConfiguration.php b/src/Drupal10/Rector/ValueObject/AnnotationToAttributeConfiguration.php similarity index 54% rename from src/Drupal10/Rector/ValueObject/DrupalIntroducedAndRemovalVersionConfiguration.php rename to src/Drupal10/Rector/ValueObject/AnnotationToAttributeConfiguration.php index 3c7399ac..7d8ee0e5 100644 --- a/src/Drupal10/Rector/ValueObject/DrupalIntroducedAndRemovalVersionConfiguration.php +++ b/src/Drupal10/Rector/ValueObject/AnnotationToAttributeConfiguration.php @@ -6,16 +6,22 @@ use DrupalRector\Contract\VersionedConfigurationInterface; -class DrupalIntroducedAndRemovalVersionConfiguration implements VersionedConfigurationInterface +class AnnotationToAttributeConfiguration implements VersionedConfigurationInterface { private string $introducedVersion; private string $removeVersion; - public function __construct(string $introducedVersion, string $removeVersion) + private string $annotation; + + private string $attributeClass; + + public function __construct(string $introducedVersion, string $removeVersion, string $annotation, string $attributeClass) { $this->introducedVersion = $introducedVersion; $this->removeVersion = $removeVersion; + $this->annotation = $annotation; + $this->attributeClass = $attributeClass; } public function getIntroducedVersion(): string @@ -27,4 +33,14 @@ public function getRemoveVersion(): string { return $this->removeVersion; } + + public function getAnnotation(): string + { + return $this->annotation; + } + + public function getAttributeClass(): string + { + return $this->attributeClass; + } } diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php index 2bfff066..41ea4d74 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php @@ -2,13 +2,13 @@ declare(strict_types=1); -use DrupalRector\Drupal10\Rector\Deprecation\ActionAnnotationToAttributeRector; -use DrupalRector\Drupal10\Rector\ValueObject\DrupalIntroducedAndRemovalVersionConfiguration; +use DrupalRector\Drupal10\Rector\Deprecation\AnnotationToAttributeRector; +use DrupalRector\Drupal10\Rector\ValueObject\AnnotationToAttributeConfiguration; use DrupalRector\Tests\Rector\Deprecation\DeprecationBase; use Rector\Config\RectorConfig; return static function (RectorConfig $rectorConfig): void { - DeprecationBase::addClass(ActionAnnotationToAttributeRector::class, $rectorConfig, false, [ - new DrupalIntroducedAndRemovalVersionConfiguration('10.2.0', '11.0.0'), + DeprecationBase::addClass(AnnotationToAttributeRector::class, $rectorConfig, false, [ + new AnnotationToAttributeConfiguration('10.2.0', '11.0.0', 'Action', 'Drupal\Core\Action\Attribute\Action'), ]); }; diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule_simulate_next_major.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule_simulate_next_major.php index 4bbf6710..65aac890 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule_simulate_next_major.php +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule_simulate_next_major.php @@ -2,13 +2,13 @@ declare(strict_types=1); -use DrupalRector\Drupal10\Rector\Deprecation\ActionAnnotationToAttributeRector; -use DrupalRector\Drupal10\Rector\ValueObject\DrupalIntroducedAndRemovalVersionConfiguration; +use DrupalRector\Drupal10\Rector\Deprecation\AnnotationToAttributeRector; +use DrupalRector\Drupal10\Rector\ValueObject\AnnotationToAttributeConfiguration; use DrupalRector\Tests\Rector\Deprecation\DeprecationBase; use Rector\Config\RectorConfig; return static function (RectorConfig $rectorConfig): void { - DeprecationBase::addClass(ActionAnnotationToAttributeRector::class, $rectorConfig, false, [ - new DrupalIntroducedAndRemovalVersionConfiguration('10.2.0', '10.0.0'), + DeprecationBase::addClass(AnnotationToAttributeRector::class, $rectorConfig, false, [ + new AnnotationToAttributeConfiguration('10.2.0', '10.0.0', 'Action', 'Drupal\Core\Action\Attribute\Action'), ]); }; From 8f0fedaa0ed4cc0f8e71b55a964e19c3440f3d0b Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 8 Mar 2024 12:16:17 +0100 Subject: [PATCH 21/27] Rename fixtures to add plugin id --- ...ic_fixture.php.inc => action_bc_basic_fixture.php.inc} | 0 ...p.inc => action_bc_existing_attribute_fixture.php.inc} | 0 ...c => action_bc_multiple_translation_arguments.php.inc} | 0 ...basic_fixture.php.inc => action_basic_fixture.php.inc} | 0 ....php.inc => action_existing_attribute_fixture.php.inc} | 0 ....inc => action_multiple_translation_arguments.php.inc} | 8 +++++--- 6 files changed, 5 insertions(+), 3 deletions(-) rename tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/{bc_basic_fixture.php.inc => action_bc_basic_fixture.php.inc} (100%) rename tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/{bc_existing_attribute_fixture.php.inc => action_bc_existing_attribute_fixture.php.inc} (100%) rename tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/{bc_multiple_translation_arguments.php.inc => action_bc_multiple_translation_arguments.php.inc} (100%) rename tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/{basic_fixture.php.inc => action_basic_fixture.php.inc} (100%) rename tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/{existing_attribute_fixture.php.inc => action_existing_attribute_fixture.php.inc} (100%) rename tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/{multiple_translation_arguments.php.inc => action_multiple_translation_arguments.php.inc} (74%) diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_basic_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/action_bc_basic_fixture.php.inc similarity index 100% rename from tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_basic_fixture.php.inc rename to tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/action_bc_basic_fixture.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_existing_attribute_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/action_bc_existing_attribute_fixture.php.inc similarity index 100% rename from tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_existing_attribute_fixture.php.inc rename to tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/action_bc_existing_attribute_fixture.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_multiple_translation_arguments.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/action_bc_multiple_translation_arguments.php.inc similarity index 100% rename from tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/bc_multiple_translation_arguments.php.inc rename to tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture-next-major/action_bc_multiple_translation_arguments.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/action_basic_fixture.php.inc similarity index 100% rename from tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/basic_fixture.php.inc rename to tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/action_basic_fixture.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/action_existing_attribute_fixture.php.inc similarity index 100% rename from tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/existing_attribute_fixture.php.inc rename to tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/action_existing_attribute_fixture.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/action_multiple_translation_arguments.php.inc similarity index 74% rename from tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc rename to tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/action_multiple_translation_arguments.php.inc index 0a17041b..fc1736ee 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/multiple_translation_arguments.php.inc +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/action_multiple_translation_arguments.php.inc @@ -6,7 +6,8 @@ * @Action( * id = "action_example_basic_action", * action_label = @Translation("Action Example: A basic example action that does nothing and has an @argument", context = "Validation", arguments = {"@argument" = "Argument"}), - * type = "system" + * type = "system", + * deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver" * ) */ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { @@ -23,10 +24,11 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface * @Action( * id = "action_example_basic_action", * action_label = @Translation("Action Example: A basic example action that does nothing and has an @argument", context = "Validation", arguments = {"@argument" = "Argument"}), - * type = "system" + * type = "system", + * deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver" * ) */ -#[\Drupal\Core\Action\Attribute\Action(id: 'action_example_basic_action', action_label: new Drupal\Core\StringTranslation\TranslatableMarkup('Action Example: A basic example action that does nothing and has an @argument', ['@argument' => 'Argument'], ['context' => 'Validation']), type: 'system')] +#[\Drupal\Core\Action\Attribute\Action(id: 'action_example_basic_action', action_label: new Drupal\Core\StringTranslation\TranslatableMarkup('Action Example: A basic example action that does nothing and has an @argument', ['@argument' => 'Argument'], ['context' => 'Validation']), type: 'system', deriver: \Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver::class)] class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { } From 58566af38ce9b850e1303038d8ad3675aa7d9003 Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 8 Mar 2024 12:16:31 +0100 Subject: [PATCH 22/27] Add block test and configuration --- .../config/configured_rule.php | 1 + .../fixture/block_basic_fixture.php.inc | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/block_basic_fixture.php.inc diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php index 41ea4d74..b7d8687c 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php @@ -10,5 +10,6 @@ return static function (RectorConfig $rectorConfig): void { DeprecationBase::addClass(AnnotationToAttributeRector::class, $rectorConfig, false, [ new AnnotationToAttributeConfiguration('10.2.0', '11.0.0', 'Action', 'Drupal\Core\Action\Attribute\Action'), + new AnnotationToAttributeConfiguration('10.2.0', '11.0.0', 'Block', 'Drupal\Core\Action\Attribute\Block'), ]); }; diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/block_basic_fixture.php.inc b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/block_basic_fixture.php.inc new file mode 100644 index 00000000..14ee6b23 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/fixture/block_basic_fixture.php.inc @@ -0,0 +1,37 @@ + + +----- + false])] +class BasicExample extends ActionBase implements ContainerFactoryPluginInterface { + +} +?> From ce8ad533d25909aa78b0f403b90f23f8f7c1fe6b Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 8 Mar 2024 12:16:47 +0100 Subject: [PATCH 23/27] Add missing deriver key --- .../Rector/Deprecation/AnnotationToAttributeRector.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php index a52493be..c6bb9770 100644 --- a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php @@ -214,7 +214,9 @@ private function createAttribute(string $attributeClass, array $parsedArgs): Att $fullyQualified = new FullyQualified($attributeClass); $args = []; foreach ($parsedArgs as $value) { - if ($value->value instanceof DoctrineAnnotationTagValueNode) { + if ($value->key == 'deriver') { + $arg = $this->nodeFactory->createClassConstFetch($value->value->value,'class'); + } elseif ($value->value instanceof DoctrineAnnotationTagValueNode) { $arg = $this->convertTranslateAnnotation($value->value); } else { $arg = new String_($value->value->value); From fc50d58833f6223778e35db5ad0a503985aec94e Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 8 Mar 2024 12:17:18 +0100 Subject: [PATCH 24/27] Use built in AnnotationToAttribute mapper for forms value in Block annotation --- .../Deprecation/AnnotationToAttributeRector.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php index c6bb9770..d0943905 100644 --- a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php @@ -25,6 +25,7 @@ use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser; use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\PhpAttribute\AnnotationToAttributeMapper; use Rector\ValueObject\PhpVersion; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -52,13 +53,19 @@ final class AnnotationToAttributeRector extends AbstractDrupalCoreRector impleme private PhpDocInfoFactory $phpDocInfoFactory; - public function __construct(PhpDocTagRemover $phpDocTagRemover, DocBlockUpdater $docBlockUpdater, PhpDocInfoFactory $phpDocInfoFactory, ArrayParser $arrayParser, TokenIteratorFactory $tokenIteratorFactory) + /** + * @var \Rector\PhpAttribute\AnnotationToAttributeMapper + */ + private AnnotationToAttributeMapper $annotationToAttributeMapper; + + public function __construct(PhpDocTagRemover $phpDocTagRemover, DocBlockUpdater $docBlockUpdater, PhpDocInfoFactory $phpDocInfoFactory, ArrayParser $arrayParser, TokenIteratorFactory $tokenIteratorFactory, AnnotationToAttributeMapper $annotationToAttributeMapper) { $this->phpDocTagRemover = $phpDocTagRemover; $this->docBlockUpdater = $docBlockUpdater; $this->phpDocInfoFactory = $phpDocInfoFactory; $this->arrayParser = $arrayParser; $this->tokenIteratorFactory = $tokenIteratorFactory; + $this->annotationToAttributeMapper = $annotationToAttributeMapper; } public function configure(array $configuration): void @@ -218,9 +225,13 @@ private function createAttribute(string $attributeClass, array $parsedArgs): Att $arg = $this->nodeFactory->createClassConstFetch($value->value->value,'class'); } elseif ($value->value instanceof DoctrineAnnotationTagValueNode) { $arg = $this->convertTranslateAnnotation($value->value); + } elseif ($value->key === 'forms') { + $attribute = $this->annotationToAttributeMapper->map($value); + $arg = $attribute->value; } else { $arg = new String_($value->value->value); } + $args[] = new Arg($arg, \false, \false, [], new Node\Identifier($value->key)); } From 0d26dd5d96d64f4924f5f44590ff57c03530b365 Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 8 Mar 2024 16:00:22 +0100 Subject: [PATCH 25/27] Fix codestyle --- .../Rector/Deprecation/AnnotationToAttributeRector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php index d0943905..4623909f 100644 --- a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php @@ -54,7 +54,7 @@ final class AnnotationToAttributeRector extends AbstractDrupalCoreRector impleme private PhpDocInfoFactory $phpDocInfoFactory; /** - * @var \Rector\PhpAttribute\AnnotationToAttributeMapper + * @var AnnotationToAttributeMapper */ private AnnotationToAttributeMapper $annotationToAttributeMapper; @@ -222,7 +222,7 @@ private function createAttribute(string $attributeClass, array $parsedArgs): Att $args = []; foreach ($parsedArgs as $value) { if ($value->key == 'deriver') { - $arg = $this->nodeFactory->createClassConstFetch($value->value->value,'class'); + $arg = $this->nodeFactory->createClassConstFetch($value->value->value, 'class'); } elseif ($value->value instanceof DoctrineAnnotationTagValueNode) { $arg = $this->convertTranslateAnnotation($value->value); } elseif ($value->key === 'forms') { From 85f4742bed7095c0803f890ce6c6e7b941d15d6e Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 8 Mar 2024 16:12:18 +0100 Subject: [PATCH 26/27] Fully remove annotations from drupal 10 set for now until we have better validation on these. --- config/drupal-10/drupal-10.2-deprecations.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/config/drupal-10/drupal-10.2-deprecations.php b/config/drupal-10/drupal-10.2-deprecations.php index 5bc5bf51..570884e9 100644 --- a/config/drupal-10/drupal-10.2-deprecations.php +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -27,12 +27,4 @@ new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'setResource', 'setImage'), ]); - // @see https://www.drupal.org/node/3395575 - $rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [ - new AnnotationToAttributeConfiguration('10.2.0', '12.0.0', 'Action', 'Drupal\Core\Action\Attribute\Action'), - ]); - // This rule is disabled from now to first see how it performs on core. - $rectorConfig->skip([ - AnnotationToAttributeRector::class, - ]); }; From 12ebdc7bf87ea0b73991a7b11ab21b3e3381f82b Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 8 Mar 2024 16:21:30 +0100 Subject: [PATCH 27/27] Protected to public --- config/drupal-10/drupal-10.2-deprecations.php | 3 --- .../Rector/Deprecation/AnnotationToAttributeRector.php | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/config/drupal-10/drupal-10.2-deprecations.php b/config/drupal-10/drupal-10.2-deprecations.php index 570884e9..869a4838 100644 --- a/config/drupal-10/drupal-10.2-deprecations.php +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -2,8 +2,6 @@ declare(strict_types=1); -use DrupalRector\Drupal10\Rector\Deprecation\AnnotationToAttributeRector; -use DrupalRector\Drupal10\Rector\ValueObject\AnnotationToAttributeConfiguration; use DrupalRector\Rector\Deprecation\FunctionToStaticRector; use DrupalRector\Rector\Deprecation\MethodToMethodWithCheckRector; use DrupalRector\Rector\ValueObject\FunctionToStaticConfiguration; @@ -26,5 +24,4 @@ new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'getResource', 'getImage'), new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'setResource', 'setImage'), ]); - }; diff --git a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php index 4623909f..e34d84a9 100644 --- a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php @@ -160,7 +160,7 @@ public function refactor(Node $node): ?Node * @param Class_|ClassMethod $node * @param AnnotationToAttributeConfiguration $configuration */ - public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node { $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); if (!$phpDocInfo instanceof PhpDocInfo) {