Michael StrelanSenior Developer

Way back in 2017, Lee “larowlan” Rowlands penned what would become one of the most popular blog posts on this site. I’m of course talking about “Safely extending Drupal 8 plugin classes without fear of constructor changes”.
This well-read article, inspired by Thomas Seidl of Search API fame, has become somewhat of a reference piece, frequently cited in:
As of Drupal 11.4, there is a new approach using attributes that simplifies this further. So, I thought it would be fun to piggyback off Lee's success prudent to update the original post so modern developers (both human and robots) are in the loop.
But first, a quick recap of how dependency injection has evolved in Symfony and Drupal over the years. You can skip to the end if you’re not interested.
In Drupal 9.3 (December 2021), support for autowiring services was added to core. This allows us to remove arguments from services.yml. Symfony will automatically inject dependencies as long as the constructor parameter typehint matches a service name or alias.
Two years later, in Drupal 10.2 (December 2023), the AutowireTrait was introduced. This allows any class implementing ContainerInjectionInterface to drop the ::create() method, and the trait will autowire the dependencies for you.
Unfortunately, that didn’t work with Plugin classes, because the ::create() method has some additional parameters to deal with. It wasn’t until Drupal 11.3 (December 2025)that the same treatment was added to PluginBase, allowing plugins to drop the ::create() method as well.
Of course, none of this really helps us with the original problem - dealing with constructor changes in the parent class. Our previous solution relied on overriding the ::create() method and setting additional services as properties. But the improvements are all about removing the ::create() method.
So how should we add dependencies in 2026, I hear you ask?
Once again, Symfony provides the solution; we just need to glue it together.
Up until now, we’ve only used autowiring for constructor injection, but it’s also possible in Symfony to autowire a method by applying the #[Required] attribute. This tells Symfony the method must be called when the class is instantiated, and any parameters will be autowired to the matching service. That looks something like this:
use Symfony\Contracts\Service\Attribute\Required;
class ExamplePlugin extends ExamplePluginBase {
protected FooMeddlerInterface $fooMeddler;
#[Required]
public function setFooMeddler(FooMeddlerInterface $fooMeddler): void {
$this->fooMeddler = $fooMeddler;
}
}We’re adding a typed property with a setter method, and we don’t need to touch the constructor or create method. The service will automatically be injected when the plugin is instantiated. Crucially, this means constructor changes in the parent class no longer affect us.
Support for this was added in Drupal 11.4 and applies to plugins extending \Drupal\Core\Plugin\PluginBase and controllers extending \Drupal\Core\Controller\ControllerBase.
Credit to @longwave and @godotislate (Drupal’s newest release manager) for working on this issue.
Looking for the latest? Check out the 2026 edition.
From time to time you may find you need to extend another module's plugins to add new functionality.
You may also find you need to alter the signature of the constructor in order to inject additional dependencies.
However plugin constructors are considered internal in Drupal's BC policy.
So how do you safely do this without introducing the risk of breakage if things change.
In this article we'll show you a quick trick learned from Search API module to avoid this issue.

VIDEO: In his DrupalCon Barcelona session, Mohit explored how Symfony Console commands can improve your Drupal workflow, offering enhanced flexibility and efficiency.
The Drupal Association has published client guides to RFPs that prioritise open source software solutions.