Dependency Injection
Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 57 of 91.
In the previous lesson, you saw how composition lets a Car contain an Engine object. But there's a problem with creating dependencies inside the class - it makes the code rigid and hard to test. Dependency Injection solves this by passing dependencies from the outside rather than creating them internally.
Compare these two approaches:
<?php
// Without DI - dependency created inside
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine(); // Tightly coupled
}
}
// With DI - dependency passed in
class Car {
public function __construct(private Engine $engine) {}
}
$engine = new Engine();
$car = new Car($engine); // Injected from outside
The second approach is dependency injection. The Car doesn't create its own engine - it receives one. This simple change has powerful benefits: you can pass different engine types, swap implementations for testing, and the class becomes more flexible.
For even greater flexibility, inject interfaces instead of concrete classes:
<?php
interface EngineInterface {
public function start(): string;
}
class Car {
public function __construct(private EngineInterface $engine) {}
public function start(): string {
return $this->engine->start();
}
}
Now Car works with any class implementing EngineInterface - a gas engine, electric motor, or a mock for testing. This decoupling is why dependency injection is fundamental to writing maintainable, testable PHP applications.
Challenge
EasyLet's build a notification system that demonstrates the power of dependency injection. Instead of hardcoding how messages are sent, you'll create a flexible system where the delivery method can be swapped out easily.
You'll organize your code across four files:
MessageSenderInterface.php— Define an interface calledMessageSenderInterfacewith a single methodsend(string $recipient, string $message): string. This interface establishes the contract that any message sender must follow.EmailSender.php— Create anEmailSenderclass that implementsMessageSenderInterface. Include the interface file. Thesend()method should return"Email to [recipient]: [message]".NotificationService.php— Create aNotificationServiceclass that receives its dependency through the constructor. Include the interface file. The class should:- Accept a
MessageSenderInterfacein its constructor using constructor promotion - Have a method
notify(string $recipient, string $message)that delegates to the injected sender and returns its result
NotificationServicedepends on the interface, not a concrete class. This means you could inject any sender that implements the interface without changing the service.- Accept a
main.php— Include the EmailSender and NotificationService files. You'll receive two inputs: a recipient and a message. Create anEmailSender, inject it into aNotificationService, then callnotify()and print the result.
This pattern keeps your NotificationService flexible — it works with any sender you inject, whether that's email, SMS, or something you haven't built yet. The service doesn't create its own dependencies; it receives them from the outside.
Cheat sheet
Dependency Injection (DI) is a technique where dependencies are passed into a class from the outside rather than created internally.
Without DI (tightly coupled):
<?php
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine(); // Created inside
}
}With DI (loosely coupled):
<?php
class Car {
public function __construct(private Engine $engine) {}
}
$engine = new Engine();
$car = new Car($engine); // Injected from outsideFor maximum flexibility, inject interfaces instead of concrete classes:
<?php
interface EngineInterface {
public function start(): string;
}
class Car {
public function __construct(private EngineInterface $engine) {}
public function start(): string {
return $this->engine->start();
}
}This allows Car to work with any class implementing EngineInterface, making the code more maintainable and testable.
Try it yourself
<?php
require_once 'EmailSender.php';
require_once 'NotificationService.php';
// Read input
$recipient = trim(fgets(STDIN));
$message = trim(fgets(STDIN));
// TODO: Create an EmailSender instance
// TODO: Inject it into a NotificationService
// TODO: Call notify() and print the result
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System10Advanced OOP Concepts
Composition vs InheritanceDependency InjectionAnonymous ClassesEnums (PHP 8.1)Fibers (PHP 8.1)Object Cloning Deep DiveGenerators & Iterators2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern