Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Container Laravel Package

illuminate/container

The Illuminate Container is Laravel's powerful dependency injection container. It manages class dependencies, binds and resolves services, supports contextual and method injection, and provides a simple, flexible foundation for building decoupled PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer (composer require illuminate/container) if you're outside Laravel; inside Laravel it's already there and you resolve through app(), facades, and constructor injection without touching the container directly. First thing to learn is the binding API — bind(), singleton(), instance(), and make(). Start with one interface and one concrete class:

$container = new Illuminate\Container\Container();

$container->bind(
    \App\Contracts\PaymentGateway::class,
    \App\Payments\StripeGateway::class
);

$gateway = $container->make(\App\Contracts\PaymentGateway::class);

Read the source for Container::make(), build(), and resolveDependencies() first — they're compact and explain 90% of the behavior you'll see when debugging. Your first real use case should be replacing new Foo() calls in one service class with constructor-injected dependencies, then binding the class in a service provider. That single change demonstrates the payoff (testability, swappable implementations) before you reach for tagging or contextual binding.

Implementation Patterns

Interface-to-concrete for every external service. The everyday pattern is bind(Contract::class, Implementation::class) in a service provider, and type-hinting the contract in constructors. This is what makes swap()-style mocking in tests trivial and lets you defer "which payment provider / storage driver / mailer" to config.

singleton() for anything stateful or expensive. Database connections, HTTP clients, and caches should be singletons; stateless value objects and controllers should stay transient. If a class holds a connection, a lock, or a pool, bind it as a singleton. Otherwise leave it auto-resolved.

Contextual binding when the same contract needs different implementations. when(UploadController::class)->needs(StorageInterface::class)->give(S3Storage::class) and when(ReportController::class)->needs(StorageInterface::class)->give(LocalStorage::class). Use this instead of branching on instanceof inside the resolved class.

Tagging for plugin/list patterns. Collect related implementations under a tag ($container->tag(Formatter::class, [JsonFormatter::class, XmlFormatter::class])) and pull them all with $container->tagged(). This is the clean way to add a new event handler, exporter, or validator without editing a registry class — the registry just iterates tagged().

call() for method injection in controllers and commands. Route actions, event listeners, and Artisan commands go through $container->call([$handler, 'handle']), which resolves typed method parameters alongside route/event arguments. This is how you accept a Request plus typed service parameters in one method signature.

extend() to decorate post-resolution. Wrap a resolved service with logging, metrics, or retries without touching the class:

$container->extend(ApiClient::class, fn ($client) => new RetryDecorator($client, 3));

Use it sparingly — extension order depends on registration order, and stacking decorators gets confusing fast.

makeWith() for runtime parameters. When a constructor takes untyped scalars (an API key, a tenant id), use $container->makeWith(Foo::class, ['apiKey' => $key]) rather than fighting contextual bindings.

Lazy resolution inside long classes. If a class has a heavy dependency it only uses on some code paths, type-hint a closure or use app() inside the method — but prefer constructor injection and split the class if the dependency really is conditional.

Gotchas and Tips

Don't touch the global instance in long-running processes. Container::setInstance() mutates process-wide state. Under Octane, Horizon, or plain queue workers, this leaks between jobs. Inject dependencies or pass the container explicitly; if you must reset, do it in a clear lifecycle hook and be deliberate.

Circular dependencies surface at resolution time, not registration. The container will throw CircularDependencyException only when you call make(). Catch this early by adding a resolve-smoke test for each of your top-level services. Real fixes: inject an interface with a lazy proxy, split the class, or defer the dependency with a closure.

Auto-wiring only works on type-hinted, resolvable parameters. Any untyped or scalar constructor parameter will fail with BindingResolutionException unless you bind it, give it a default, or use makeWith(). When you see that error, check the constructor signature before assuming the binding is wrong.

bind() vs singleton() is a real behavioral difference in tests. A bind() returns a fresh instance every make() call — usually what you want. A singleton() will hand the same instance to every consumer in a test, which can cause subtle state bleed between assertions. Reset the container or use forgetInstance() between tests.

Use resolved() and build() to debug. resolved(Foo::class) tells you if an instance already exists (helpful when you suspect singleton state), and build(Foo::class) shows how a class would be constructed without caching it. Both are cheap and far more useful than dumping the whole container.

PSR-11 compatibility does not mean feature parity. get() is the PSR-11 method and works everywhere; tag(), tagged(), extend(), when(), and call() are Laravel extensions. If you plan to run the same code in a non-Laravel PSR-11 container later, keep those out of library code.

Watch for instance() sharing surprises. Registering a pre-built object with instance() makes it shared forever in that container — fine for config or a single logger, dangerous for anything request-scoped.

Resolve config-dependent scalars through bindings, not hardcoded values. Bind configuration values as named strings ($container->bind('app.timezone', fn ($c) => $c->make('config')->get('app.timezone'))) rather than closing over env() or config() in the class constructor — it keeps classes testable and container-swappable.

Performance: resolution is fast, but reflection-based auto-wiring on hot paths (per-request loops) adds up. Cache resolved singletons for anything used in a tight loop, and avoid calling make() inside large iterations where the instance doesn't change.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mydnic/volet-feature-board
wmde/fundraising-phpcs
friendlylabs/surveyr
mg-techlegend/laravel-notify-africa
islamabdurahman/laravel-sms-service
dmhendricks/b2-sdk-php
aplicativa/laravel-admin
ezavalishin/vkma
jackardios/laravel-json-api-paginate
viral/laravel-adminpanel
dieppk/log-viewer
daimakuai-ext/helpers
codexshaper/php-blade-view
abraxas81/charts
adamlien/laravel-cas
quimgc/tasks
sistem-atc/bank-sdk
core45/laravel-cookieconsent
projectsend/community-modules
ovac/laravel-hubtel-payment