Definition Manager API#

$c->definitions() returns an instance-fluent manager that stores recipes (definitions) by ID. Everything that can be resolved by Infocyph\InterMix\DI\Container::get ultimately lives in this registry.

1. Binding values, classes & factories#

$def = $c->definitions();

// 💠 scalars / plain values
$def->bind('app.name',    'InterMix Demo');
$def->bind('answer',      42);

// 💠 class-string → auto–resolve on first get()
$def->bind('clock', DateTimeImmutable::class);

// 💠 factory closure
$def->bind('uuid', fn () => bin2hex(random_bytes(16)));

You may chain calls – the manager is fluent and ->end() brings you back to the container:

// Using method chaining (fluent interface)
$c->definitions()
    ->bind('foo', 123)
    ->bind('bar', 456)
    ->lock();  // lock the container after definitions are registered

// Using array access (via ManagerProxy)
$def = $c->definitions();
$def['baz'] = fn() => new SomeService();  // Same as bind()
$service = $def['baz'];  // Same as get()
$hasBaz = isset($def['baz']);  // Same as has()

2. Lifetimes (Singleton ⇢ Scoped ⇢ Transient)#

use Infocyph\InterMix\DI\Support\LifetimeEnum;

// default = Singleton
$def->bind('uniq', fn() => new stdClass());                 // same instance forever

// Transient – fresh each time
$def->bind('once', fn() => new stdClass(), LifetimeEnum::Transient);

// Scoped – unique per “scope” key
$def->bind('req', fn() => new stdClass(), LifetimeEnum::Scoped);

$obj1 = $c->get('req');
$c->enterScope('next-request');
$obj2 = $c->get('req');          // ⚠️ not equal to $obj1
$c->leaveScope();

Lifetimes apply equally to class-string bindings – InterMix transparently converts them into internal lazy initialisers.

4. Bulk import & sugar syntax#

Array import

$def->addDefinitions([
    'db.host'            => '127.0.0.1',
    LoggerInterface::class => FileLogger::class,   // interface ⇒ concrete
]);

Property / array / invoke sugar (handy for tests & prototyping) – available on both the container and all manager classes (DefinitionManager, OptionsManager, InvocationManager, RegistrationManager) thanks to the ManagerProxy trait:

$c->logger = fn () => new DummyLogger();          // property
$c['cfg']  = fn () => ['debug' => true];          // array access

$log = $c->logger;          // magic __get
$cfg = $c('cfg');           // __invoke

The same trait also proxies container methods via __call() (for example $def->get('foo') or $def->has('foo')), while preserving fluent manager chaining.

5. Lazy loading — opt-in or opt-out#

Definitions default to lazy placeholders (cheap objects holding a closure), resolved the first time you call get('service').

Toggle globally:

$c->options()->enableLazyLoading(false);   // eager – resolve immediately

User-supplied closures are not wrapped in DeferredInitializer. They execute when the ID is resolved (for example on first get() for singleton/scoped, or every get() for transient), not at bind-time.

6. Environment-aware bindings (quick reminder)#

Although technically part of Options & Feature Toggles, the Definition Manager plays nice with environment overrides declared in options() – when you bind(Interface::class, Concrete::class) the container substitutes the correct concrete based on the current environment at resolve-time.

What’s next?#

Need to register constructor parameters, method calls or properties? Head to Registration Manager. Want to see all manager calls in a cheat sheet? ― DI Cheat Sheet.