icom/ framework

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

example
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

example
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.

Developer preview — benchmark claims require reproducible evidence.

Explore the architecture