PHP Course : What Is Dependency Injection

profile picture

PHP Course : What Is Dependency Injection

In this first PHP Lesson serie, we will cover what could be one of the first thing that any programmer must understand. It's probably the most basic yet complicated thing about programming, and it will help you greatly to understand how any framework work.

I remember one technical interview I had to pass for a job, and the first question I was asked was "What is dependency injection ?". We will cover several aspects on how to answer this question.

In one sentence

From Wikipedia : "Dependency injection is a technique whereby one object supplies the dependencies of another object"

Dependency Injection is simply passing an argument to a function, or a constructor.

Not to confuse with...

... an IoC Container, or a Dependency Injection Container, or DI Container, which is a tool to help injecting dependencies.

What problem does it solves?

To demonstrate what is dependency injection and what is it for, we first need to write some code that we will try to refactor.

For our little demo, we will use an FileService class. This class will be able to store a file that was uploaded by a user.

  • The FileService Class can rely on DiskFileSystem class to store the file on the disk
  • OR The FileService Class can also rely on S3FileSystem class to store the file on AWS (Cloud Storage)

We can illustrate it like this

Without dependency injection

class FileService
{
    public function store($uploadedFile) {
        $fileSystem = new DiskFileSystem();
        // or $fileSystem = new S3FileSystem();

        return $fileSystem->storeUploadedFile($uploadedFile);
    }
}

Now imagine that, one day, you must use AWS S3 Cloud Storage instead of the Disk System. What do you do? You have to change the code of FileService and all other classes that use "DiskFileSystem", and specify to use the other class instead.

As you can understand, DiskFileSystem is thightly coupled with FileService.

Dependency Injection solves this issue.

With Dependency Injection

class FileService
{
    private $fileSystem;
  
    public function __construct(FileSystemInterface $fileSystem) {
        $this->fileSystem = $fileSystem;
    }
  
    public function store($uploadedFile) {
        return $this->fileSystem->storeUploadedFile($uploadedFile);
    }
}

The FileSystem is injected into FileService.

The filesystems are now defined using an interface:

interface FileSystemInterface
{
    public function storeUploadedFile($uploadedFile);
}

class DiskFileSystem implements FileSystemInterface { ...
class S3FileSystem implements FileSystemInterface { ...

Our FileService class now doesn't know anything about DiskFile and S3 systems. It is no more dependent on those classes

Inversion Of Control

What we just demonstrate is called Inversion of Control.

The control of the dependencies is inverted from one being called to the one calling.

Uh.. what?! Well instead of using new DiskFileSystem you will simply use a $filesystem property, that your controller doesn't need to worry about.

It allows you to replace a dependency by another.

In other words, instead of putting your Controller (business domain) in charge of the dependencies, it's the application configuration that handles the dependencies.

For example what if Library X uses Logger Y and you want to make it use your logger Z? With dependency injection, you don't have to change the code of Library X.

Clarification : Dependency Inversion

The code sample we just wrote with the FileSystem is actually an implementation of the "Dependency Inversion Principle" which is different from Dependency Injection

Dependendy Injection is simply passing the dependency class in the class constructor.

And Dependency Inversion is the Interface solution where what you put in the class Constructor is actually an Interface.

Does that mean I can inject any class in the constructor?

Yes! Dependency Injection is simply passing an argument to a function, or a constructor. At this point in the PHP theory, we don't speak about Inversion of Control yet.

So, transferring the task of creating the object to someone else and directly using the dependency is called dependency injection.

Speaking of which, you can also use Dependency Injection outside the constructor:

class Profile {
    private $setting;
    public function setSetting(Setting $setting)
    {
        $this->setting = $setting;
    }
}

This is called a setter injection and it is very useful to write tests.

Conclusion

Dependency Injection is actually a very simple thing. What's more complicated to understand is Inversion of Control, and Dependency Inversion, which I quickly demonstrated with the FileSystem code sample.

If you'd have to remember one thing about Dependency Injection, it's this example:

BAD

class FileService {
    public function store($uploadedFile)
    {
        $fileSystem = new DiskFileSystem();

        return $fileSystem->storeUploadedFile($uploadedFile);
    }
}

GOOD

class FileService {
    private $fileSystem;
  
    public function setFileSystem(FileSystem $fileSystem) // or __constructor()
    {
        $this->fileSystem = $fileSystem;
    }
  
    public function store($uploadedFile)
    {
        return $this->fileSystem->storeUploadedFile($uploadedFile);
    }
}

Let someone else worry about creating the class instance.

In the next lesson we will have a look at DI Container and how it works. How does injecting an interface gives me a class? We will cover that in the next lesson!

php
php course
dependency injection
DI

2019 My Dynamic Production SPRL All rights Reserved.