PHP Dependency Injection in detail

Spread the love

PHP Dependency Injection

What is Dependency Injection in PHP?

Before looking into what is PHP Dependency Injection lets see what is a dependency.

In previous chapters inheritance, abstract classes and interface we have seen the concept of code reusability. We can use code from one class into another through inheritance.

Similarly, you can reuse code of, let’s say first class into a second class by using the first class object or more specifically a component of the first class. Simply means you can create a first class’ object into the second class.

In this case, the second class becomes dependent on the first class.

This is called as a dependency of classes.

But this type of dependency will be problematic as the first class becomes tightly coupled to second or second class becomes dependent on the first one.

PHP Dependency Injection a solution to tightly coupled dependency

To overcome this problem a design pattern called dependency injection is introduced.

In this design pattern, the object of one class is not directly created into dependent class, instead, the dependency is passed externally using the constructor, setters or simple methods.

Let’s see the concept of PHP Dependency Injection in detail.

Let’s create a FileLogger utility class, which logs (writes) every activity of program execution into a file.

<?php
 class FileLogger
 {
  public function log($message)
  {
   echo "Operation logged in File: ".$message."<br>";
  }
 }
 
 class User
 {
  private $logger;
  
  public function __construct()
  {
   $this->logger = new FileLogger();
  }
  public function createUser()
  {
   $this->logger->log("User created");
  }
  
  public function deleteUser()
  {
   $this->logger->log("User deleted");
  }
 }
 
 $user = new User();
 $user->createUser();
 $user->deleteUser();
?>

As you see in above program FileLogger class object is created into User class constructor and assigned to $logger variable. This is nothing but the dependency of User class into $logger object of FileLogger.

Using $logger we have called the log() method to log the operation performed in createUser and deleteUser methods.

In this case User class is tightly coupled to FileLogger class and is strictly dependent only on FileLogger class for a logging operation.

Now imagine, like User class you have many other classes like 100s of classes, where you are using the same FileLogger class for logging the operations. In every class constructor, you are creating a FileLogger object and calling log method to use.

So here also every class will become strictly dependent only on FileLogger class.

Now suppose after some time you want to log your operations into a database using DatabaseLogger or emails using EmailLogger object then?

Then, you have to go to each and every class which has created an object directly into their constructor and have to change them with new logger class.

Hence this type of implementation will not be feasible and become difficult to maintain.

To overcome this problem we will send dependency object $logger of FileLogger class from outside of User class, i.e. injecting the dependency to the User class.

Let’s see the modifications.

<?php
 class FileLogger
 {
  public function log($message)
  {
   echo "Operation logged in File: ".$message."<br>";
  }
 }
 
 class User
 {
  private $logger;
  
  public function __construct(FileLogger $fileLogger)
  {
   $this->logger = $fileLogger;
  }
  public function createUser()
  {
   $this->logger->log("User created");
  }
  
  public function deleteUser()
  {
   $this->logger->log("User deleted");
  }
 }
 
 $fileLogger = new FileLogger();
 $user = new User($fileLogger);
 $user->createUser();
 $user->deleteUser();
?>

Now we have sent the FileLogger object to the constructor of User class and collected using type hinting in the User constructor.

Type hinting is nothing but specifying the type of an object.

Here in the constructor we have specified the type of $fileLogger by adding class name i.e. FileLogger before it – “public function __construct(FileLogger $fileLogger)”.

Why we used type hinting in this example?

Because if we don’t use type hinting in the constructor then you can pass any other object also, whose functionality is not logging.

In that case, your code may behave inappropriately as you only want to have logger object in a constructor.

Hence type hinting will strictly check whether the object passed in the constructor is of logger class or not.

Now in the above program, the FileLogger object becomes loosely coupled to the User class and User class is not strictly dependent.

But still, one problem is still present. Though we have sent the FileObject from outside still if we need to change from FileLogger to any other logger class like DatabaseLogger or EmailLogger then?

Then in this type of program also we have to go to all the classes and change the type hinting. Here the problem still remains due to type hinting.

We can overcome this second problem also, using the wonderful “interface” and polymorphism concept.

Now let’s see the detailed program with Logger interface and creating and using one more class DatabaseLogger.

<?php
 
 interface Logger
 {
  public function log($message);
 }
 
 class FileLogger implements Logger
 {
  public function log($message)
  {
   echo "Operation logged in File: ".$message."<br>";
  }
 }
 
 class DatabaseLogger implements Logger
 {
  public function log($message)
  {
   echo "Operation logged in Database: ".$message."<br>";
  }
 }
 
 class User
 {
  private $logger;
  
  public function __construct(Logger $fileLogger)
  {
   $this->logger = $fileLogger;
  }
  public function createUser()
  {
   $this->logger->log("User created");
  }
  
  public function deleteUser()
  {
   $this->logger->log("User deleted");
  }
 }
 
 $logger = new FileLogger();
 $user = new User($logger);
 $user->createUser();
 $user->deleteUser();
 
 echo "<br>";
 
 $logger = new DatabaseLogger();
 $user = new User($logger);
 $user->createUser();
 $user->deleteUser();
?>



Output:


Operation logged in File: User created
Operation logged in File: User deleted

Operation logged in Database: User created
Operation logged in Database: User deleted

In this program we have created:

An interface name Logger with one method declaration public function log($message)  with a single parameter $meaasge and a DatabaseLogger class.

The logger interface is then implemented into the FileLogger and Database logger class with log() method definition implemented.

Next, we have changed the type hinting in the User constructor from “FileLogger” class to “Logger” interface.

Now, as the Logger interface is implemented by both FileLogger and DatabaseLogger classes, the constructor argument with Logger type hinting can able to catch the object of both of these classes.

Due to this flexibility now you can use and change any logger class as per your requirement in your dependent classes and can call the same log method due to polymorphism.

Now if there are many classes where the logger class needs to be changed then you need not change code inside those classes, just change the logger object outside and then send it to those classes.

In this way, you can send or inject the dependency as per your requirements.

Inversion of control

As you see in this program the User class is not responsible to create an object of logger classes in its constructor, but the logger class object is created by you outside of a User class and User class receives it as per its requirement.

In this process, the dependent class is not controlling the creation of dependency but it gets inverted and provided to the other sources outside the dependent class, in this case, you, the person creating and sending an object.

Such a process will be called as an inversion of control. Inversion of control walks together with the PHP Dependency Injection concept.

So the PHP Dependency Injection is a most useful design pattern you can use in your object oriented PHP projects.

In the next tutorial, you will learn about the Polymorphism in detail.


Spread the love

1 thought on “PHP Dependency Injection in detail”

  1. Wonderful explaination. I got full understanding of the dependency injection concept from this single tutorial. Thank you so much. Keep posting such tutorials more

    Reply

Leave a Comment