Dependency injection
How Drupal classes get services such as the current user: declared in services.yml or a create() method and passed to the constructor.
Dependency injection means a class is handed the objects it needs instead of building or looking them up itself. In Drupal the objects are services, such as the database connection or the current user, and the service container supplies them when it creates the class. The container comes from the Symfony DependencyInjection component.
How dependency injection works in Drupal 11
A service is defined in a MODULE.services.yml file. The entry gives the service a machine name, a class and a list of arguments. Each argument written as @service_name is another service that the container passes to the constructor. Core's own definitions live in core.services.yml. A module can also define a different class for an existing service name.
Some classes are not services. When a controller, form or plugin class has a static create() method from the matching interface, Drupal calls it with the container to build the object. ContainerInjectionInterface::create() should pass the needed services to the constructor and not the container itself.
The container can also work out constructor arguments from type hints. This is autowiring, and it can be switched on per service with autowire: true. A type-hinted interface needs an alias that points to a service. Core registers such aliases, for example Drupal\Core\Session\AccountInterface for current_user. The AutowireTrait gives a class a create() method that autowires. ControllerBase uses it, and FormBase does since Drupal 11.4. The trait's documentation warns that it uses reflection and can be slow for classes created many times. Classes in a module's Hook namespace are registered as autowired services, as explained under hooks.
Example
The service definition lists what the class needs:
services:
my_module.greeter:
class: Drupal\my_module\Greeter
arguments: ['@current_user', '@datetime.time']
The constructor receives those two services in the same order:
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Session\AccountInterface;
class Greeter {
public function __construct(
protected AccountInterface $currentUser,
protected TimeInterface $time,
) {}
}
Common mistakes
- Passing the whole container to the constructor. The interface documentation asks for the specific services only. Passing the container hides what the class depends on.
- Creating a service with
new. Core's container topic says to always get a service through the container, so that a class another module swapped in is the one you get. - Getting the order wrong. The container passes the arguments in the order of the
argumentslist, so the constructor must match it.
For marketing and content teams
You never edit this directly, but the term explains why a developer can swap a behavior on your site without changing every class that uses it. A custom module can replace a core service class, and every part of the site that asks for that service gets the new one. When someone says a feature "is a service", they mean it sits in this container.
Sources
- Services and Dependency Injection Container topic, Drupal API
- ContainerInjectionInterface, Drupal API
- Drupal.org: Services and dependency injection in Drupal
- Symfony: Defining services dependencies automatically (autowiring)
- AutowireTrait, Drupal 11.4.10 source
- FormBase, Drupal 11.4.10 source
- core.services.yml, Drupal 11.4.10 source
- HookCollectorPass, Drupal 11.4.10 source
- Hook attribute class, Drupal API