> For the complete documentation index, see [llms.txt](https://quantumphp.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://quantumphp.gitbook.io/docs/packages/di/contracts.md).

# Contracts

This page summarizes the runtime contracts exposed by the Di package.

## Registration contracts

### Concrete classes must be instantiable

`DiRegistry::register()` rejects a concrete class when either of these is true:

* the class does not exist
* the class exists but `ReflectionClass::isInstantiable()` returns `false`

That means interfaces, abstract classes, and traits cannot be used as concrete targets.

### Abstract keys must be real class-like names

When you register or set an abstract key explicitly, that key must already exist as a class or interface.

The package does not support arbitrary string service identifiers.

### Duplicate registrations are not replaced

A registry key can only be registered once. Re-registering the same abstract throws `DiException::dependencyAlreadyRegistered(...)`.

`registerDependencies()` is slightly softer: it silently skips keys that already exist.

## Instance contracts

### `has()` and `isRegistered()` mean different things

* `isRegistered($abstract)` checks whether a binding exists in `DiRegistry`
* `has($abstract)` checks whether a shared instance already exists in `DiContainer::$container`

A dependency can be registered without being instantiated, and an instance can exist only after resolution or `set()`.

### `set()` validates type compatibility

`set($abstract, $instance)` requires the instance to satisfy the abstract via `is_a($instance, $abstract)`.

If not, it throws `DiException::invalidAbstractDependency(...)`.

### `set()` does not overwrite existing instances

If the container already holds an instance for the abstract key, `set()` throws `DiException::dependencyAlreadyRegistered(...)`.

The `$override` parameter does not change this part of the behavior.

### `$override` only affects registry collisions

When `$override` is `false`, `set()` also refuses to insert an instance if the abstract already has a registry binding.

When `$override` is `true`, that registry collision check is skipped. This does not rewrite the stored binding in `DiRegistry`; it only seeds the runtime container with the supplied object. If you later call `resetContainer()`, that seeded instance is removed and the container falls back to the original registered concrete.

## Resolution contracts

### `get()` requires prior registration

`get()` throws `DiException::dependencyNotRegistered(...)` when the abstract is unknown to the registry.

Unlike `create()`, it does not auto-register the class.

### `create()` self-registers missing classes

`create()` calls `register($dependency)` when the dependency is not already registered.

That means plain instantiable classes can be created without an explicit boot-time registration step. It also means the binding stays in the registry after the fresh instance is returned, so a later `get($dependency)` call can reuse that self-registration and start returning a shared instance for the same class key.

### Shared caching is keyed by abstract name

When `get()` resolves `FooInterface::class` to `FooService::class`, the cached instance is stored under `FooInterface::class`, not under the concrete class name.

A separate abstract key pointing to the same concrete class would get its own cached instance unless you seed the container manually.

### Registered classes win over manual args

When a parameter type is registered, the resolver injects the shared dependency even if the caller supplied manual values in `$args`.

Manual arguments are only consumed after typed dependency resolution fails.

### Array-typed parameters receive all remaining args

For an `array` parameter, the resolver returns the remaining `$args` array as-is.

It does not shift one array element.

### Unresolvable parameters fall back to defaults or `null`

When the resolver cannot produce a value from typed dependencies or manual args, it returns:

1. the default value, if present
2. otherwise `null`

The package does not throw its own exception for missing required scalar values.

## Callable contracts

### `autowire()` returns arguments only

`autowire()` prepares an argument list and returns it. The caller is responsible for invoking the callable.

### Only closures and array callables are accepted

These are accepted for argument resolution:

* `function () {}`
* `[$service, 'handle']`
* `[Handler::class, 'run']`

Class-string array callables are only practical when the target method is static or when you use the reflected signature and then invoke the method yourself on an instance. The container does not instantiate the target object for you.

These are rejected by the current implementation:

* `'strlen'`
* invokable objects such as `new Handler()` with `__invoke()`

Rejected callable shapes throw `DiException::invalidCallable(...)`.

## Reset contracts

### `resetContainer()` preserves bindings

`resetContainer()` clears only cached shared instances and the active circular-dependency map.

Registered bindings remain available.

### `reset()` clears everything

`reset()` calls both `DiRegistry::reset()` and `resetContainer()`.

After `reset()`, the container behaves like a fresh empty container.

## Exception messages

The package defines dedicated messages for:

* unregistered dependencies
* duplicate dependencies
* non-instantiable concretes
* invalid abstract dependencies
* circular dependency chains
* invalid callable input

All package-level validation errors use `Quantum\Di\Exceptions\DiException`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://quantumphp.gitbook.io/docs/packages/di/contracts.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
