Scopes#
A scope is a label that groups together all services registered with the
LifetimeEnum::Scoped lifetime.
Inside the same scope, a scoped service behaves like a singleton; change the
label and you get a fresh instance.
Typical use-cases#
HTTP request ID β isolate per-request state or caches.
CLI job / queue worker β reuse expensive objects during the job but not across jobs.
Fiber / coroutine β give each fiber its own contextual dependencies.
Multi-tenant apps β tag each tenant with their customer ID.
API#
$c->enterScope('req-123'); // β enter / switch scope
// ... resolve scoped services ...
$c->leaveScope(); // β‘ leave and restore previous scope
Switching scope never clears non-scoped singletons; only services bound with
LifetimeEnum::Scoped are affected.
Example π°#
use Infocyph\InterMix\DI\Support\LifetimeEnum;
$def->bind('user.ctx', fn () => new StdClass, LifetimeEnum::Scoped);
// ββ Request #1 ββββββββββββββββββββββββββββββββ
$c->enterScope('req-A');
$a1 = $c->get('user.ctx'); // instance #1
$a2 = $c->get('user.ctx'); // same object (cached)
$c->leaveScope();
// ββ Request #2 ββββββββββββββββββββββββββββββββ
$c->enterScope('req-B');
$b1 = $c->get('user.ctx'); // new instance (instance #2)
$c->leaveScope();
assert($a1 !== $b1);
Scope helpers#
If you need a temporary scope:
$c->withinScope('cli-batch-42', function () use ($c) {
$svc = $c->get('user.ctx'); // scoped inside the closure
});
// scope automatically restored
( withinScope enters the scope, runs your callback, then always restores. )
Best practices π‘#
Keep scopes short-lived β usually the lifetime of a single request or job.
Avoid cross-scope leakage β pass IDs or DTOs between scopes, not the scoped objects themselves.
Combine with Lazy-Loading β scoped services are still initialised on first access unless eager-loaded.