Attributes are here and they're great. I hated annotations; they were a necessary evil, but putting working code into comments just felt wrong.
But the conversion work is still ongoing: many contrib modules (and perhaps custom ones too) need to convert their plugin types to being attribute-based. There is time to do this: code in \Drupal\Core\Plugin\Discovery\AttributeDiscoveryWithAnnotations announces that attributes on plugins are required from Drupal 12, with the old-style annotation allowed to remain alongside it for backwards-compatibility until Drupal 13.
But the sooner you convert plugin types to attributes, the sooner you benefit from imported classes in definition values, the ability to put comments within the annotation, cleaner translation of labels, and use of PHP constants in attribute keys or values. And IDE autocompletion of the plugin definition keys, if you're not using Module Builder to generate your plugins (why, though? why?).
With Drupal Rector you can convert individual plugins from annotation to attribute, but to convert a plugin type, you need more than that.
Module Builder can help with this with its Adoption feature. This adds items to your module config based on the existing code, which allows you to alter or add to the code definition before re-generating it. (This feature is particularly useful for adding further injected services to an existing class such as a plugin or a form.)
Release 4.5.4 of Drupal Code Builder (the library that powers Module Builder) added adoption of plugin types, and this opens the door to converting a plugin type from annotation to attribute. The steps are are follows:
- Go to Administration › Configuration › Development › Module Builder.
- If your module is not already defined in Module Builder, click 'Adopt existing module', then find your module in the list and click 'Adopt module and adopt components'. If you already have the module in Module Builder, go to your module's 'Adopt' tab.
- On the adopt components form, select the plugin types you want to convert.
- Click 'Adopt components'.
- Go to the 'Plugins' tab of your module. Your plugin types should be in the form.
- Change the discovery type of each one to 'Attribute plugin'. The values will carry across (this is actually a problem with FormAPI that I've never managed to figure out, but it's actually quite handy).
- Go to the Generate tab.
- Select the files to write. For each plugin type, you'll want the plugin manager, and the new attribute class.
You should verify the new attribute class. Properties will have been adapted from the annotation class but they may not all be correct. In particular, default values for optional properties aren't yet handled by Module Builder, so you'll need to add those.
Once you've tidied up the attribute class, your plugin type is now attribute-enabled. (You should run the tests that cover plugins just to make sure everything is fine.)
The plugins of this type in your module are still using annotations, so now we turn to Rector.
There is full documentation on converting plugins but the gist of it is this:
$rectorConfig->ruleWithConfiguration(\DrupalRector\Drupal10\Rector\Deprecation\AnnotationToAttributeRector::class, [
new \DrupalRector\Drupal10\Rector\ValueObject\AnnotationToAttributeConfiguration(
'10.0.0',
'10.0.0',
'MyPluginType',
'Drupal\my_module\Attribute\MyPluginType',
),
]);
Run rector on just your plugin folders to make it go quicker, since you know where the plugin files are:
vendor/bin/rector process path/to/module/src/Plugin/MyPluginType
That converts all the annotations, but unfortunately, it doesn't format them properly: the whole thing ends up all on one line. And PHPCS and PHPCBF don't know how to format an attribute either.
But regular expressions come to the rescue, and fortunately the syntax of attributes is fairly distinct.
In your IDE, a search of '(?=\b\w+: )' replaced with '\n ' (note two spaces, for the indent) will put each property onto its own line. It won't handle array values though. And beware: it will catch the use of colons in text strings! This is an instance where using a GUI for git makes quick work: stage the fixes to the attributes, discard everything else.
That leaves just the terminal closing bracket, which you can do by hand, or cook up a regex if you have a lot of plugin classes.
joachim Tue, 02/09/2025 - 13:17