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

flightphp/container

A lightweight PSR-11 dependency injection container built for the Flight PHP micro-framework, offering simple service binding, lazy resolution, and constructor injection without the overhead of heavier container libraries.

View on GitHub
Deep Wiki
Context7

Getting Started

This is flightphp/container — the PSR-11 container that ships with the Flight micro-framework, not an Illuminate component. Treat it as a small, standalone DI container you can drop into CLI tools, queue workers, package internals, or legacy code that doesn't need Laravel's full container. v1.2.0 is purely additive (singletons + fluent registration), so it's safe to pull in alongside a Laravel app.

composer require flightphp/container

Where to look first, in order:

  1. src/Container.php — the whole API is set(), get(), has(), and now singleton(). It's a short read; skim it before trusting the README.
  2. tests/ — the new singleton tests confirm identity semantics (===) and how objects are keyed.
  3. The GitHub compare link in the release (v1.1.0...v1.2.0) — the only behavioural change is that set()/singleton() now return $this instead of void.

First use case — one shared connection instead of six:

use flight\Container;

$container = new Container;
$container->singleton(PDO::class, fn() => new PDO('sqlite::memory:'));

$container->get(PDO::class) === $container->get(PDO::class); // true

If you're in a Laravel app and only need "one PDO / one HTTP client / one logger across a package", this is lighter than reaching for a full service provider + singleton() on the app container.

Implementation Patterns

Class-string singletons (autowired). Passing only a class name tells the container to resolve it once and cache it. This works cleanly for zero-arg or fully-resolvable constructors like DateTimeImmutable:

$container->singleton(DateTimeImmutable::class);
$container->get(DateTimeImmutable::class); // same instance every time

Object singletons for pre-built config. Register an already-configured instance; the container keys it by its class.

$logger = new Logger('app');
$container->singleton($logger);
$container->get(Logger::class) === $logger; // true

This is the pattern to reach for when construction needs runtime values (paths, DSNs, API keys) you don't want to re-derive inside a factory.

Fluent bootstrap. Since registration methods now return $this, the whole container can be built in one expression — handy in a bootstrap.php, a service provider's register(), or a package's setUp():

$container
    ->set(Cache::class, fn() => new ArrayCache())
    ->singleton(PDO::class, fn() => new PDO('sqlite::memory:'))
    ->singleton(HttpClient::class, fn() => new Client(['timeout' => 5]));

Keep it as one chain per layer (infrastructure → services) rather than one giant chain, so diffs stay readable.

Lazy shared factories. singleton($id, $closure) is the workhorse: the closure is deferred until first get(), then the result is memoised. Use this for connections you don't want opened on every request/command.

Bridging into Laravel. Two viable approaches:

// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->singleton(\flight\Container::class, function () {
        return (new \flight\Container)
            ->singleton(\PDO::class, fn() => DB::connection()->getPdo());
    });

    $this->app->bind(\Psr\Container\ContainerInterface::class, \flight\Container::class);
}

Or use it only inside a package you're writing, and expose the resolved services to Laravel via normal bindings. Don't hand flight\Container around as if it were app().

Test swapping. Because every registration is a closure slot, tests can re-register a fake before exercising the subject:

$container->set(Mailer::class, fn() => new FakeMailer);

Gotchas and Tips

  • singleton(Foo::class) is not magic. It still has to construct Foo once. If the constructor needs arguments, you get a resolution failure — supply a closure instead: singleton(Foo::class, fn() => new Foo($bar)). The DateTimeImmutable example works precisely because it's zero-arg.
  • Object singletons are keyed by get_class(), not by interfaces or arbitrary ids. singleton($pdo) only satisfies get(PDO::class). If callers ask for ConnectionInterface, register explicitly with set(ConnectionInterface::class, fn() => $pdo).
  • Last registration wins. Mixing set() and singleton() on the same id silently overwrites; there's no "already registered" guard. Audit chains when a service mysteriously behaves statelessly.
  • get() before singleton() is a silent trap. If something resolved the id earlier via set(), your later singleton() doesn't retroactively make that instance shared. Register singletons during bootstrap, not lazily mid-request.
  • Objects registered via singleton($object) capture state at registration time. If that object reads config in its constructor, config changes after registration won't be picked up — register late in the boot sequence.
  • set() with a non-closure-shaped factory returning a fresh object each call gives you a new instance per get(). That's the correct behaviour, but it's the usual cause of "why isn't my container caching?" — switch to singleton().
  • This is not Laravel's container. No contextual binding, no tagged(), no method injection into controllers, no app() global. Don't expect resolve()-style autowiring of Laravel contracts; anything you get() must have been set() or be trivially constructible.
  • Catch PSR-11 exceptions, not Laravel ones. get() on an unregistered id throws the PSR-11 not-found exception; use has() for feature detection rather than try/catch.
  • Debugging tip: temporarily wrap the factory to log every construction — set(Foo::class, function () use ($c) { logger('building Foo'); return new Foo; }). If you see two lines, something is bypassing the singleton registration.
  • Extension point: decoration is done manually — set(Foo::class, fn($c) => new DecoratedFoo($c->get(InnerFoo::class))) — so keep ids stable and avoid circular decoration.
  • Upgrade note: v1.2.0 changes the return type of set()/singleton() from void to self. Non-breaking for callers, but if you extend Container and override those methods with void, update your signatures.
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