Skip to main content
Aikeedo uses a small autowiring container. Most classes never register anything: they declare what they need in the constructor and the container builds it.

Autowiring

Concrete classes are constructed recursively. Interfaces resolve when something has bound them, which module bootstrappers do during boot:

Injecting values by ID

Scalars and configuration come from the container by ID, using an attribute:
Always pair #[Inject('option.…')] with a nullable type and a default. Options that were never saved don’t resolve, and a non-nullable parameter then fails to construct the class.

Resolvers

Two resolvers extend the container with dynamic IDs.

Configuration

Anything starting with config. comes from the configuration object built at bootstrap:

Options

Anything starting with option. comes from the options table. Values are stored as JSON, flattened into a dot map, and cast on the way out:
Options are read once per request. If the database isn’t configured, resolution quietly returns nothing, which keeps the installer working.

Resolving imperatively

Where constructor injection isn’t possible, such as inside a plugin’s boot():
Application::make($id, $default) returns the default when the ID can’t be resolved, instead of throwing.
Prefer constructor injection. Static resolution hides dependencies and makes classes harder to test; use it only for bootstrap-time decisions.

Binding your own services

Bind an interface when callers should depend on the contract:
Bind an instance when it needs setting up before anything resolves it, such as passing configuration or registering entries:

Keyed registries

For extension points with many implementations, Aikeedo uses registries rather than container bindings, so implementations can be listed as well as resolved: Entries are stored as class names and resolved from the container on first use, so registering is cheap.

Lifetimes

Services resolved from the container are shared for the request. There’s no request-scoped or transient lifetime to configure: a fresh process handles each request, and everything is discarded at the end.