Winter DI
Winter DI is a lightweight, PSR-11 dependency injection container for PHP. You declare what a class needs as typed constructor parameters, and the container builds the whole object graph for you — resolving each dependency by type, recursively, with a lifecycle scope you control.
Philosophy
- Type-driven autowiring — describe dependencies as typed parameters; the container
reads the types via reflection and constructs the full tree. You stop writing
newfor wired services. - Explicit lifecycles — every service has a scope (
singleton,request,transient) that decides whether it is shared or freshly built — and therequestscope is isolated per coroutine under Swoole. - Attribute-first configuration — classes declare their own scope and injection points
with attributes (
#[Singleton],#[Autowired], …). No central XML or config array. - Small and standard — one runtime dependency (
psr/container). It implementsContainerInterface, so it drops into any PSR-11 consumer.
Core concepts
- Container — the registry and resolver.
make()resolves a class,call()invokes a method with its parameters injected. - Scope — how long an instance lives:
singleton(one per process),request(one per request / coroutine),transient(a new one every time). - Attributes —
#[Singleton]/#[Request]/#[Transient]set a class’s scope;#[Autowired]/#[Inject]mark injection points;#[Lazy]defers resolution. - ServiceProvider — a class that groups related bindings (interface → implementation, factories, named values) in one place.
- Scanner — walks the project tree once and auto-registers annotated classes through
collectors like
DICollector.
Key features
- PSR-11 compliant — implements
ContainerInterface(get()/has()). - Constructor autowiring — parameters resolved automatically by type, recursively.
- Three scopes —
singleton,request,transient, selectable by attribute or binding. - Property & method injection —
#[Autowired]/#[Inject]on properties;call()resolves the parameters of any callable. - Contextual bindings —
contextual()factories receive the consuming class (e.g. a logger named after the class that uses it). - Lazy injection —
#[Lazy]injects a native PHP 8.4 proxy that resolves on first use, which breaks circular dependencies (the@Lazyof Spring). - Directory scan —
Scanner+DICollectorauto-discover annotated classes, with an optional production cache. - Circular dependency detection — a real cycle throws a clear
ContainerException. - Swoole-safe —
requestscope usesCoroutine::getContext()for per-coroutine isolation. - ReflectionCache — per-process cache for reflection objects, built once per worker.
Good fit for
Any PHP application that wants constructor autowiring and clear service lifetimes without a heavyweight framework container — HTTP controllers, CLI commands, and long-running Swoole workers where per-request isolation and cached reflection matter.
Requirements
- PHP ≥ 8.4 (the
#[Lazy]proxy uses native PHP 8.4 lazy objects) psr/container^2.0ext-swoole— optional, only forrequest-scope coroutine isolation under the Swoole runtime
Install
composer require flytachi/winter-diQuick start
Mark a class with a scope, then resolve it — the container wires its dependencies for you:
use Flytachi\Winter\DI\Container;
$container = Container::init();
$service = $container->make(UserService::class); // dependencies autowired by typeWalk it end to end — bootstrap, autodiscovery, and a first resolved service — in the Quickstart.
Source & links
- GitHub — github.com/flytachi/winter-di
- Packagist — packagist.org/packages/flytachi/winter-di
- Docs — winterframe.net/packages/di
Continue with Installation & requirements, the Quickstart, and the Mental model.