What Is Dependency Injection in PHP? A Beginner-Friendly Explanation

What Is Dependency Injection in PHP? A Beginner-Friendly Explanation
Dependency Injection (DI) means giving an object (class) the things it depends on, instead of the object creating them itself.
In simple words:
"You don't let the class build what it needs; you give it what it needs."
This makes your code easier to test, reuse, and maintain.
Let's understand without dependency injection (DI):
Imagine we have a class UserController that sends a welcome email when a new user registers.
Example 1: Without dependency injection:
class MailService {
public function sendMail($email, $message) {
echo "Sending mail to $email: $message";
}
}
The MailService class will be responsible for sending emails to the newly registered users.
class UserController {
private $mailService;
public function __construct() {
// The class creates the dependency itself
$this->mailService = new MailService();
}
public function register($email) {
// Some registration logic...
$this->mailService->sendMail($email, "Welcome to our website!");
}
}
The UserController class instantiates the MailService object in its constructor, the class creates the dependency itself.
$user = new UserController();
$user->register("user@example.com");
What's wrong here?
UserControllerdepends directly onMailService.- You cannot easily replace
MailService(e.g., for testing or switching to another mail provider like SendGrid). - Hard to test and maintain.
Example 2: With Dependency Injection:
Instead of creating the dependency inside the class, we inject it from outside:
class MailService {
public function sendMail($email, $message) {
echo "Sending mail to $email: $message";
}
}
class UserController {
private $mailService;
// We inject MailService via the constructor
public function __construct(MailService $mailService) {
$this->mailService = $mailService;
}
public function register($email) {
// Some registration logic...
$this->mailService->sendMail($email, "Welcome to our website!");
}
}
// We create the dependency and inject it
$mailService = new MailService();
$user = new UserController($mailService);
$user->register("user@example.com");
Why is this better?
1- Loos Coupling:
The UserController doesn't know how the MailService works; it just uses it. So, we can replace it at anytime.
2- Easier Testing:
We can inject a fake mail service during testing.
3- More Reusable:
We can reuse UserController with a different mail service, like (e.g., Mailgun, SendGrid).
Example 3: Dependency Injection for Testing
Suppose you want to test the UserController, but you don't want to send real emails. You can inject a fake mail service.
class FakeMailService {
public function sendMail($email, $message) {
echo "Pretending to send mail to $email: $message";
}
}
$fakeMail = new FakeMailService();
$user = new UserController($fakeMail);
$user->register("test@example.com");
Now, UserControllerworks perfectly without sending a real email - because you injected a different dependency.
Types of Dependency Injection:
There are mainly 3 ways to inject dependencies in PHP.
Constructor Injection (Most common)
Inject through the constructor.
class Controller {
public function __construct(MailService $mailService) { ... }
}
Setter Injection
Inject using a setter method.
class Controller {
private $mailService;
public function setMailService(MailService $mailService) {
$this->mailService = $mailService;
}
}
Method Injection
Inject directly into the method.
class Controller {
public function register($email, MailService $mailService) {
$mailService->sendMail($email, "Welcome!");
}
}
Conclusion
Dependency Injection in PHP is ultimately about writing flexible, maintainable, and testable code. When a class creates its own dependencies, it becomes tightly coupled to specific implementations, which makes your application harder to change, test, and extend.
By injecting dependencies instead of constructing them internally:
- You give each class exactly what it needs, without forcing it to decide how it gets it.
- You make it possible to swap implementations anytime - for example, switching from real service to a fake service when testing.
- You keep your code cleaner, more organized, and easier to reason about.
This simple shift - moving object creation from inside the class to outside the class - is what makes dependency injection a powerful pattern in both PHP and Laravel. It lays the foundation for more advanced concepts like interfaces, service providers, the Laravel service container, and design patterns that keep your application scalable and professional.