Dependency injection is a software design pattern that implements the inversion of a control concept for resolving dependencies. According to this concept, a class should not configure its dependencies statically, but should be configured from the outside.
A dependency is an object that can be used (a service) and an injection is the passing of a dependency to a dependent object (a client) that would use it. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
This fundamental requirement means that using values (services) produced within the class from new or static methods is prohibited. The class should accept values passed in from outside. As you will see in the sections of this article, there are three common means for a client to accept a dependency injection: constructor, setter and interface based injection. The most commonly-used injection method is the constructor injection, but the setter and constructor injection differ principally by the time that the can be used. Interface injection differs by the fact that the dependency controls its own injection.
All these methods require that the separate construction code (the injector) take responsibility for introducing a client and its dependencies to each other. The injection mechanism is similar to parameter passing mechanism and it refers at controlling the passing (never the client) and is independent of how the passing is accomplished, whether by passing a reference or a value. Any object that may be used can be considered a service. Any object that uses other objects can be considered a client.
As for a simple example of concrete dependency are the next lines of code:
<?php
class A
{
public function f_a() {
$b = new B();
$b->f_b();
}
}
?>
<?php
class B
{
public function f_b() {
$c = new C();
$c->f_c();
}
}
?>
<?php
class C
{
public function f_c();
}
?>
You will notice that class A depends on class B, that depends on class C. Moreover, every time we hard code the creation of an object inside a class, we are making a concrete dependency to that class. Concrete dependencies are a barrier to writing testable code. A better way is to provide an object of class B to class A. These objects can be provided through A’s constructor or a setter method.
class Car {
// property declaration
private $maximumSpeed = 200;
// method declaration
public function getMaximumSpeed() {
return $this->maximumSpeed;
}
}
$myCar = new Car();
echo $myCar->getMaximumSpeed();
class Key {
private $car;
public function __construct(Car $car) {
$this->car = $car;
}
public function TurnKey() {
if (/*condition */) {
$this->Car->turnOn();
}
}
}
Note: In the old days, it would have been done in such a manner, but dependency injection is precisely about not doing like this:
Types of Injection
There are many types of injection, as you will see next in this article with some simple examples:
Constructor
Constructor injection is certainly the most common method used and there are many advantages in using this type of injection:
• The dependency can’t be changed or modified during the object lifetime, considering that the constructor is called only when instantiating our object
• By using constructor injection is an assurance that the required dependencies are present, in case that the dependency is required by the class and cannot work without it.
Next, is a simple example of using the constructor injection:
class Key implements ModelCar{}
class Car
{
private $modelCar;
public function __construct(ModelCar $ modelCar)
{
$this-> modelCar = $ modelCar;
}
}
$key = new Key('Citroen C4');
$car = new Car($key);
Setter Injection
Another common type of dependency injection is called setter injection and the same code as above would look something like this:
class ModelCar implements Color{}
class Car
{
private $color;
public function setColor(Color $color)
{
$this->color = $color;
}
}
$color = new ModelCar('Citroen C4');
$car = new Car();
$car->setColor($color);
As we can see, with Setter Injection the dependencies are provided to our class after it has been instantiated using setter methods. Setter Injection is more suitable when you need more flexibility and has the following advantages:
• Allows for optional dependencies and the class can be created with default values; If you do not need the dependency, then just do not call the setter.
• Adding new dependencies is an easy job, all you need to do is adding a new setter method that will not break any existing code.
• You can also call the setter method multiple times.
Property
Another way to use dependency injection is by setting public fields of the class directly, but there are some disadvantages, because some problems may occur:
• You cannot be sure what dependency is injected, except by writing into the class code to explicitly test the class instance before using it.
• You cannot control when the dependency is set at all, it can be changed at any point in the object’s lifetime.
But, it is useful to know that this can be done using the service container, mainly if you are working with code that you cannot control, such as in a third-party library that uses public properties for its dependencies.
A simple example of using this type of injection is listed below:
class Car
{
public $trailer;
}
$car = new Car();
$car->trailer = new Trailer();
Interface
Interface injection differs from the other differs through the fact that the dependency can control its own injection. All that is needed is to separate the construction code (the injector) to take responsibility for introducing a client and its dependencies to each other. The advantage of interface injection is that dependencies can be completely ignorant of their clients yet can still receive a reference to a new client and, using it, send a reference-to-self back to the client. In this way, the dependencies become injectors. The key is that the injecting method (that could just be a classic setter method) is provided through an interface.
Next, is a simple PHP example that uses interface injection mechanism:
class Car{
protected $carModel;
function _construct(CarModelInterface){
$this-> carModel =$ carModel;
}
interface CarModelInterface {
function get($key);
function set($key,$value);
}
Reflection
Reflection is the ability to introspect and reverse engineer functions, classes, methods and interfaces during runtime. This makes it possible to find specific information about your code, such as a classes internal properties, methods & even doc blocks for those methods. In the below code lines, is shown a simple example of dependency injection, that uses the reflection mechanism:
class Car
{
private $attachTrailer;
}
$car = new Car();
$trailer = new ReflectionClass($car);
$attachTrailer = $trailer->getProperty('attachTrailer');
$attachTrailer ->setAccessible(true);
$attachTrailer ->setValue($car, new AttachTrailer());
So, as we have seen so far, there are some useful methods to implement dependency injection in PHP, so we can conclude the main aspects of dependency injection in a list of advantages, that is listed in the following lines:
Advantages of using dependency injection
As a completion of what was presented so far in this article, we can deduct some of the main advantages of implementing dependency injection in our application: a clean code that is easier to understand, improve testability, reusability of the code, using the dependency inversion principle, decoupling dependencies, separation of concerns.
As a main disadvantage of dependency injection is:
Complex initialization of dependency injection
A proper solution for resolving the above disadvantage is using a dependency injection container. The next section of this article presents more dependency injection containers used in working with PHP and some examples of using these:
Dependency Injection Container
Dependency Injection Container is a PHP object that handles the instantiation of other objects, in other words, a dependency injection controls the process of object creation; instantiating and injecting any dependencies before returning an instance to the caller.
In the next section of this article, will be presented some of the most-used PHP dependency injection containers (or frameworks):
Pimple: A simple dependency injection container that takes advantage PHP’s closures to define dependencies in manageable way. Here it is a simple example of using this container:
<?php
// create an instance of Pimple to act as a container for storing dependencies
$container = new Pimple();
// define a key that holds the class name
$container['className'] = 'Car';
// define a closure to return the instance of the specified class that acts as a service
$container['objectName'] = function ($car) {
return new $car['className']();
};
$example = $container['objectName'];
The $car parameter will be passed as instance of the container, so we can reference other defined keys as we want; each defined parameter or object is available in the closure through the $car variable. Now every time we want an instance of the class, we can reference the key to retrieve the object.
Dice: Another simple dependency injection container used in PHP is Dice, a single 100-line class, that allows developers to move object creation logic out of their application logic. Dice has been designed to minimize and simplify the application code, making the application developers job easier.
Orno: A fast and powerful dependency injection container that allows for automatic resolution of dependencies. OrnoDi is a small but powerful dependency injection container that allows you to decouple components in your application in order to write clean and testable code. The container can automatically resolve dependencies of objects resolved through it.
Aura DI: A dependency injection container that provides a dependency injection container system that has as main features: the native support for constructor- and setter-based injection, inheritable configuration of setters and constructor params, lazy-loading of services. When combined with factory classes, you can completely separate object configuration, object construction, and object usage, allowing for great flexibility and increased testability.
PHP-DI: Popular Dependency Injection Container for PHP that intends to be practical and powerful.
ZendDI: The Dependency Injection Container provided by Zend Framework and its main job is to provide you instances of a requested object populated with all the dependencies required for it to work correctly.
Symfony2: Is the Dependency Injection Container provided by the Symfony framework. Symfony2 uses a very solid DI component that is based on Java Spring. Dependency injection containers (or frameworks) handle the process of object creation; instantiating and injecting any dependencies before returning an instance to the caller.
The Symfony Service Container is available as a standalone component in the Symfony official Subversion repository: http://svn.symfony-project.com/components/dependency_injection/trunk/ and one of the main goals of Symfony Dependency Injection is to be as fast as possible. The Dependency Injection Container in Symfony is managed by a class namedsfServiceContainer, that is a very lightweight class.
Conclusion
In this article, you learned about Dependency Injection and the main PHP containers/frameworks used in working with PHP and we can conclude that the Dependency Injection can make the life of a developer much easier.