Dependency Injection in PHP: Complete Guide with Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Dependency Injection in PHP



Modern PHP development is unthinkable without the principles of object-oriented design. One of the key patterns that allows building flexible, testable, and maintainable applications is Dependency Injection (DI).



The essence of the pattern is simple: instead of creating dependent objects inside a class, we pass them (inject them) from the outside. This fundamentally changes the application architecture, making classes independent of specific implementations and simplifying their testing.



In this article, we will break down what Dependency Injection in PHP is, what ways exist to implement it, and how to use DI containers to automate the process.



The Problem: why is it bad without DI?



Let's consider a typical example. The UserController class is responsible for processing requests related to users. It uses a database through the MysqlDatabase class:



<?php

class MysqlDatabase { public function query(string $sql): array { // ... connection to MySQL and query execution return []; }}

class UserController { private MysqlDatabase $database;

public function __construct() { // Tight coupling to a specific implementation $this->database = new MysqlDatabase(); }

public function listUsers(): array { return $this->database->query('SELECT * FROM users'); }}

$controller = new UserController();


What are the problems with this code?



  • Tight coupling: UserController is tightly bound to MysqlDatabase. If we want to switch to PostgreSQL or use a file storage, we have to change the controller code.
  • Testing difficulty: You cannot replace the database with a mock object in unit tests. Every test will hit the real database, which is slow and inconvenient.
  • Violation of the Single Responsibility Principle: The controller should not know how to create a database connection. Its job is to handle requests.


The solution to this problem is to apply the Inversion of Control (IoC) principle and use Dependency Injection.



Ways to inject dependencies in PHP



There are three main ways to inject a dependency into a class:



1. Constructor Injection



The most common and recommended method. The dependency is passed through a constructor parameter and is usually stored as a private property of the class.



<?php

interface DatabaseInterface { public function query(string $sql): array;}

class MysqlDatabase implements DatabaseInterface { public function query(string $sql): array { // Implementation for MySQL return []; }}

class PostgresDatabase implements DatabaseInterface { public function query(string $sql): array { // Implementation for PostgreSQL return []; }}

class UserController { private DatabaseInterface $database;

// Dependency is injected via the constructor public function __construct(DatabaseInterface $database) { $this->database = $database; }

public function listUsers(): array { return $this->database->query('SELECT * FROM users'); }}

// Usage$mysqlDb = new MysqlDatabase();$controller = new UserController($mysqlDb);

// Easily switch to another DB$postgresDb = new PostgresDatabase();$controller2 = new UserController($postgresDb);


Pros: The dependency is explicitly visible, the object is created in a valid state (dependency is mandatory).



2. Setter Injection



Suitable for optional dependencies or when dependenc

Blogs

Book Recommendations