Adding annotation metadata for pluginsfor Drupal 8 , 9 , 10 , and 11

Last updated :  

A plugin annotation is a nutrition label for a plugin implementation. Just like a nutrition label lists exactly what's in a product in a fixed, predictable format — without you having to open the packaging and analyze the contents yourself — a plugin annotation declares a plugin's id, label, and any custom metadata in a fixed format the plugin manager can read without ever instantiating the class. This lesson looks at how to define that format.

What you'll learn in this lesson

  • Why annotations are read without instantiating the plugin class
  • How to define an annotation class with both required and custom properties
  • The @Translation() convention for translatable annotation values
  • Why the trailing @Annotation line in the docblock is mandatory

Why annotations, not constructors

Imagine a site with 40 enabled modules, each contributing a handful of plugins. If Drupal had to instantiate every single plugin class just to find out its label and description (say, for a dropdown listing all available options), that would mean running dozens of constructors — and their dependencies' constructors — on every single page load, just to build a list. Annotations solve this by putting that metadata in a docblock comment above the class, which Drupal's discovery system can parse directly from the file without ever loading or instantiating the PHP class itself. Only when a specific plugin is actually needed does Drupal instantiate it.

Defining the annotation class

An annotation class is a small, mostly-empty PHP class whose public properties describe what a plugin implementation is allowed to declare:

namespace Drupal\plugin_type_example\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
 * Defines a Sandwich annotation object.
 *
 * @see \Drupal\plugin_type_example\SandwichPluginManager
 * @see plugin_api
 *
 * @Annotation
 */
class Sandwich extends Plugin {

  /**
   * A brief, human readable, description of the sandwich type.
   *
   * @var \Drupal\Core\Annotation\Translation
   */
  public $description;

  /**
   * The number of calories per serving of this sandwich type.
   *
   * @var int
   */
  public $calories;

}

Every annotation class extends the base Plugin annotation, which already supplies the universal id property every plugin needs — you only declare the extra properties specific to your plugin type. Here that's description (translatable, hence the \Drupal\Core\Annotation\Translation type hint) and calories (a plain integer, no translation needed).

The trailing @Annotation line is not decoration. Drupal's annotation-discovery system scans docblocks specifically for classes ending in a standalone @Annotation tag to build its internal list of valid annotation types. Miss that line and Drupal won't recognize this as an annotation class at all — your plugin implementations' annotations will simply fail to parse.

Using the annotation on an implementation

With the annotation class defined, any plugin implementation can now use it:

/**
 * @Sandwich(
 *   id = "ham_sandwich",
 *   description = @Translation("Ham, mustard, rocket, sun-dried tomatoes."),
 *   calories = 426
 * )
 */
class ExampleHamSandwich extends SandwichBase {
  // ...
}

Notice description is wrapped in @Translation(...) — that's what makes this specific value translatable through Drupal's interface translation system, exactly matching the type hint declared on the annotation class's $description property. calories, typed as a plain int, is written as a bare number with no wrapper.

Quick check: could you add a new property called spicy (a boolean) to the Sandwich annotation class after plugins already exist using it? (Yes — existing plugins simply won't set that property and it'll be NULL for them. Annotation properties are optional unless your manager or consuming code specifically requires them; adding one doesn't retroactively break anything.)

Key takeaways

  • Annotations are parsed from docblock comments without instantiating the plugin class — this is what keeps large numbers of plugins cheap to discover.
  • An annotation class extends the base Plugin annotation and only needs to declare properties specific to its plugin type — id comes for free.
  • The trailing @Annotation line in the docblock is required for Drupal to recognize the class as a valid annotation type at all.
  • Wrap any translatable annotation value in @Translation(...); leave plain data types (integers, booleans) as bare values.

Coming up next

You now understand the full lifecycle of a custom plugin type — interface, manager, and annotation. The next lesson turns to one of Drupal core's own plugin types you'll use constantly: building a real Block plugin from the ground up.