Search
Search the entire web effortlessly
maxresdefault   2025 05 02T204510.350
Implementing a PSR-11 Dependency Injection Container in PHP

Dependency Injection (DI) is an essential concept in modern software development, particularly in PHP programming. It allows for better separation of concerns, making it easier to manage code dependencies, and enhancing testability. In this article, we will delve deep into implementing a basic PSR-11 compatible Dependency Injection (DI) container in PHP. Specifically, we will explore how the DI container functions, what autowiring is, and how to leverage reflection to automate dependency resolution.

Understanding Dependency Injection and DI Containers

What is Dependency Injection?

Dependency Injection is a design pattern used to implement Inversion of Control (IoC), whereby the control of instantiation of an object is transferred from the object itself to an external source. This allows the dependencies of an object to be injected at runtime, thus promoting loose coupling and flexibility in the code.

What is a DI Container?

A Dependency Injection Container is a class responsible for managing the instantiation of different classes and their dependencies. By storing references to these classes, a DI container can resolve the required instances when needed without direct instantiation code scattered throughout your application. These containers allow for complexity reduction through automation of dependency resolution, especially when combined with features like autowiring.

Introducing PSR-11

PSR-11 is a specification designed for Dependency Injection Containers. It defines a set of interfaces that enable the implementation of DI and ensures consistency across different projects and libraries. Through this standardization, developers benefit from a common API that they can use regardless of the underlying container implementation.

Core Interfaces of PSR-11

The two main interfaces defined by PSR-11 are:

  • ContainerInterface: Defines the core methods such as get() and has() for retrieving services.
  • NotFoundExceptionInterface: Used to indicate a requested service was not found in the container.

Creating a simple DI container will involve implementing these interfaces, allowing us to manage our dependencies within the application seamlessly.

Building a Basic DI Container

Step 1: Setup Composer Dependency

To start with our implementation, ensure that Composer is installed. Then, we will include the necessary PSR-11 dependency:

composer require psr/container  

Step 2: Container Class Implementation

Next, create a container class that implements the ContainerInterface. In this example, we’ll establish the basic structure with methods for adding and retrieving entries:

namespace App\Container;  

use Psr\Container\ContainerInterface;  
use Psr\Container\NotFoundExceptionInterface;  

class SimpleContainer implements ContainerInterface {
    private $entries = [];

    public function set(string $id, callable $concrete): void {
        $this->entries[$id] = $concrete;
    }

    public function get(string $id) {
        if (!isset($this->entries[$id])) {
            throw new NotFoundException("No entry found for ID: $id");
        }
        return ($this->entries[$id])();
    }

    public function has(string $id): bool {
        return isset($this->entries[$id]);
    }
}

Step 3: Implement Dependency Resolution and Autowiring

To enhance our DI container with autowiring capability, we’ll also include reflection to inspect classes and resolve their dependencies:

  1. Inspect the Class: Utilize PHP’s Reflection API to inspect the class definitions.
  2. Resolve Dependencies: Recursively resolve the dependencies based on the class constructors.

Here is a simplified version of how that could be implemented:

public function resolve(string $id) {
   $reflector = new \ReflectionClass($id);
   if (!$reflector->isInstantiable()) {
       throw new ContainerException("Cannot instantiate $id");
   }
   $constructor = $reflector->getConstructor();
   if (is_null($constructor)) {
       return new $id();
   }
   $parameters = $constructor->getParameters();
   $dependencies = array_map(function($parameter) {
       return $this->resolve($parameter->getType()->getName());
   }, $parameters);
   return $reflector->newInstanceArgs($dependencies);
}

Step 4: Registering Services within the Container

To fully utilize our DI container, we need to register the services that our application will use. This can be done in a main application class:

$config = new SimpleContainer();
$config->set('InvoiceService', function () {
   return new InvoiceService(
      $this->get('SalesTaxService'),
      $this->get('PaymentGatewayService'),
      $this->get('EmailService')
   );
});

Step 5: Utilizing the DI Container

With our container implemented and services registered, we can now retrieve instances without directly creating them:

$invoiceService = $config->get('InvoiceService');
$invoiceService->process();

Conclusion

By implementing a Dependency Injection container using PSR-11, we create a more maintainable and testable codebase. Dependency Injection enhances our application’s flexibility, allowing us to swap service implementations effortlessly. While the container we built here is simple and lacks advanced features such as caching or singleton management, it provides a strong foundation for understanding more sophisticated implementations in popular frameworks.

If you’re working with PHP and looking to improve your code organization, consider integrating a DI container into your applications. With this knowledge, you can simplify how you manage class dependencies and create more modular systems.

For further enhancements, consider exploring autowiring capabilities, error handling, and integrating a full-service provider to streamline your application setup. Let me know if you have any questions or feedback on this tutorial!


With these fundamentals of DI containers outlined, you are now positioned to implement dependency injection effectively in your own PHP applications. Don’t hesitate to share your experiences!