Dependencies in the open.
PSR-11 autowiring with explicit lifetimes and a fresh scope for every request.
Choose a lifetime
Transient services are constructed each time. Scoped services are shared only within a single request. Singleton services are shared at the application root and must be stateless or immutable. A singleton resolves through the root container, so it cannot capture a scoped service. Circular dependency chains fail with a diagnostic exception.
Register an interface
use Icom\Container\Container;
$app->container->bind(
InvoiceRepository::class,
SqlInvoiceRepository::class,
'scoped'
);
$app->container->bind(
Clock::class,
fn (Container $c) => new SystemClock(),
'singleton'
);Constructor injection
final class InvoiceController
{
public function __construct(
private readonly InvoiceRepository $invoices
) {}
public function show(string $id): ResponseInterface
{
return Response::json($this->invoices->find($id));
}
}Request cleanup
The application clears its child container in a finally block. Scoped services implementing Icom\Contracts\Resettable receive reset() before references are released. The database binding rolls back unfinished transactions. This cannot reset arbitrary static properties, closures capturing mutable objects, or third-party globals. Middleware objects registered directly must be stateless; register a middleware class as scoped if it owns request state.
Explicit limits
Intersection/union-type inference, scalar configuration injection, contextual bindings and compiled container generation are not included. Use a factory for those dependencies. Long-running applications must never store a request, user identity or mutable per-request data in singleton services.