IoC Container
debt(d7/e5/b7/t5)
Closest to 'only careful code review or runtime testing' (d7). The common mistakes — injecting the container itself (Service Locator antipattern), binding concretes to concretes, missing singleton bindings — are not caught by compilers or standard linters. No detection_hints.tools are specified. These misuses typically surface only during code review or when tests reveal tight coupling or unexpected object recreation at runtime.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix describes a targeted bind() call in a ServiceProvider, which sounds simple, but common mistakes like the Service Locator antipattern (injecting the container into many classes) or widespread concrete-to-concrete bindings require touching every affected class to replace container references with proper constructor injection — a multi-file refactor across a component.
Closest to 'strong gravitational pull' (b7). The IoC container applies to both web and cli contexts across the entire application. Every class that participates in dependency injection is shaped by how the container is configured. Architectural decisions like interface-to-implementation bindings and singleton vs. transient lifetimes ripple through the entire codebase, and every new service added must conform to the container's conventions. This is a load-bearing, high-reach choice.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The canonical misconception is that a DI container is required for dependency injection — many developers conflate the two concepts and either avoid DI because they think they need a container, or overuse the container for simple cases. The Service Locator antipattern (injecting the container itself) is another well-documented trap that most PHP developers encounter and must unlearn.
Also Known As
TL;DR
Explanation
An IoC (Inversion of Control) container, also called a DI (Dependency Injection) container or service container, maintains a registry of how to construct objects. When you request a class, the container inspects its constructor type hints, recursively resolves each dependency, and returns a fully-constructed instance. In Laravel, the service container is the central registry — type-hint an interface in a controller constructor and the container injects the concrete implementation bound to that interface. Symfony's DI component reads service definitions from YAML/XML/PHP configuration. Binding patterns: concrete bindings (App::bind(UserRepository::class, EloquentUserRepository::class)); singleton bindings (App::singleton(Cache::class, RedisCache::class) — same instance reused); factory bindings (App::bind(Report::class, fn($app) => new Report($app->make(DB::class), config('reports')))); contextual bindings (inject different implementations depending on which class is requesting the dependency).
Common Misconception
Why It Matters
Common Mistakes
- Injecting the container itself into classes — this is the Service Locator antipattern; inject specific dependencies, not the container.
- Binding concrete classes to concrete classes — bind interfaces to implementations so you can swap implementations without changing dependent code.
- Not using singleton binding for expensive-to-construct services — database connections and HTTP clients should be shared instances, not recreated per-request.
- Overusing the container for simple value objects — inject config values directly, not through the container.
Code Examples
// Service locator antipattern — container injected into class
class OrderService {
public function __construct(private Container $container) {}
public function process(int $id): void {
// Hidden dependency — unclear what this class needs
$repo = $this->container->make(OrderRepository::class);
$mail = $this->container->make(Mailer::class);
}
}
// Constructor injection — dependencies explicit, testable
class OrderService {
public function __construct(
private OrderRepository $orders,
private Mailer $mailer,
) {}
public function process(int $id): void {
$order = $this->orders->find($id); // injected dependency
$this->mailer->send(new OrderConfirmed($order));
}
}
// ServiceProvider — bind interface to implementation once
public function register(): void {
$this->app->singleton(OrderRepository::class, EloquentOrderRepository::class);
$this->app->singleton(Mailer::class, SmtpMailer::class);
}