Plugin API
In Drupal's Plugin API, a manager service discovers classes, caches their definitions and creates them on request, so modules add interchangeable options.
The Plugin API is Drupal's system for swappable, object-oriented functionality. A module defines a plugin type with an interface and a plugin manager, and other modules add plugins, which are classes that implement the interface. Blocks, field types, field widgets, field formatters, image effects and render elements are all plugin types.
How the Plugin API works in Drupal 11
Core's Plugin API topic names the moving parts. A plugin type is usually defined by an interface. A plugin manager is the service that finds plugins of that type with a discovery method and creates them with a factory. Discovery can read annotations or attributes, call a hook, read YAML files or use a static list. Some types add derivatives, which let one class present itself as several plugins, such as one block per menu.
Attribute discovery arrived in Drupal 10.2.0. A plugin class carries an attribute such as #[Block(id: ...)] and sits in a fixed namespace subdirectory. The block manager, for example, looks in Plugin/Block inside each module's namespace.
Core's topic notes that most plugin managers extend DefaultPluginManager. Its constructor takes the subdirectory, the namespaces, the module handler, the plugin interface and the attribute class. A manager can name an alter hook so other modules can change the discovered definitions; the block manager's hook is hook_block_alter(). Definitions are cached, so a new plugin class does not appear until the plugin caches are cleared.
To create an instance, call createInstance() on the manager. The factory checks whether the class implements ContainerFactoryPluginInterface. If it does, the factory calls the class's static create() method with the container, so the plugin can receive services. If it does not, the factory calls the constructor with the configuration, plugin ID and definition.
Example
This block plugin lives in my_module/src/Plugin/Block/HelloBlock.php:
<?php
namespace Drupal\my_module\Plugin\Block;
use Drupal\Core\Block\Attribute\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\StringTranslation\TranslatableMarkup;
#[Block(
id: 'my_module_hello',
admin_label: new TranslatableMarkup('Hello block'),
)]
class HelloBlock extends BlockBase {
public function build() {
return ['#markup' => $this->t('Hello from a plugin.')];
}
}
After a cache rebuild, the block appears in the block list under its admin label, and you can place it on Structure › Block layout.
Common mistakes
- Wrong directory or namespace. Core's topic says a plugin must be in the right plugin namespace to be discovered. A block class outside
Plugin/Blockis never found. - Forgetting the cache rebuild. The manager serves cached definitions. A full cache rebuild clears the plugin caches.
- Injecting services in the constructor without a
create()method. The default factory only passes the configuration, plugin ID and definition, so the extra constructor arguments are missing. - Mixing up the discovery style. Each plugin type's manager decides whether it reads attributes or annotations, so copy the style of an existing plugin of the same type.
For marketing and content teams
In Drupal a plugin is not an add-on you install. It is a unit of code inside a module. You still choose plugins in the interface without knowing it: the "Powered by Drupal" entry in the block list is a block plugin, and the widget you select for a field is a field widget plugin. When a developer says a feature needs "a new plugin", they mean new code in a custom module, which shows up as a new option in a list.
Sources
- Drupal API: Plugin API topic (core.api.php, 11.2.0)
- DefaultPluginManager, Drupal 11.2.0 source
- ContainerFactory, Drupal 11.2.0 source
- BlockManager, Drupal 11.2.0 source
- SystemPoweredByBlock, Drupal 11.2.0 source
- Drupal.org: Plugin API
- Drupal change record: Plugin types should use PHP attributes instead of annotations