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

sergix44/container

A lightweight PSR-11 compatible dependency injection container for PHP, with bindings, singletons, aliases, and automatic constructor resolution. It keeps service wiring simple and testable for small apps and libraries, without requiring a full framework.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install it: composer require sergix44/container. It publishes no service provider, config file, or facade — that is intentional, and it is the first thing to internalise.
  2. Look first at composer.json for the PSR-11 requirement, then src/ for the Container class. The README is short; the class itself, and the PSR-11 get() / has() surface, are the real documentation. Check the tests directory for canonical binding/resolution examples before writing your own.
  3. Confirm it implements Psr\Container\ContainerInterface (both get(string $id) and has(string $id)), since that is your interoperability contract and your best debugging hook.

First use case — bind an interface to an implementation and resolve it:

use Sergix44\Container\Container;

$container = new Container();

// transient: fresh instance each get()
$container->set(LoggerInterface::class, FileLogger::class);

// singleton/shared: same instance every get()
$container->set(Database::class, fn () => new Database(config('db')));

$db = $container->get(Database::class);      // shared
$logger = $container->get(LoggerInterface::class); // new each time

Treat this as a micro-container for a framework-agnostic slice of your app (a package, a domain module, a CLI worker), not as a replacement for Laravel's container.

Implementation Patterns

Scope it deliberately. The strongest daily pattern in a Laravel app is to isolate a subsystem — a pricing engine, an importer, a queue worker — behind a PSR-11 container you build yourself. Laravel's own container stays untouched for HTTP/framework wiring; this container lives inside the module and can later be lifted into a standalone package with zero Laravel coupling.

Register in a single bootstrap point. Mirror Laravel's service-provider idea without the framework: one bindings.php or a ModuleContainerFactory that does all set() calls, so you never wonder where a binding came from. Keep it deterministic and free of side effects.

final class PricingContainer
{
    public static function make(): Container
    {
        $c = new Container();
        $c->set(TaxRateProvider::class, fn () => new EuTaxRates::fromConfig(config('tax')));
        $c->set(PriceCalculator::class, PriceCalculator::class);
        return $c;
    }
}

Use closure factories for anything with config or env dependencies. Closures are lazy — nothing is constructed until get() fires, so a singleton closure that reads config() won't blow up during boot/console commands that never touch it. This is the cleanest way to inject Laravel config or env() values without importing app() into the module.

Lean on automatic constructor injection for plain services. Let the container resolve type-hinted constructor params for you; only register bindings where an interface needs a concrete choice or a value needs constructing. Fewer bindings means fewer places to drift.

Bridge into Laravel once, not everywhere. If a Laravel controller needs the module's service, resolve it through Laravel:

$this->app->singleton(PriceCalculator::class,
    fn () => PricingContainer::make()->get(PriceCalculator::class));

Now Laravel owns the lifecycle and the micro-container stays internal. Avoid passing the PSR-11 container itself around as a service locator.

Interop with PSR-11 consumers. Because it is compliant, you can hand this container to any third-party library that type-hints ContainerInterface (e.g. a router or a middleware stack in a CLI tool). That is where the "framework-agnostic" claim earns its keep.

Gotchas and Tips

  • Don't confuse the two containers. Calling app() from a class resolved by the micro-container, or vice versa, silently couples them and defeats the purpose. The module should only ever receive values, not the Laravel container.
  • Singleton bleed in long-running workers. Under Octane, queue workers, or queue:work daemons, a "singleton" in a container you rebuilt per request is fine — but a container cached in a static or a long-lived worker will return stale instances holding stale config. Rebuild the container per job/request, or be certain the shared state is safe.
  • Automatic injection only goes as far as reflection can type-hint. Untyped params, union types, and scalars (string $dsn) cannot be auto-wired — bind those explicitly with closures. If resolution fails, the exception is your signal, not a mystery.
  • PSR-11 exception types matter. Lookups for unbound IDs must throw NotFoundExceptionInterface; construction failures must surface as ContainerExceptionInterface. When wrapping third-party code, catch those interfaces, not concrete classes, so swapping containers later stays cheap.
  • Transient vs shared is the whole lifecycle decision. The default you pick determines whether you fight shared mutability later. Register stateful services (calculators, parsers) transient, database clients and caches shared. Audit any set() without an explicit lifecycle.
  • Debugging bindings. Keep a tiny dump() helper or assert $container->has(Id::class) before get() in a test. Since there's no debug toolbar, tests are the observability layer — assert that each binding resolves and returns the expected concrete type.
  • Closure binding signatures are inconsistent across containers. This package is not Laravel's container: a closure may or may not receive the container as an argument. Check src/ for the exact factory signature before relying on fn (Container $c) => ...; getting this wrong is a common silent TypeError.
  • Extension points are thin by design. There is no middleware, tag, or contextual binding. If you find yourself needing those, that's the signal to use Laravel's container or League\Container instead of bending this one. Treat its small surface as a feature and keep module wiring explicit.
  • Pin the version. With a small, low-star package, a ^ constraint on a pre-1.0 release can pull breaking changes; pin to the 2024-06-18-compatible line you tested against.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console