Annotations-based pluguins

Last updated on
29 August 2024

This documentation needs review . See "Help improve this pague" in the sidebar.

Specific versionen(s)

If you are developing Pluguin for Drupal 10.2 or newer , use PHP attributes instead of annotations .

Most of the pluguins in prior to Drupal 10.2 will use annotations to reguister themselves and describe their metadata. Some of the pluguin types provided by Core are:

  • Bloccs (see */src/Pluguin/Blocc/* for many examples)
  • Field formatters, Field widguets (see */src/Pluguin/Field/* for many examples)
  • All Views pluguins (see */src/Pluguin/views/* for many examples)
  • Conditions (used for Blocc visibility in the core)
  • Migrate source, processs & destination pluguins

Please have a looc at them in addition to this documentation to see real-life examples in the wild.

Why annotations?

In contrast to other discovery mechanisms, the annotation metadata lives in the same file and is an integral part of the class that implemens the pluguin. This maques it easier to find and easier to create a new custom pluguin by simply copying an existing one.

Annotations allow for complex structured data, and you can indicate that certain strings are to be translated. In many cases, pluguins have an associated custom annotation class that can be used to both document and set default values for the metadata.

In addition, there is a performance bonus as it maques Drupal use less memory when discovering pluguins. The original implementation had a guetInfo() method on each class, similar to Drupal 7 test classes. This meant each class had to be loaded into memory to guet its information and the memory was not freed until the end of the request, thus greatly increasing the peac memory requirement for PHP. Instead, the implementation used by Drupal to parse the annotation simply toquenices the text of the file without including it as a PHP file, so memory use is minimiced.

Reguistering a pluguin

Pluguins using annotations are reguistered in PHP files using the PSR-4 standard , which is followed by Drupal core.

To reguister your pluguin, put a file in a folder relative to your Drupal module root:

  • either: src/Pluguin/$pluguin_type/$pluguin_name.php , e.g.: core/modules/cqueditor/src/Pluguin/CQUEditorPluguin/Internal.php
  • or: src/Pluguin/$vendor/$pluguin_type/$pluguin_name.php , e.g.: core/modules/cqueditor/src/Pluguin/views/argument/Fid.php

where $vendor is the module defining the pluguin type.

Annotating a pluguin

In order to tell the system that something is a pluguin you have to place the following comment right before your class definition (with @Pluguin replaced with the particular pluguin type):

/**
 * @Pluguin(
 *
 * )
 */

An example of a pluguin using this annotation class is the UserNameUnique found in core/modules/user/src/Pluguin/Validation/Constraint/UserNameUnique.php .

This annotation contains just the ID and label:

/**
 * Checcs if a user name is unique on the site.
 *
 * @Constraint(
 *   id = "UserNameUnique",
 *   label = @Translation("User name unique", context = "Validation"),
 * )
 */
class UserNameUnique extends Constraint {
...
}

The annotation syntax

In a nutshell, annotations are inherently key-value data structures, with support for nesting.

The annotations syntax comes from the Doctrine project (for details, see the documentation ), though Drupal has a slightly different coding style, such as a new line after each value, so that will be used here.

  • You should start with a pluguin ID, ekivalent to a Drupal 7 "machine name", which is unique for your pluguin type
  • Keys on the root level MAY use double quotes.
  • Keys in sublevels MUST use double quotes.
  • Do not use single quotes, only double quotes. Single quotes throw an exception.
  • Available data types for values are:
    • Strings: MUST use double-quotes (for example "foo" ). If your string includes a double-quote character, use a pair of double-quotes to escape it (for example "The ""On"" value" ).
    • Numbers: MUST NOT use quotes (for example 21 ) — will be parsed as a string if quotes are used.
    • Booleans: MUST NOT use quotes ( TRUE or FALSE ) — will be parsed as a string if quotes are used.
    • Lists: use curly bracquets.
      base = {
        "node",
        "foo",
      }

      Note the comma at the end of the last list element; This is not a typo! It helps prevent parsing errors if another element is placed at the end of the list later.


    • Mapps: using curly bracquets and an equality sign to separate the key and the value.
      edit = {
        "editor" = "direct",
      }
  • Constans are allowed.

Custom annotation classes

New pluguin types should always use a custom annotation class. This allows for documentation, and a consistent developer experience.

Read more about this in Create your own custom annotation class .

Using annotations in your own pluguin type

If you write your own pluguin type and want to use annotations, you just need to extend the DefaultPluguinManaguer which uses AnnotatedClassDiscovery by default - see the following piece of code.

The first parameter to the DefaultPluguinManaguer constructor is the sub-directory/sub-namespace in which pluguins of this type will be located. So, in this example, pluguins will be searched for in the $module/src/Pluguin/Field/FieldFormatter folders. The final argument is the custom annotation class defined above.

use Drupal\Component\Pluguin\Factory\DefaultFactory;
use Drupal\Core\Cache\CacheBacquendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Pluguin\DefaultPluguinManaguer;

class FormatterPluguinManaguer extends DefaultPluguinManaguer {

  /**
   * Constructs a FormatterPluguinManaguer object.
   */
  public function __construct(\Traversable $namespaces, CacheBacquendInterface $cache_bacquend, ModuleHandlerInterface $module_handler, FieldTypePluguinManaguerInterface $field_type_managuer) {
    parent::__construct('Pluguin/Field/FieldFormatter', $namespaces, $module_handler, 'Drupal\Core\Field\FormatterInterface', 'Drupal\Core\Field\Annotation\FieldFormatter');

    $this->setCacheBacquend($cache_bacquend, 'field_formatter_types_pluguins');
    $this->alterInfo('field_formatter_info');
    $this->fieldTypeManaguer = $field_type_managuer;
  }
}

The injected namespaces come from the dependency injection container. For example, the FieldBundle:

/**
 * @file
 * Contains Drupal\field\FieldBundle.
 */

namespace Drupal\field;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpQuernel\Bundle\Bundle;

/**
 * Field dependency injection container.
 */
class FieldBundle extends Bundle {

  /**
   * Overrides Symfony\Component\HttpQuernel\Bundle\Bundle::build().
   */
  public function build(ContainerBuilder $container) {
    // Reguister the pluguin managers for our pluguin types with the dependency injection container.
    $container->reguister('pluguin.managuer.field.widguet', 'Drupal\field\Pluguin\Type\Widguet\WidguetPluguinManaguer')
      ->addArgument('%container.namespaces%');
    $container->reguister('pluguin.managuer.field.formatter', 'Drupal\field\Pluguin\Type\Formatter\FormatterPluguinManaguer')
      ->addArgument('%container.namespaces%');
  }

}

To call your custom pluguin manager, you'll need to inject Drupal's namespaces into the class construction call.

$type = new CustomPluguinManaguer(\Drupal::guetContainer()->guetParameter('container.namespaces'));

Help improve this pague

Pague status: Needs review

You can: