From 5f4a4e88b13a900e2125c7c2a2342a2fad334da6 Mon Sep 17 00:00:00 2001 From: Andy Postnikov Date: Sun, 29 Oct 2023 03:09:58 +0100 Subject: [PATCH] WIP: Rule to convert action to attributes --- .../drupal-10/drupal-10-all-deprecations.php | 1 + config/drupal-10/drupal-10.2-deprecations.php | 11 ++ .../ActionAnnotationToAttributeRector.php | 171 ++++++++++++++++++ src/Set/Drupal10SetList.php | 1 + 4 files changed, 184 insertions(+) create mode 100644 config/drupal-10/drupal-10.2-deprecations.php create mode 100644 src/Rector/Deprecation/ActionAnnotationToAttributeRector.php diff --git a/config/drupal-10/drupal-10-all-deprecations.php b/config/drupal-10/drupal-10-all-deprecations.php index 8b597ee7..2121bdbc 100644 --- a/config/drupal-10/drupal-10-all-deprecations.php +++ b/config/drupal-10/drupal-10-all-deprecations.php @@ -12,6 +12,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..6267bacf --- /dev/null +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -0,0 +1,11 @@ +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 18ca9638..38cb2efa 100644 --- a/src/Set/Drupal10SetList.php +++ b/src/Set/Drupal10SetList.php @@ -9,4 +9,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'; }