Wire interfaces and implementations manually at the composition root. The examples separate the essential operation from the conditions a production implementation must enforce.
What It Does
Wire interfaces and implementations manually at the composition root.
Prerequisites
Use a dedicated development environment with test accounts and records. Separate authorization, validation and error handling from response formatting; never copy demonstration-only secrets into production.
Example 1: Core Behavior
This scenario applies the exact rule described in the guide, with controlled sample values.
<?php
interface CacheStore { public function get(string $key): ?string; }
final class LocalCache implements CacheStore { public function get(string $key): ?string {return null;} }
final class Reader {public function __construct(private CacheStore $cache) {} public function read(): string {return $this->cache->get("home")??"miss";} }
echo (new Reader(new LocalCache()))->read();
Example 2: Alternative or Failure Case
This scenario applies the exact rule described in the guide, with controlled sample values.
<?php
interface Logger {public function write(string $message): void;}
final class SpyLogger implements Logger { public array $logs=[]; public function write(string $message): void {$this->logs[]=$message;} }
$spy=new SpyLogger(); $spy->write("job started"); echo count($spy->logs);
Practical Example: Applying the Rule
This scenario applies the exact rule described in the guide, with controlled sample values.
<?php
final class ClockService {public function __construct(private Closure $clock) {} public function read(): int {return ($this->clock)();}}
$clock=new ClockService(static fn():int=>1234); echo $clock->read();
Important Behavior and Edge Cases
Do not instantiate external clients directly inside business methods if they must be substituted in tests.
Comparison with Related Functions or Patterns
| Approach | When it differs |
|---|---|
| Using Dependency Injection Without a Framework | Wire interfaces and implementations manually at the composition root. |
| Related API or pattern | An interface makes a boundary testable; a container only automates wiring. |
Common Beginner Mistakes
| Mistake | Correct approach |
|---|---|
| Creating dependencies inside a domain method instead of injecting them | Do not instantiate external clients directly inside business methods if they must be substituted in tests. |
| Substituting a related API without checking its semantics | An interface makes a boundary testable; a container only automates wiring. |
Key Takeaways
- Wire interfaces and implementations manually at the composition root.
- Do not instantiate external clients directly inside business methods if they must be substituted in tests.
- An interface makes a boundary testable; a container only automates wiring.
Select this API or pattern based on its documented behavior and the trust boundaries in your own application, and test all external integrations against their actual deployments.