Skip to content

Essential APIs - Plugin API

From Plugin API Overview on Drupal.org updated Mar 2021:

Plugins are small pieces of functionality that are swappable. Plugins that perform similar functionality are of the same plugin type.

Drupal contains many different plugins, of different types. For example, 'Field widget' is a plugin type, and each different field widget type is a plugin. The admin user may select from the list of field widget plugins to set the widget that a field uses.

The Drupal plugin system provides a set of guidelines and reusable code components to allow developers to expose pluggable components within their code and (as needed) support managing these components through the user interface.

Plugins are defined by modules: a module may provide plugins of different types, and different modules may provide their own plugins of a particular type.

Plugin Elements

  • Plugin Types are the controlling class defining discovery and instantiation of plugins of that type. A plugin type should describe the central purpose of all plugins of that type, e.g., cache backends, image actions, blocks, etc.

  • Plugin Discovery is the process of finding plugins within the available code base that qualify for use within this particular plugin type's use case.

  • Plugin Factory is responsible for instantiating the specific plugin(s) chosen for a given use case.

Additionally, the plugin system includes several situationally useful components:

  • Plugin Derivatives allow a single plugin to act in place of many. This is useful for situations where user entered data might have an impact on available plugins. For example, if menus are placed on screen using a plugin, then when the site administrator creates a new menu, that menu must be available for placement without needing a new plugin to do so. Plugin Derivatives also support the user interface by allowing it to display multiple plugins in place of one, allowing for help text specific to the use case to be rendered and utilized. The primary purpose of plugin derivatives is to provide partially configured plugins as "first class" plugins that are indistinguishable in the UI from other plugins, thus reducing the burden on administrators using these plugins.

  • Discovery Decorators are another available discovery method meant to wrap an existing discovery method. Core currently supplies the cacheDecorator which will cache the discovery process that is chosen for a plugin type. This pattern could be expanded to other use cases if necessary.

  • Plugin Mappers allow you to map something (most often a string) to a specific plugin instance. Plugin types which use this approach can return fully configured and instantiated plugins based upon arbitrarily definable names instead of requiring developers using this api to manually instantiate and configure a plugin instance.

Plugin discovery types, from Plugin Discovery on Drupal.org updated Jan 2024:

  1. StaticDiscovery allows for direct registration of plugins within the discovery class itself. A protected variable ($definitions) in the class holds all plugin definitions that are registered with it through the public method setDefinition(). Any plugin defined through this method can then be invoked as outlined in the plugin manager documentation.

  2. HookDiscovery allows Drupal's hook_component_info()/hook_component_info_alter() pattern to be used for plugin discovery. With this discovery, the plugin manager will invoke info hooks to retrieve a list of available plugins.

  3. AnnotatedClassDiscovery uses name of the annotations that contains the plugin definition, e.g., @Plugin, @EntityType, in plugin docblocks to discover plugins, minimizing memory usage during the discovery phase. The AnnotatedClassDiscovery class takes an argument in its constructor, $subdir, which specifies the subdirectory/sub-namespace for this plugin type. The AnnotatedClassDiscovery class scans PSR-4 classes inside those subdirectories of Plugin folders to find plugins (see a "Annotations-based plugins").

  4. YamlDiscovery allows plugins to be defined in yaml files. Drupal core uses this for local tasks and local actions.

  5. AttributeClassDiscovery class allows plugins to be defined using [PHP attributes](https://www.php.net/manual/en/language.attributes.overview.php. See [Attribute-based plugins on Drupal.org updated Oct 2024](https://www.drupal.org/docs/drupal-apis/plugin-api/attribute-based-plugins.

Annotations based Plugins

Assume you are adding a Constraint plugin (see detailed example here - Annotatins-based plugins on Drupal.org updated Aug 2024)

Register your plugin by placing it in a file relative to your module root, (e.g. src/Plugin/Constraint/UserNameUnique.php).

php
<?php
/**
 * @Constraint(
 *   id = "UserNameUnique",
 *   label = @Translation("User name unique", context = "Validation"),
 * )
 */
class UserNameUnique extends Constraint {
// ...
}
?>

Note

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

php
<? php

namespace Drupal\custom_module\Plugin\Block;

use Drupal\Core\Block\Attribute\Block;
use Drupal\Core\StringTranslation\TranslatableMarkup;

/**
 * Provides a custom block plugin.
 */
#[Block(
  id: "custom_block",
  admin_label: new TranslatableMarkup("Custom Block"),
  category: new TranslatableMarkup("Custom Category"),
)]
class CustomBlock extends BlockBase {
  // Plugin implementation.
}

Also Note

Unlike other PHP coding standards, annotations should always use double quotes instead of single quotes.*

Resources

Questions

How do you define a plugin using annotations, and what are the key components of an annotations-based plugin?

For Drupal 10.2 and newer, you can use PHP attributes instead of annotations:

php
#[Block(
  id: "custom_block",
  admin_label: new TranslatableMarkup("Custom Block"),
  category: new TranslatableMarkup("Custom Category"),
)]
class CustomBlock extends BlockBase {
  // Plugin implementation.
}

This defines a custom block plugin using the PHP attribute syntax.

What are the five plugin discovery methods, and how do they differ in how plugins are registered and discovered?

The five plugin discovery methods in Drupal are:

  1. StaticDiscovery: Plugins are directly registered within the discovery class itself. The class holds a protected variable ($definitions) where plugins are defined using the setDefinition() method. This approach allows plugins to be registered in a static manner.

  2. HookDiscovery: This method uses Drupal's hook_component_info()/hook_component_info_alter() to discover plugins. The plugin manager calls these hooks to retrieve a list of available plugins, allowing for a more dynamic plugin discovery process based on the code that implements these hooks.

  3. AnnotatedClassDiscovery: Plugins are discovered through annotations in the plugin's PHP docblock, such as @Plugin. This method scans classes in specified subdirectories, making it memory efficient as plugins are discovered only when needed. It's used for plugins where annotation-based definitions are employed.

  4. YamlDiscovery: This method allows plugins to be defined in YAML files. Drupal core uses this for defining local tasks and actions. It provides a configuration-based approach where plugins are declared in structured YAML files, which are then read and processed by the plugin discovery system.

  5. AttributeClassDiscovery: This method allows plugins to be defined using PHP attributes. Introduced in Drupal 10, attributes offer a more modern approach to defining plugins and can be used as an alternative to annotations. This method scans the plugin classes to find the PHP attributes that define the plugin metadata.

Each discovery method provides a different way of registering and discovering plugins, ranging from static definitions to dynamic scanning through annotations, YAML, or PHP attributes.

Which quotes should we use when defining a plugin?

Double quotes. For both Annotation and Attribute-based plugins.