Pass service dependencies via constructors and compose them at an application bootstrap boundary. The examples separate the essential operation from the conditions a production implementation must enforce.
What It Does
Pass service dependencies via constructors and compose them at an application bootstrap boundary.
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
The first example isolates the core operation so its result is easy to understand.
<?php
interface Clock { public function now(): int; }
final class SystemClock implements Clock { public function now(): int { return time(); } }
final class Greeting { public function __construct(private Clock $clock) {} public function message(): string { return 'At ' . $this->clock->now(); } }
echo (new Greeting(new SystemClock()))->message();
Example 2: Alternative or Failure Case
The second example demonstrates an alternative outcome, rule or failure condition.
<?php
interface Notifier { public function send(string $s): void; }
final class MemoryNotifier implements Notifier { public array $sent = []; public function send(string $s): void { $this->sent[] = $s; } }
final class Signup { public function __construct(private Notifier $notifier) {} public function run(): void { $this->notifier->send('Welcome'); } }
$fake = new MemoryNotifier(); (new Signup($fake))->run(); echo count($fake->sent);
Practical Example: Applying the Rule
The practical example shows how to carry the same constraint into application code.
<?php
final class AppConfig { public function __construct(public readonly string $timezone) {} }
final class Formatter { public function __construct(private AppConfig $settings) {} public function zone(): string { return $this->settings->timezone; } }
$settings = new AppConfig('UTC');
echo (new Formatter($settings))->zone();
Important Behavior and Edge Cases
Avoid global service locators and uncontrolled container access inside domain classes.
Comparison with Related Functions or Patterns
| Approach | When it differs |
|---|---|
| Using Dependency Injection in Larger Projects | Pass service dependencies via constructors and compose them at an application bootstrap boundary. |
| Related API or pattern | Factories handle conditional object construction while constructor injection exposes requirements. |
Common Beginner Mistakes
| Mistake | Correct approach |
|---|---|
| Creating dependencies inside a domain method instead of injecting them | Avoid global service locators and uncontrolled container access inside domain classes. |
| Substituting a related API without checking its semantics | Factories handle conditional object construction while constructor injection exposes requirements. |
Key Takeaways
- Pass service dependencies via constructors and compose them at an application bootstrap boundary.
- Avoid global service locators and uncontrolled container access inside domain classes.
- Factories handle conditional object construction while constructor injection exposes requirements.
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.