Understanding Dependency Injection in PHP

Dependency Injection (DI) is a design pattern used in software development to achieve Inversion of Control (IoC) between classes and their dependencies. In simpler terms, it’s a way to provide an object with all the things it needs (its dependencies) from the outside rather than creating them itself.

This pattern helps in writing cleaner, more maintainable, and testable code.

What Is Dependency Injection?

Imagine a class that needs another class to function. Traditionally, it might create that dependency itself:

class Mailer {
    public function send(\$message) {
        // sending email logic
    }
}

class UserController {
    private \$mailer;

    public function __construct() {
        \$this->mailer = new Mailer();
    }

    public function notifyUser() {
        \$this->mailer->send("Hello User!");
    }
}

In this example, UserController is tightly coupled to Mailer. If you ever want to change Mailer or replace it with a different implementation (e.g., for testing), you’ll have to modify the class.

Dependency Injection solves this by “injecting” the dependency into the class instead of letting it create it:

class UserController {
    private \$mailer;

    public function __construct(Mailer \$mailer) {
        \$this->mailer = \$mailer;
    }

    public function notifyUser() {
        \$this->mailer->send("Hello User!");
    }
}

Now, Mailer is passed into UserController from the outside. This makes the code more flexible and easier to test.

Types of Dependency Injection

There are three common types of Dependency Injection:

  1. Constructor Injection – Dependencies are passed via the class constructor (as shown above).
  2. Setter Injection – Dependencies are set via public setters.
  3. Interface Injection – The dependency provides an injector method that will inject the dependency.

Example: Setter Injection

class UserController {
    private \$mailer;

    public function setMailer(Mailer \$mailer) {
        \$this->mailer = \$mailer;
    }

    public function notifyUser() {
        \$this->mailer->send("Hello User!");
    }
}

Benefits of Dependency Injection

  • Decoupling – Classes do not depend on specific implementations.
  • Easier Testing – You can inject mock dependencies.
  • Reusability – Components can be reused in different contexts.
  • Maintainability – Easier to change dependencies without rewriting classes.

Using Dependency Injection Containers

Manually managing dependencies can become complex in larger applications. That’s where Dependency Injection Containers (DICs) come in.

A DIC is a tool that automatically instantiates and injects dependencies.

Example with PHP-DI

require 'vendor/autoload.php';

use DI\Container;

\$container = new Container();

\$controller = \$container->get(UserController::class);