Content · Web Development · PHP

Using Dependency Injection Without a Framework

Wire interfaces and implementations manually at the composition.

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

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


ApproachWhen it differs
Using Dependency Injection Without a FrameworkWire interfaces and implementations manually at the composition root.
Related API or patternAn interface makes a boundary testable; a container only automates wiring.

Common Beginner Mistakes


MistakeCorrect approach
Creating dependencies inside a domain method instead of injecting themDo not instantiate external clients directly inside business methods if they must be substituted in tests.
Substituting a related API without checking its semanticsAn 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.

Share this resourceSend this page to someone who may find it useful.