# Dependency Injection

> Use this skill when wiring dependencies, configuring a DI container, designing plugin/extension points, or migrating away from legacy service locators in PHP. Triggers on questions about constructor injection, interface registration, swapping implementations, or testing without booting the container.

- **Type:** Skill
- **Install:** `agentstack add skill-garrettw-php-arch-skills-dependency-injection`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [garrettw](https://agentstack.voostack.com/s/garrettw)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MPL-2.0
- **Upstream author:** [garrettw](https://github.com/garrettw)
- **Source:** https://github.com/garrettw/php-arch-skills/tree/main/skills/dependency-injection

## Install

```sh
agentstack add skill-garrettw-php-arch-skills-dependency-injection
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Dependency Injection Best Practices for PHP

## When to Use a DI Container (and When NOT to)
A DI container is an infrastructure concern. Use it for applications that need flexible wiring, interface swapping, or framework integration. Libraries should use constructor injection directly and let the consumer resolve dependencies.

| Use a DI Container For                            | Do Not Use a Container For               |
|---------------------------------------------------|------------------------------------------|
| Applications with many interchangeable services   | Libraries and packages                   |
| Need to swap implementations (test vs production) | Simple scripts with one or two classes   |
| Framework integration (Symfony, Laravel)          | Throwaway code or prototypes             |

The container should wire the application together at the edge, while the core remains ignorant of the container. This skill provides the rules for registering dependencies, migrating legacy locators, and injecting objects.

A [**Plugin**](references/plugin.md) is the purpose this wiring serves: define extension points (Ports) so new behavior is added by plugging in implementations (Adapters) at the composition root, without modifying the core. A plugin must *extend* the existing abstractions, never bypass them. It protects the **framework / extension boundary** — one of a family of boundary-protection patterns (with Gateway, Mapper, Remote Facade, DTO, and Special Case) that form the foundation of Hexagonal architecture; see [boundary-protection-patterns.md](../distribution-patterns/references/boundary-protection-patterns.md).

## Numbered Workflows

### 1. Registering Dependencies
If deciding whether to create an interface and register it in the DI container:
1. **Check for Multiple Implementations.** Are there multiple implementations (e.g., `StripeAdapter` and `PayPalAdapter`)?
2. **Check for Boundary Inversion.** Is the application defining a port that the infrastructure implements?
3. **Check for Decorators.** Do you need to wrap the class with caching or logging?
4. **If Yes to any:** Create an interface and register the binding in the container configuration.
5. **If No to all:** Depend on the concrete class directly and let the container autowire it. Do not blindly create interfaces for every class.

### 2. Testing with Constructor Injection
If writing tests for classes that use constructor injection:
1. **For Domain/Handler Tests:** Construct the object using `new`. Pass in test doubles (mocks or fakes) for any infrastructure dependencies. Do not boot the DI container.
2. **For Integration/Feature Tests:** Ask the test framework to resolve the controller or handler from the DI container.

### 3. Migrating Legacy Code
If modifying a legacy class that uses a service locator (`Container::getInstance()->get()`):
1. **Follow the Migration Sequence.** Read the [Migration Sequence for Legacy Containers](references/migration-sequence.md).
2. **Extract to Constructor.** Find every internal `getInstance()` call and add those dependencies as typed constructor properties.
3. **Fix Callers.** Update all instantiations of the legacy class to pass the dependencies.
4. **Use Shims if Necessary.** If the class is instantiated in 50 places you cannot change, use the temporary shim pattern to bridge the gap.

## Boundaries

### Always Do
- Always use constructor injection for services, handlers, repositories, adapters, and listeners.
- Always keep the container wiring logic at the framework edge (composition roots, service providers).
- Always keep the domain core free of framework dependencies. It should resolve from tests or a composition root with no framework boot.
- Always treat a plugin as a first-class architectural citizen: it implements a defined Port and obeys the same dependency rule and invariants as core code (see [plugin.md](references/plugin.md)).

### Ask First
- Ask before introducing a legacy shim if it is feasible to simply update all callers instead.

### Never Do
- Never use service locators inside domain objects, application handlers, or policies. They hide dependencies and couple core code to the framework.
- Never let a plugin become a backdoor: it must not reach into core internals, write to the database directly, or skip domain invariants. Plug in via the defined Port only.

## Related Patterns
- The **Factory** pattern (variant selection, composition-root wiring, Singleton as anti-pattern) is covered in [creation-patterns.md](references/creation-patterns.md).

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [garrettw](https://github.com/garrettw)
- **Source:** [garrettw/php-arch-skills](https://github.com/garrettw/php-arch-skills)
- **License:** MPL-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-garrettw-php-arch-skills-dependency-injection
- Seller: https://agentstack.voostack.com/s/garrettw
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
