Di Usage

This package gives you two main workflows:

  • register a binding and resolve a shared instance with get()
  • create a fresh object graph on demand with create() or autowire()

Register an interface binding

Use register() when application code depends on an interface or abstract class name.

use App\Contracts\MailerInterface;
use App\Services\SmtpMailer;
use Quantum\Di\Di;

Di::register(SmtpMailer::class, MailerInterface::class);

$mailer = Di::get(MailerInterface::class);

get() returns the same shared instance on later calls for MailerInterface::class.

Create a class without pre-registration

Use create() when you want a fresh instance and do not need shared caching.

use App\Services\ReportBuilder;
use Quantum\Di\Di;

$builderA = Di::create(ReportBuilder::class);
$builderB = Di::create(ReportBuilder::class);

$builderA and $builderB are different objects.

Because create() auto-registers missing classes against themselves, this works even when ReportBuilder was not registered earlier. That self-registration persists in the container registry, so a later Di::get(ReportBuilder::class) call will succeed and start returning a shared instance for that class key.

Seed a prebuilt instance

Use set() when you already have an object and want the container to return it later.

use App\Contracts\ClockInterface;
use App\Testing\FakeClock;
use Quantum\Di\Di;

Di::set(ClockInterface::class, new FakeClock());

$clock = Di::get(ClockInterface::class);

The current implementation has two gotchas:

  • you cannot call set() twice for the same abstract once an instance already exists
  • the abstract key must be a real class or interface name

Also, set() seeds only the runtime container entry. If you clear the container with Di::resetContainer(), the seeded object is removed and future get() calls fall back to the registered concrete binding.

Autowire a callable

autowire() prepares the arguments for a closure or array callable.

use App\Contracts\MailerInterface;
use Quantum\Di\Di;

$args = Di::autowire(
    function (MailerInterface $mailer, string $subject = 'Hello') {
        return [$mailer, $subject];
    },
    ['Weekly digest']
);

The returned $args array contains the resolved mailer instance plus the manual subject argument.

You still invoke the callable yourself:

$result = (function (MailerInterface $mailer, string $subject = 'Hello') {
    return [$mailer, $subject];
})(...$args);

If you pass a class-string array callable such as [Handler::class, 'run'], autowire() only reflects the method signature. It does not create a Handler instance or perform the method call for you.

Understand argument precedence

Constructor and callable parameters are resolved in a strict order.

Registered services come first

If a parameter type is registered in the container, that dependency is injected before any manual argument is consumed.

Instantiable classes come next

If the type is not registered but names an instantiable class, the container creates it automatically.

Scalar values are positional

For non-class parameters, manual $args are consumed with array_shift().

$args = Di::autowire(
    function (string $name, int $count) {},
    ['cache', 3]
);

The resolver does not match by parameter name.

Array parameters capture the remaining args

If a parameter is typed as array, it receives the full remaining manual argument list.

$args = Di::autowire(
    function (array $payload) {},
    ['a' => 1, 'b' => 2]
);

In the current implementation, $payload becomes the remaining $args array exactly as held by the resolver.

Handle circular dependencies early

If class graphs reference each other in a loop, resolution fails with DiException::circularDependency(...).

Example shape:

  • A depends on B
  • B depends on A

The exception message includes the chain that was being resolved.

Reset state in tests or isolated runs

Use:

  • Di::resetContainer() to clear shared instances but keep bindings
  • Di::reset() to clear both bindings and shared instances

reset() is the stronger reset and is usually the safer option for test isolation.

Caveats

  • get() fails for unknown abstractions; it does not auto-register them.
  • autowire() does not accept string callables or invokable objects.
  • Missing required scalar parameters can fall through as null and fail later when PHP enforces the signature.
  • Shared instances are container-scoped. If the app context changes containers, later Di::... calls follow the new container.