Skip to content

Commit

Permalink
feat: Optimize AnnotationToAttributeRector and add core plugin conver…
Browse files Browse the repository at this point in the history
…sion documentation (#296)

* feat: add PluralTranslation and test for ConfigEntityType

* fix: codestyle

* fix:L ContentEntityType

* fix: overmatching )

* fix: tests and style

* fix: new Drupal\Core\StringTranslation\TranslatableMarkup should be new \Drupal\Core\StringTranslation\TranslatableMarkup

* feat: add documentation for core plugin conversion

* fix: correct paths for rector.php
  • Loading branch information
bbrala authored Mar 9, 2024
1 parent 2af405c commit dc2e858
Show file tree
Hide file tree
Showing 12 changed files with 485 additions and 17 deletions.
88 changes: 88 additions & 0 deletions docs/core_plugin_conversion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Core annotation to attribute conversion

To convert a plugin in core from an annotation to an attribute, you need to do the following:

## Install drupal-rector
```bash
composer require --dev palantirnet/drupal-rector
```

## Configure drupal-rector (rector.php)

Create the folling file in the root of core. In this example we are converting the `ContentEntityType` and `ConfigEntityType` annotations to attributes. If you want to convert other annotations, you will need to configure the `AnnotationToAttributeRector` with the appropriate `AnnotationToAttributeConfiguration` objects.

```php
<?php
declare(strict_types=1);

use Rector\Config\RectorConfig;

return static function(RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(\DrupalRector\Drupal10\Rector\Deprecation\AnnotationToAttributeRector::class, [

// Setting both introduce and remove version to 10.x means the comments are not kept. Which is good for core. ;)
new \DrupalRector\Drupal10\Rector\ValueObject\AnnotationToAttributeConfiguration('10.0.0', '10.0.0', 'ContentEntityType', 'Drupal\Core\Entity\Attribute\ContentEntityType'),
new \DrupalRector\Drupal10\Rector\ValueObject\AnnotationToAttributeConfiguration('10.0.0', '10.0.0', 'ConfigEntityType', 'Drupal\Core\Entity\Attribute\ConfigEntityType'),
]);

$rectorConfig->autoloadPaths([
'./core/lib',
'./core/modules',
'./core/profiles',
'./core/themes'
]);


$rectorConfig->skip([
'*/upgrade_status/tests/modules/*',
'*/ProxyClass/*',
'*/tests/fixtures/*',
'*/vendor/*',
]);
$rectorConfig->fileExtensions([
'php',
'module',
'theme',
'install',
'profile',
'inc',
'engine'
]);
$rectorConfig->importNames(FALSE, FALSE);
$rectorConfig->importShortClasses(FALSE);
};
```
## Running rector against core

Running will take a while. You can run against specific directories like so:

```bash
vendor/bin/rector process ./core/lib
vendor/bin/rector process ./core/modules
vendor/bin/rector process ./core/themes
```

Or run against speific modules:

```bash
vendor/bin/rector process ./core/modules/system
vendor/bin/rector process ./core/modules/user
```

Or if you have horsepower, run against the whole of core:

```bash
vendor/bin/rector process ./core
```

## Review the changes

Always review the changes. Rector is a tool to help you, not to do the work for you. It will not be able to convert everything, and it may make mistakes. Make sure you understand the changes and that they are correct.

## Reporting errors

If you find an error, please report it to the rector project. You can do this by creating an issue in the rector project on Drupal.org.

We are also available on the #rector channel on Drupal Slack.

@bbrala @mglaman @agentrickard
36 changes: 30 additions & 6 deletions src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
Expand Down Expand Up @@ -224,12 +223,15 @@ private function createAttribute(string $attributeClass, array $parsedArgs): Att
if ($value->key == 'deriver') {
$arg = $this->nodeFactory->createClassConstFetch($value->value->value, 'class');
} elseif ($value->value instanceof DoctrineAnnotationTagValueNode) {
$arg = $this->convertTranslateAnnotation($value->value);
} elseif ($value->key === 'forms') {
$arg = $this->convertAnnotation($value->value);
} else {
$attribute = $this->annotationToAttributeMapper->map($value);
$arg = $attribute->value;
} else {
$arg = new String_($value->value->value);
}

// Sometimes the end ) matches. We need to remove it.
if ($value->key === null) {
continue;
}

$args[] = new Arg($arg, \false, \false, [], new Node\Identifier($value->key));
Expand All @@ -238,6 +240,28 @@ private function createAttribute(string $attributeClass, array $parsedArgs): Att
return new Attribute($fullyQualified, $args);
}

public function convertAnnotation(DoctrineAnnotationTagValueNode $value): ?Node\Expr
{
return match ($value->identifierTypeNode->name) {
'@Translation' => $this->convertTranslateAnnotation($value),
'@PluralTranslation' => $this->convertPluralTranslationAnnotation($value),
default => null,
};
}

public function convertPluralTranslationAnnotation(DoctrineAnnotationTagValueNode $value): ?Node\Expr
{
// Check the annotation type, this will be helpful later.
if ($value->identifierTypeNode->name !== '@PluralTranslation') {
return null;
}

return $this->nodeFactory->createArray([
$value->values[0]->key => $value->values[0]->value->value,
$value->values[1]->key => $value->values[1]->value->value,
]);
}

public function convertTranslateAnnotation(DoctrineAnnotationTagValueNode $value): ?Node\Expr\New_
{
// Check the annotation type, this will be helpful later.
Expand Down Expand Up @@ -277,6 +301,6 @@ public function convertTranslateAnnotation(DoctrineAnnotationTagValueNode $value
$argArray[] = $contextArg;
}

return new Node\Expr\New_(new Node\Name('Drupal\Core\StringTranslation\TranslatableMarkup'), $argArray);
return new Node\Expr\New_(new Node\Name('\Drupal\Core\StringTranslation\TranslatableMarkup'), $argArray);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

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'),
new AnnotationToAttributeConfiguration('10.2.0', '12.0.0', 'Action', 'Drupal\Core\Action\Attribute\Action'),
new AnnotationToAttributeConfiguration('10.2.0', '12.0.0', 'Block', 'Drupal\Core\Action\Attribute\Block'),
new AnnotationToAttributeConfiguration('10.2.0', '12.0.0', 'ContentEntityType', 'Drupal\Core\Entity\Attribute\ContentEntityType'),
]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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'), 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 {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* 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')]
#[\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 {

}
Expand All @@ -21,7 +21,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'), 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 {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 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')]
class BasicExample extends ActionBase implements ContainerFactoryPluginInterface {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface
* 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')]
#[\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 {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* 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')]
#[\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 {

}
Expand All @@ -27,7 +27,7 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface
* 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')]
#[\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 {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface
* 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', deriver: \Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver::class)]
#[\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 {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class BasicExample extends ActionBase implements ContainerFactoryPluginInterface
* },
* )
*/
#[\Drupal\Core\Action\Attribute\Block(id: 'page_title_block', admin_label: new Drupal\Core\StringTranslation\TranslatableMarkup('Page title'), forms: ['settings_tray' => false])]
#[\Drupal\Core\Action\Attribute\Block(id: 'page_title_block', admin_label: new \Drupal\Core\StringTranslation\TranslatableMarkup('Page title'), forms: ['settings_tray' => false])]
class BasicExample extends ActionBase implements ContainerFactoryPluginInterface {

}
Expand Down
Loading

0 comments on commit dc2e858

Please sign in to comment.