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.
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 PatternPractice on your own: Online PHP compiler