Content · Web Development · PHP

PHP Dependency Inversion with Practical Examples

High-level services should depend on interfaces that can be implemented by databases, queues or test doubles.

1 views Published Oct 17, 2026 Updated Oct 17, 2026 Admin

High-level services should depend on interfaces that can be implemented by databases, queues or test doubles.

What It Does and When to Use It


A high-level use case depends on an interface rather than a concrete transport. A minimum platform requirement controls what syntax can be parsed; target-version tests find runtime and extension incompatibilities.

Requirements, Inputs and Expected Behavior


Confirm the runtime, extension, environment variables, and permissions before running the examples in production. Pass interfaces through constructors instead of instantiating concrete infrastructure in methods.

Example 1: Basic Operation


A high-level use case depends on an interface rather than a concrete transport.

<?php
interface Notifier { public function send(string $message): void; }
 final class EchoNotifier implements Notifier {
     public function send(string $message): void { echo $message; }
 }
 final class RegisterUser {
     public function __construct(private Notifier $notifier) {}
     public function run(): void { $this->notifier->send('Welcome'); }
 }
 (new RegisterUser(new EchoNotifier()))->run();

Example 2: Behavior and Validation


Inject a clock to make time-dependent behavior testable.

<?php
interface Clock { public function now(): DateTimeImmutable; }
 final class FixedClock implements Clock {
     public function now(): DateTimeImmutable { return new DateTimeImmutable('2026-01-01'); }
 }
 function expiresAt(Clock $clock): string { return $clock->now()->modify('+1 day')->format('Y-m-d'); }
 echo expiresAt(new FixedClock());

Practical Example: Application Use


Use an in-memory substitute without changing application code.

<?php
interface PriceReader { public function price(int $id): int; }
 final class InMemoryPrices implements PriceReader { public function price(int $id): int { return 50; } }
 final class QuoteService {
     public function __construct(private PriceReader $prices) {}
     public function quote(int $id, int $quantity): int { return $this->prices->price($id) * $quantity; }
 }
 echo (new QuoteService(new InMemoryPrices()))->quote(1, 3);

Important Behavior and Edge Cases


The second example highlights: Inject a clock to make time-dependent behavior testable. The practical example adds: Use an in-memory substitute without changing application code.

Comparison with Related Approaches


ApproachWhat changes
Minimum PHP version and target PHP versionA minimum platform requirement controls what syntax can be parsed; target-version tests find runtime and extension incompatibilities.
Choosing safelyDo not introduce syntax from a newer minor version before every production host supports it.

Common Beginner Mistakes


MistakeCorrect approach
Assuming newer syntax parses on older PHPLint with the oldest supported runtime; gate or delay newer syntax.
Upgrading CLI PHP but not the web workerCheck the FPM binary, all extensions and the Composer platform in staging.
Skipping operational constraintsPass interfaces through constructors instead of instantiating concrete infrastructure in methods.

Key Takeaways


  • High-level services should depend on interfaces that can be implemented by databases, queues or test doubles.
  • Use an in-memory substitute without changing application code.
  • Do not introduce syntax from a newer minor version before every production host supports it.
Share this resourceSend this page to someone who may find it useful.