Attributes
The complete attribute surface. Scope attributes go on classes; injection attributes go on
properties and constructor parameters. All are final readonly.
At a glance
| Attribute | Target | Argument | Purpose |
|---|---|---|---|
#[Singleton] |
class | — | One shared instance per process. |
#[Request] |
class | — | One instance per request / coroutine. |
#[Transient] |
class | — | A new instance every resolution (the default). |
#[Autowired] |
property, parameter | — | Inject by the declared type. |
#[Inject] |
property, parameter | ?string $id = null |
Inject a specific class or named value. |
#[Lazy] |
property, parameter | — | Inject a deferred proxy, resolved on first use. |
Namespace: Flytachi\Winter\DI\Attribute\.
Scope attributes
Set the lifetime of a class. Read by DICollector during a scan, or by the container on first
make() when the class isn’t manually bound. A manual binding always overrides the
attribute. Full semantics on the Scopes page.
#[Singleton]
use Flytachi\Winter\DI\Attribute\Singleton;
#[Singleton]
class UserRepository
{
public function __construct(private DatabaseConnection $db) {}
}One instance per container (process) lifetime. Equivalent to $container->singleton(UserRepository::class).
Use for stateless services. Not for per-request state under Swoole — use #[Request].
#[Request]
use Flytachi\Winter\DI\Attribute\Request;
#[Request]
class AuthContext
{
private ?User $user = null;
}One instance per HTTP request / coroutine. Under Swoole it is isolated via
Coroutine::getContext(); under FPM / CLI it is equivalent to #[Singleton] (one process =
one request). Use for per-request state.
#[Transient]
use Flytachi\Winter\DI\Attribute\Transient;
#[Transient]
class QueryBuilder
{
private array $clauses = [];
}A new instance on every make() / injection. This is already the default when no attribute is
present — #[Transient] just makes the intent explicit.
Injection attributes
Mark where and how a dependency is supplied. On a property, injection runs
automatically after the constructor during make(); the property need not be public.
#[Autowired]
use Flytachi\Winter\DI\Attribute\Autowired;
class UserController
{
#[Autowired]
private UserService $service;
public function __construct(
#[Autowired] private CacheInterface $cache,
) {}
}Resolves the dependency by the declared PHP type. Equivalent to #[Inject] with no
argument — an explicit marker for by-type injection. An interface still needs a binding for
this to resolve.
#[Inject]
use Flytachi\Winter\DI\Attribute\Inject;
// #[Inject(?string $id = null)]Overrides autowiring for one parameter or property.
$id value |
Behaviour |
|---|---|
null (default) |
Resolve by the declared PHP type — same as #[Autowired]. |
FQCN string, e.g. #[Inject(FileCache::class)] |
Inject that specific class, bypassing the global binding for the declared type. |
named key, e.g. #[Inject('config.timeout')] |
Inject a scalar / pre-built value registered with Container::set(). |
class UserService
{
public function __construct(
private CacheInterface $primary, // global binding
#[Inject(FileCache::class)] private CacheInterface $fallback, // specific class
#[Inject('config.timeout')] private int $timeout, // named value
) {}
}#[Lazy]
use Flytachi\Winter\DI\Attribute\Lazy;
class SmsSendService
{
#[Lazy]
private FakeSendService $peer; // native PHP 8.4 proxy; resolved on first use
}Injects a native PHP 8.4 lazy proxy (ReflectionClass::newLazyProxy()) instead of resolving
immediately. The real instance is built from the container on first access — the mechanism for
breaking circular dependencies.
- Works on properties and constructor parameters; combinable with
#[Autowired]/#[Inject]. - The proxied type must be a concrete class. For an interface, name the concrete:
#[Inject(SmsSendService::class), Lazy]. A bare#[Lazy]on an interface or abstract class throwsContainerException. - The proxy is type-compatible with the real class and stays uninitialised until first use.
See Lazy proxies for the internals.
Precedence
For a single dependency, the container applies the first rule that matches:
- A
make()/call()override keyed by the parameter name. #[Inject]— explicit$id, or the declared type when$idisnull.#[Autowired]— the declared type.- Autowiring — the declared type, with the parameter’s default value as a fallback if the type can’t be resolved and a default exists.
#[Lazy] is orthogonal: it changes when the chosen dependency is built (deferred to first
use), not which one.