Dependency Inversion Principle (DIP)
debt(d5/e5/b7/t5)
Closest to 'specialist tool catches' (d5). PHPStan and Psalm (listed in detection_hints.tools) can detect when domain classes import concrete infrastructure classes, but this requires custom rules or strict layer enforcement configurations. The detection is not automated by default and requires deliberate setup of architectural boundary rules.
Closest to 'touches multiple files / significant refactor' (e5). The quick_fix describes flipping dependencies by defining interfaces in domain layers and implementing in infrastructure — this inherently spans multiple files (interface definition, concrete implementation, and all consuming classes). Not architectural rework, but more than a parameterized fix.
Closest to 'strong gravitational pull' (b7). DIP applies across all PHP contexts (web, cli, queue-worker) and is tagged as a SOLID principle affecting OOP architecture. Once DIP is either followed or violated as a pattern, every future class design is shaped by this choice. Violations accumulate as coupling debt; adherence requires consistent discipline across the codebase.
Closest to 'notable trap' (t5). The misconception explicitly states that developers confuse DIP (the principle) with DI (the technique). This is a documented gotcha that most developers eventually learn, but initially causes design confusion — thinking that using a DI container automatically satisfies DIP, when the abstraction direction matters independently.
Also Known As
TL;DR
Explanation
DIP (the D in SOLID) inverts the conventional dependency direction. Instead of OrderService depending directly on MySQLOrderRepository, both depend on OrderRepositoryInterface. This decouples business logic from infrastructure — the repository can be swapped for an in-memory test double or a different database without changing the service. In PHP, DIP is implemented by type-hinting interfaces in constructors and wiring concrete implementations in a DI container. Violating DIP produces tightly-coupled code where changing a storage driver requires editing business logic.
Common Misconception
Why It Matters
Common Mistakes
- High-level service classes importing and instantiating concrete low-level classes directly.
- Interfaces defined in the implementing module rather than the consuming module — inversion not fully achieved.
- Injecting concrete types via constructor even when an interface exists — defeats the purpose.
- Confusing DIP with DI (dependency injection) — DI is a mechanism; DIP is the principle about abstraction direction.
Code Examples
class OrderService {
public function __construct() {
$this->repo = new MySQLOrderRepository(); // concrete — hard to test
}
}
class OrderService {
public function __construct(
private OrderRepositoryInterface $repo // depend on abstraction
) {}
}