Dependency Injection

Typically, all of the features of a modern PHP application are handled by objects. Each object (i.e. class) does something very specific, which makes the code maintainable and testable. Several objects can easily be attached to your main application using a programming design pattern called dependency injection.

An example

Suppose you want to implement a registration mechanism for your new fancy website. One class should handle persisting the new user's data (email, username, etc) to the database and a separate class should send a confirmation email after the user has successfully registered. These two services can simply be injected to the client that depends on them.

The code that implements the above setup could look like this:

<?php
class DatabaseManager
{
  public function __construct($argA, $argB, $argC)
  {
    ...
  }

  // Performs checks and creates the new user in the database
  public function createUser()
  {
    ...
  }
}

class EmailManager
{
  public function __construct($argD, $argE, $argF)
  {
    ...
  }

  // Sends emails to newly registered users 
  public function sendEmailTo($user)
  {
    ...
  }
}

class RegistrationManager
{
  private $dbManager;
  private $mailManager;

  public funcion __construct(DatabaseManager $dbManager, EmailManager $mailManager)
  {
    $this->dbManager = $dbManager;
    $this->mailManager = $mailManager;
  }

  public function register()
  {
    // called when user submits the registration form
    $user = $this->dbManager->createUser();
    $this->mailManager->sendEmailTo($user);
  }
}

The best thing about this implementation is that the client (the RegistrationManager) does not have to be aware of the configuration of the two services that are injected to it.

Without using dependency injection the constructor of the RegistrationManager would look like this.

<?php
public funcion __construct($argA, $argB, $argC, $argD, $argE, $argF)
  {
    $this->dbManager = new DatabaseManager($argA, $argB, $argC);
    $this->mailManager = new EmailManager($argD, $argE, $argF);
  }

This is messy and easier to break than the former implementation. Moreover, any slight change to the constructor methods of the two dependencies (DatabaseManager and EmailManager) would mean that we would have to update all instances of RegistrationManager. Bummer, right?

Types of Dependency Injection

The two most commonly used types of dependency injection are listed below.

Constructor injection

All dependencies are provided to the client through its constructor.

<?php
private $depA;
private $depB;
public function __construct(DepA $depA, DepB $depB, ...)
{
    $this->depA = $depA;
    $this->depB = $depB;
}

Setter Injection

A setter method for each dependency is implemented by the client.

<?php
private $depA;

public function __construct()
{

}

public function setDepA(DepA $depA)
{
  $this->depA = $depA;
}

Advantages

  • Any changes in injected services will not affect the client, as long as the injected services' interface remains unchanged. This keeps the code maintainable.
  • Coupling between classes is hugely decreased.
  • Unit testing the code gets super easy, given that you can mock every service that is injected into a client.

Disadvantages

  • Injected services must usually be fetched from a service container and should also be verified before being used.
  • Moving functionality out of the client and into injected services might not always be easily managed.
  • Dependency injection can make it difficult to trace how the application works, because one needs to refer to several files in order to understand what the code does.

Dependency Injection is not only used in PHP frameworks and applications, but there are several modern frameworks that use it efficiently, such as AngularJS. Symfony2, which is used by all services behind codebender, implements a powerful Service Container, which provides instances of all the objects used by the framework. Then the Dependency Injection component makes it super easy to inject services to your Controllers and make your code far more efficient.

Till next time, Inject the venom!