Dependency Injection passes required objects to a class through the constructor instead of creating them internally. This makes classes easier to test and swap. Type-hint against interfaces for maximum flexibility.
Dependency Injection
// Without DI — tightly coupled, hard to test
class UserService {
public function find(int $id): array {
$pdo = new PDO("mysql:...", "user", "pass"); // hardcoded!
// ...
}
}
// With DI — decoupled, easy to test and swap
class UserService {
public function __construct(
private UserRepository $repo,
private LoggerInterface $logger,
private CacheInterface $cache
) {}
public function find(int $id): ?User {
$this->logger->info("Finding user $id");
return $this->cache->remember("user.$id", 3600,
fn() => $this->repo->findById($id)
);
}
}
// DI Container (basic)
$container = new Container();
$container->bind(LoggerInterface::class, FileLogger::class);
$service = $container->make(UserService::class);