PHP Dependency Injection Tutorial + 3 Code Examples 2023

Last Updated on

CraftyTechie is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

Dependency Injection PHP Tutorial

Dependency injection is a popular design pattern used in modern programming languages to promote code reusability, maintainability, and testability. It allows developers to manage object dependencies and decouples classes from their dependencies, making them more flexible and easier to manage.

This article is part of our larger series on using Object-Oriented Programming (OOP) in PHP. Click here to start from the beginning.

 In this article, we will discuss what dependency injection is, how it can be used in PHP, and the different types of dependency injection available.

Dependency Injection PHP Code Example

<?php


require 'vendor/autoload.php';
use DI\ContainerBuilder;


// Instantiate container builder
$builder = new ContainerBuilder();


// Configure container
$builder->addDefinitions([
    UserController::class => function ($container) {
        return new UserController(
            $container->get(UserService::class)
        );
    },
    UserService::class => function ($container) {
        return new UserService();
    },
 
]);


// Build container
$container = $builder->build();




class UserService {




    public function createUser(array $data) {
        // validate user data
        $name = $data['name'];
        $email = $data['email'];
        $password = $data['password'];
           
        echo "User {$name} has been created";
         
        return `{name: {$name}, email: {$email}}`;


    }
}




class UserController {
    private $userService;


    public function __construct(UserService $userService) {
        $this->userService = $userService;
    }


    public function createUser(Request $request) {
        $data = $request->getParsedBody();
        $user = $this->userService->createUser($data);
        return json_encode($user);
    }
}


?>

Article Highlights

  • Dependency injection is a technique for managing the dependencies between different components of an application.
  • Dependency injection is closely related to software design’s SOLID principles, particularly the dependency inversion principle.
  • There are three main types of dependency injection: constructor injection, setter injection, and interface injection.
  • Constructor injection is the most commonly used dependency injection, providing an unambiguous way to inject dependencies into a class.
  • Dependency injection can be used in PHP, with popular libraries such as PHP-DI providing powerful tools for managing dependencies.

Table of Contents

What is Dependency Injection?

Dependency injection (DI) is a technique used in object-oriented programming that allows classes to depend on interfaces or abstract classes rather than concrete implementations. In other words, instead of creating an object inside a class, the class is given the object from outside. The process of providing an object to a class is called dependency injection.

Sounds confusing? Let’s understand this idea with a real-world analogy.

Real-world analogy

A real-world analogy of dependency injection is a chef cooking a meal. When a chef prepares a meal, they must use different ingredients like vegetables, meats, and spices. Instead of the chef sourcing all the ingredients themselves, the ingredients are provided to them by a supplier. The chef can then focus on preparing the meal without worrying about finding and sourcing the ingredients themselves.

php dependency injection

Similarly, in software development, a class or a method may need to use different dependencies like database connections, web services, or third-party libraries. Instead of the class or method creating and managing these dependencies themselves, they can be provided to them by a dependency injection container

This makes the code more modular and easier to maintain, as each class or method is responsible for a specific task, and can focus on that task without worrying about the dependencies it needs to function.

Just like how a chef can use the same ingredients to prepare different meals, a class or method can use the same dependencies to perform different tasks. 

Separating the dependencies from the implementation makes the code more flexible and easier to test.

Dependency Injection & SOLID Principles

Dependency injection is an important aspect of the SOLID principles of object-oriented programming. The SOLID principles are guidelines that promote clean, modular, and scalable code. Using dependency injection, we can easily achieve SOLID principles, such as the Single Responsibility Principle (SRP) and the Open-Closed Principle (OCP).

The SRP states that a class should have only one reason to change. By using dependency injection, we can separate the responsibilities of a class and its dependencies, making it easier to maintain and modify the codebase.

The OCP states that a class should be open for extension but closed for modification. By using dependency injection, we can easily add new functionality to a class without modifying it.

Pros & Cons of Dependency Injection

ProsCons
Dependency injection allows for more modular code, as it separates the dependencies from the classes that use them.Dependency injection can add complexity to the codebase, especially for larger projects.
Dependency injection allows for greater codebase flexibility, making it easier to swap out dependencies.If dependencies are not configured correctly, it can lead to runtime errors.
Dependency injection allows for more modular code, separating the dependencies from the classes that use them.

Types of Dependency Injection

There are three types of dependency injection in PHP: 

  • Constructor injection.
  • Setter injection.
  • Interface injection. 

Constructor Injection

Constructor injection involves passing dependencies to a class through its constructor. This is the most common f and most straightforward dependency injection.

Here’s an example of how constructor injection works:

<?php


class Foo
{
    private $bar;
   
    public function __construct(Bar $bar)
    {
        $this->bar = $bar;
    }
}


class Bar
{
    // Class implementation
}


$bar = new Bar();
$foo = new Foo($bar);


?>

In this example, the Foo class depends on an instance of the Bar class, which is passed to the Foo class through its constructor. This ensures that the Foo class has access to the Bar instance it needs to function properly.

Setter Injection

Setter injection involves passing dependencies to a class through its setters. This approach allows dependencies to be added or changed after the class has been instantiated.

Here’s an example of how setter injection works:

<?php


class Foo
{
    private $bar;
   
    public function setBar(Bar $bar)
    {
        $this->bar = $bar;
    }
}


class Bar
{
    // Class implementation
}


$bar = new Bar();
$foo = new Foo();
$foo->setBar($bar);

?>

In this example, the Foo class depends on an instance of the Bar class, which is passed to the Foo class through its setBar() method. This allows the Bar instance to be added or changed after the Foo class has been instantiated.

Interface Injection

Interface injection involves passing dependencies to a class through an interface. This approach allows for more flexibility in managing dependencies and makes swapping out different dependency implementations easier.

Here’s an example of how to interface injection works:

<?php


interface BarInterface
{
    // Interface methods
}


class Foo
{
    private $bar;
   
    public function __construct(BarInterface $bar)
    {
        $this->bar = $bar;
    }
}


class Bar implements BarInterface
{
    // Class implementation
}


class Baz implements BarInterface
{
    // Class implementation
}


$bar = new Bar();
$foo = new Foo($bar);


$baz = new Baz();
$foo = new Foo($baz);


?>

In this example, the Foo class depends on an instance of a class that implements the BarInterface interface. This allows for different implementations of the Bar class to be used without needing to change the Foo class.

Can You Use Dependency Injection in PHP?

Yes, you can use dependency injection in PHP. PHP has several frameworks and libraries that provide support for dependency injection. One of the most popular is the Symfony framework, which has a built-in dependency injection container called the “Symfony DI container”.

When to Use Dependency Injection in PHP?

Here are some more specific use cases for dependency injection:

  1. Simplifying complex object creation: Dependency injection can simplify object creation by removing the need for complex object creation logic in your code. This can make your code easier to read, write, and maintain.
  1. Decoupling dependencies: Dependency injection can help you decouple dependencies between classes, making it easier to modify individual components of your code without affecting other parts of the application.
  1. Improving testability: Dependency injection can make your code more testable by allowing you to replace dependencies with mock objects or stubs during testing. This can make it easier to isolate and test individual code components in isolation.
  1. Managing resource lifecycles: Dependency injection can help you manage resource lifecycles by ensuring that resources are created and destroyed correctly and that dependencies are injected into resources appropriately.
  1. Enabling dynamic behavior: Dependency injection can enable dynamic behavior in your code by allowing you to inject different dependencies at runtime, depending on the current context or configuration.
  1. Simplifying configuration management: Dependency injection can help you manage configuration more easily by allowing you to specify dependencies and their configurations in a central location rather than scattering configuration code throughout your codebase.

These use cases illustrate some of the practical benefits of dependency injection and how it can help improve the quality, maintainability, and flexibility of your code.

Using DI in PHP

Several standalone libraries are available for PHP, such as PHP-DI, Auryn, and Dice, which provide support for dependency injection. These libraries make it easy to implement dependency injection in PHP. They provide a container that manages dependencies and injects them into objects as needed.

In this article, we will focus on PHP-DI.

PHP-DI 7

PHP-DI is a dependency injection container for PHP that is easy to use and highly customizable. It supports several types of dependency injection, including constructor, setter, and interface. PHP-DI also supports auto wiring, which can automatically resolve dependencies for a class based on its constructor or type hint.

How to Setup Dependency Injection in PHP

To use PHP-DI in your PHP project, you must install it using Composer.

composer require php-di/php-di

Once you have installed PHP-DI, you can configure it using a PHP or YAML file. Here is an example of configuring PHP-DI using a PHP file:

<?php


require 'vendor/autoload.php';
use DI\ContainerBuilder;


$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
    // Define your dependencies here
]);
$container = $containerBuilder->build();


?>

In this example, we define a container builder, add definitions for our dependencies, and then build the container. The definitions can be any PHP code that resolves the dependencies.

Using Dependency Injection in PHP Code Example

Here is an example of using dependency injection in a PHP class:

UserService class

Following is an example pseudocode of a service class that creates a user.

<?php


class UserService {




    public function createUser(array $data) {
        // validate user data
        $name = $data['name'];
        $email = $data['email'];
        $password = $data['password'];
           
        echo "User {$name} has been created";
         
        return `{name: {$name}, email: {$email}}`;






    }
}


?>

UserController class

<?php




class UserController {
    private $userService;


    public function __construct(UserService $userService) {
        $this->userService = $userService;
    }


    public function createUser(Request $request) {
        $data = $request->getParsedBody();
        $user = $this->userService->createUser($data);
        return json_encode($user);
    }
}


?>

In this example, we use constructor injection to inject the UserService dependency into the UserController class. This allows us to separate the responsibilities of the UserController and the UserService, making the codebase more modular and easier to maintain.

DI Container

In this example, we’re defining a container using the PHP-DI library. We’re adding definitions for the UserController and UserService classes.

<?php


require 'vendor/autoload.php';
use DI\ContainerBuilder;


// Instantiate container builder
$builder = new ContainerBuilder();


// Configure container
$builder->addDefinitions([
    UserController::class => function ($container) {
        return new UserController(
            $container->get(UserService::class)
        );
    },
    UserService::class => function ($container) {
        return new UserService();
    },
 
]);


// Build container
$container = $builder->build();

?>

The UserController definition is a closure that creates a new instance of UserController and injects an instance of UserService by calling $container->get(UserService::class).

The UserService definition is also a closure that creates a new instance of UserService.

Finally, we build the container using $builder->build(), which returns an instance of the DI\Container class.

We can then use the container to retrieve an instance of UserController and call its methods. For example:

$userController = $container->get(UserController::class);
$response = $userController->createUser($request);

Note that this is just one example of configuring a DI container for the UserController class, and there are many different ways to do so. Understanding the principles of dependency injection and how to apply them to your specific use case is important.

Frequently Asked Questions

What is a dependency injection container?

A dependency injection (DI) container is a software mechanism that manages objects’ instantiation and lifetime and dependencies. It provides a centralized place to configure and manage dependencies, which can help you write more modular and maintainable code.

A DI container can simplify object creation, reduce coupling between different application parts, and improve testability. Managing dependencies in a central location can also make your code more modular and easier to maintain over time.

How does dependency injection help in unit testing?

Dependency injection (DI) simplifies unit testing by decoupling the components of your application, making it easier to isolate and test each component individually. By using DI, you can substitute mock or stub implementations of the dependencies during testing, ensuring that the component you’re testing interacts with the dependencies in the expected way.

This approach simplifies testing by reducing complexity and focusing on the behaviour of each component independently, improving the reliability and maintainability of your application. 

Overall, by using DI and decoupling your components, you can simplify writing and maintaining unit tests, helping you catch bugs earlier and ensure that your application works as intended.

Can dependency injection be used in legacy PHP applications, or is it only relevant for newer projects?

Yes, dependency injection can be used in legacy PHP applications. It can be a valuable technique for improving such applications’ maintainability, testability, and scalability.

However, introducing dependency injection into a legacy codebase can be challenging, and it may require significant refactoring and restructuring of the existing code. 

One approach is to start by identifying the application’s most critical or complex parts and gradually introduce dependency injection to those parts.

It’s also worth noting that legacy applications may not be designed with the SOLID principles in mind, making it more difficult to apply dependency injection effectively. In such cases, it may be necessary to refactor the application to make it more modular and decoupled before introducing dependency injection.

Using Dependency Injection with PHP

In summary, dependency injection is a design pattern that helps you manage dependencies between classes and components in your application. Dependency injection can help you create more flexible, maintainable, and scalable code by decoupling dependencies, simplifying object creation, and improving testability.

There are several types of dependency injection, including constructor, setter, and interface injection. Each with its pros and cons. When deciding which type of injection to use, you should consider the specific requirements of your application.

In PHP, there are several DI containers available, including PHP-DI 7. It provides a simple, flexible way to manage dependencies and configure your application. Using a DI container, you can simplify object creation, decouple dependencies, and improve testability, making creating high-quality, maintainable PHP code easier.

Dependency injection is essential for any PHP developer looking to create flexible, scalable, and maintainable applications.

I hope you have enjoyed this article. Stay tuned for more at FuelingPHP.

PHP Object-Oriented Programming Learning Path

This article is part of our series on learning Object-oriented programming with PHP. It introduces some concepts, best practices and strategies to help you level up your skills. This is a great intro to growing into a successful programmer.

Did you find this article helpful?

Join the best weekly newsletter where I deliver content on building better web applications. I curate the best tips, strategies, news & resources to help you develop highly-scalable and results-driven applications.

Build Better Web Apps

I hope you're enjoying this article.

Get the best content on building better web apps delivered to you.