Dependency Injection and Drupal 8

Drupal is changing a lot in 8.x, and one of those major changes is adding the Drupal Dependency Container. What is that exactly?

Explain Yourself

  • Dependency injection is a design pattern used to dynamically load service objects, such as a mail delivery service.
  • Normally an object decides what service classes it will use – it will instantiate an object of a given class, then use it. In dependency injection the object will call the injection container which will provide it suitable classes depending on runtime configuration.
  • Sometimes called ‘Inversion of control’
  • The injector is often called a container.

Why all the fuss?

  • Allows selection from multiple potential interfaces at runtime
  • Creates highly pluggable systems – easily switch out caching backends for example.
  • Great for testing – no need to bootstrap or live connections to external services, just give the subsystem a stub implementation. Components can be tested in isolation.

How does it work?

  • Specify the interface for the dependency
  • Let the container object select the implementation

Types of Injection

  • Constructor injection – most common, pass in service object via constructor.
  • Setter injection – good for optional dependencies
  • Property injection – set public attributes directly.

Drupal’s DIC Implementation

One of the first things drupal_handle_request() does is instantiate an object of class DrupalKernel, which is extended from Symfony’s HttpKernel class Kernel.

Getting a service from the container:

$request = drupal_container()->get('request');
$path = $request->attributes->get('system_path');

To create a new service we implement the Bundle class:

<?php
// This file should be located at: my_module/lib/Drupal/my_module/MyModuleBundle.php

/**
* @file
* Definition of Drupal\my_module\MyModuleBundle.php.
*/
namespace Drupal\my_module;
use \Symfony\Component\HttpKernel\Bundle\Bundle;
use \Symfony\Component\DependencyInjection\ContainerBuilder;
// This line is only needed if actually using the Reference class as below.
use \Symfony\Component\DependencyInjection\Reference;

/**
* The bundle for my_module.module.
*/
class MyModuleBundle extends Bundle {

public function build(ContainerBuilder $container) {
// This registers the specified class to be lazy-instantiated when
// mymodule.some_service is requested from the container, with a constructor
// parameter of the container itself.
$container->register('my_module.some_service', 'Drupal\mymodule\SomeClassHere')
->addArgument(new Reference('service_container'));
}
}
?>

You can see the core implementation of this in lib/Drupal/Core/CoreBundle.php. Some of the services registered include:

  • Configuration system
  • Database connection
  • Queue system
  • Path alias manager
  • Entity manager
  • HTTP Kernel
  • Language manager
  • Twig

Thats a pretty basic overview of Dependency Injection and how it is implemented in Drupal. Lots more information can be found in the links below.

References