diff --git a/config/drupal-10/drupal-10-all-deprecations.php b/config/drupal-10/drupal-10-all-deprecations.php index d7667917..43cbdd84 100644 --- a/config/drupal-10/drupal-10-all-deprecations.php +++ b/config/drupal-10/drupal-10-all-deprecations.php @@ -9,6 +9,7 @@ $rectorConfig->sets([ Drupal10SetList::DRUPAL_100, Drupal10SetList::DRUPAL_101, + Drupal10SetList::DRUPAL_102, ]); $rectorConfig->bootstrapFiles([ diff --git a/config/drupal-10/drupal-10.2-deprecations.php b/config/drupal-10/drupal-10.2-deprecations.php new file mode 100644 index 00000000..7b3b63e5 --- /dev/null +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -0,0 +1,11 @@ +rule(ActionAnnotationToAttributeRector::class); +}; diff --git a/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php new file mode 100644 index 00000000..bd769964 --- /dev/null +++ b/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector.php @@ -0,0 +1,190 @@ +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; + } + + /** + * @param array|ArrayItemNode[] $parsedArgs + * + * @return \PhpParser\Node\Attribute + */ + private function createAttribute(array $parsedArgs): Attribute + { + $fullyQualified = new FullyQualified('Drupal\Core\Action\Attribute\Action'); + $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))]); + } 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 aafeb6e5..768e2eba 100644 --- a/src/Set/Drupal10SetList.php +++ b/src/Set/Drupal10SetList.php @@ -11,4 +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'; } 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..f2d6a510 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/config/configured_rule.php @@ -0,0 +1,11 @@ + + +----- + 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 @@ + + +----- + 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 { + +} +?>