Package · di

Scopes

A scope defines how long a resolved instance lives and whether it is shared. There are three: singleton, transient, and request. A class picks one via attribute or binding; the default is transient.

The three scopes

Scope Identity Cached Set by
singleton one per process yes, after first make() #[Singleton] · singleton()
transient new every resolution no #[Transient] · bind() · transient() · (default)
request one per request / coroutine per request #[Request] · request()

singleton

Created on the first make(), cached in the container, returned on every later call.

Use for stateless services — repositories, factories, connection pools, config readers. Avoid for classes holding per-request state; that leaks across requests under Swoole.

transient

No caching — every make() / injection returns a fresh object. This is the default when a class has no scope attribute and no manual binding.

Use for stateful, non-shared objects — query builders, form objects, DTOs, units of work.

request

One instance per HTTP request, keyed by runtime:

Runtime Behaviour
Swoole Stored in Coroutine::getContext()['__di'] — fully isolated per coroutine. Each concurrent request gets its own instance, cleaned up when the coroutine ends.
FPM Equivalent to singleton — one request = one process, so the process-level cache is correct.
CLI Equivalent to singleton — one command = one process.

Use for per-request state — auth context, current user, unit of work, request-bound counters.

The request scope is the one place ext-swoole matters; see Request scope & Swoole.

Precedence

A manual registration always overrides a class attribute — the last binding wins.

php
#[Singleton]
class UserService {}

// Override to transient for a specific context (e.g. tests)
$c->transient(UserService::class);
// → UserService is now transient regardless of #[Singleton]

Overrides skip the cache entirely

make($abstract, $overrides) with a non-empty $overrides array always builds a fresh instance and neither reads nor writes the singleton/request cache — regardless of scope. Use make() with no overrides to get the shared instance.

Swoole safety matrix

Never place mutable per-request data in a singleton under Swoole — it leaks across concurrent requests sharing the worker’s memory.

Class type Recommended scope
DB connection pool singleton
Repository (stateless) singleton
Config reader singleton
Auth context request
Current user request
Unit of work request
Query builder transient
DTO / value object transient
Form object transient