Package · di

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 new for wired services.
  • Explicit lifecycles — every service has a scope (singleton, request, transient) that decides whether it is shared or freshly built — and the request scope 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 implements ContainerInterface, 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 scopessingleton, request, transient, selectable by attribute or binding.
  • Property & method injection#[Autowired] / #[Inject] on properties; call() resolves the parameters of any callable.
  • Contextual bindingscontextual() 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 @Lazy of Spring).
  • Directory scanScanner + DICollector auto-discover annotated classes, with an optional production cache.
  • Circular dependency detection — a real cycle throws a clear ContainerException.
  • Swoole-saferequest scope uses Coroutine::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.0
  • ext-swooleoptional, only for request-scope coroutine isolation under the Swoole runtime

Install

bash
composer require flytachi/winter-di

Quick start

Mark a class with a scope, then resolve it — the container wires its dependencies for you:

php
use Flytachi\Winter\DI\Container;

$container = Container::init();

$service = $container->make(UserService::class);   // dependencies autowired by type

Walk it end to end — bootstrap, autodiscovery, and a first resolved service — in the Quickstart.

Continue with Installation & requirements, the Quickstart, and the Mental model.