Dependency Injection in Modern PHP
Constructor injection, service boundaries, testability, and the practical value behind the terminology.
Dependency injection means an object receives the collaborators it needs instead of locating or constructing them. That small constraint produces clearer APIs and cheaper tests.
Dependencies are part of the type’s contract
A constructor tells readers what must exist for the object to work. Depending on interfaces separates policy from implementation and allows infrastructure to change without rewriting business rules.
Prefer constructor injection
Constructor injection creates valid objects in one step. Setter injection is appropriate only for genuinely optional behavior; service location hides requirements and moves failures to runtime.
Testing gets smaller
A service with explicit collaborators can be tested with a fake clock, repository, or mailer. The test focuses on decisions rather than booting the framework.
Working example
final class PublishArticle {
public function __construct(
private ArticleRepository $articles,
private Clock $clock,
) {}
public function __invoke(Article $article): void {
$article->publishAt($this->clock->now());
$this->articles->save($article);
}
}Key Takeaways
- Make required collaborators constructor arguments.
- Depend on narrow interfaces at architectural boundaries.
- Use injection to simplify decisions and tests, not as ceremony.