Registration Manager#
$c->registration() unlocks class-level metadata so you can steer
how InterMix builds an object without sprinkling the source code with
attributes.
Why register?
Add scalars / env values a constructor would otherwise not receive.
Call a bootstrap method right after instantiation.
Override private / static properties in legacy classes.
Disable autowiring entirely (
injection:false) and describe everything up-front.
The RegistrationManager (which uses the ManagerProxy trait) provides a fluent interface for class registration. You can also use array access for a more concise syntax.
Because it proxies container access, direct calls such as $reg->get(Foo::class) and sugar forms like $reg('id') / $reg['id'] also work.
Finish with ->end() to return to the container.
1 · registerClass( FQCN , array $args = [] )#
Pass positional or named arguments – just like PHP itself.
// Using method chaining (fluent interface)
$reg = $c->registration()
->registerClass(Db::class, [
'mysql:host=127.0.0.1;dbname=app', // position #1
'root', // position #2
'p@ssw0rd', // position #3
'flags' => [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION], // named
]); // stays on RegistrationManager
// Using array access (via ManagerProxy)
$reg[Db::class] = [ // Same as registerClass()
'mysql:host=127.0.0.1;dbname=app',
'root',
'p@ssw0rd',
'flags' => [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
];
$db = $c->get(Db::class);
Hints#
Un-listed parameters fall back to autowiring (if enabled) or
#[Infuse]attributes.The registration overrides anything the attribute would set for the same parameter.
2 · registerMethod( FQCN , string $method , array $args = [] )#
Schedules a post-construction call.
// EmailService::setConfig(array $cfg)
$c->registration()
->registerMethod(EmailService::class, 'setConfig', [
['smtp' => 'localhost', 'port' => 25], // first (and only) param
]);
When you later do:
$svc = $c->get(EmailService::class);
the container:
Builds
EmailServiceInjects the supplied parameters (plus Infuse fallbacks)
Executes
setConfig()Stores/returns the configured instance
Tip | You can omit $args to rely solely on #[Infuse] in the
method signature.
3 · registerProperty( FQCN , array $map )#
Set private, public, static or promoted properties without reflection gymnastics.
$c->registration()
->registerProperty(Configurable::class, [
'theme' => 'dark',
'staticValue' => 'GLOBAL',
]);
Precedence (highest → lowest):
registerProperty()
#[Infuse]on the property (if propertyAttributes = true)Do nothing (property remains untouched)
4 · import( ServiceProviderInterface::class )#
Service providers encapsulate a bundle of definitions / registrations.
When to use a provider#
You want to group related bindings into one reusable module.
You publish a package and need one entry-point for container setup.
You have feature-specific wiring (mail, queue, payments) you may enable/disable.
Why this pattern helps#
Keeps bootstrap code small.
Reduces repeated registration calls across commands/tests/apps.
Makes wiring easier to test and version.
interface LoggerInterface
{
public function log(string $message): void;
}
final class FileLogger implements LoggerInterface
{
public function __construct(private string $path = '/tmp/app.log') {}
public function log(string $message): void
{
file_put_contents($this->path, $message . PHP_EOL, FILE_APPEND);
}
}
final class Notifier
{
public function __construct(
private LoggerInterface $logger,
private string $channel
) {}
public function send(string $msg): void
{
$this->logger->log("[$this->channel] $msg");
}
}
final class FrameworkProvider implements ServiceProviderInterface
{
public function register(Container $c): void
{
// reusable IDs / factories
$c->definitions()->bind(LoggerInterface::class, FileLogger::class);
$c->definitions()->bind('notify.channel', 'ops');
// class-level constructor params
$c->registration()->registerClass(Notifier::class, [
'channel' => $c->get('notify.channel'),
]);
}
}
// class-string import
$c->registration()->import(FrameworkProvider::class);
// instance import is also supported
$c->registration()->import(new FrameworkProvider());
$notifier = $c->get(Notifier::class);
$notifier->send('deployment finished');
Providers are perfect for modules, packages or feature toggles.
Feature-specific wiring (step-by-step)#
Step 1: Define the feature contract and implementations.
interface PaymentGateway
{
public function charge(int $amount): string;
}
final class StripeGateway implements PaymentGateway
{
public function charge(int $amount): string { return "stripe:$amount"; }
}
final class PaypalGateway implements PaymentGateway
{
public function charge(int $amount): string { return "paypal:$amount"; }
}
final class CheckoutService
{
public function __construct(private PaymentGateway $gateway) {}
public function checkout(int $amount): string { return $this->gateway->charge($amount); }
}
Step 2: Create one provider per feature variant.
final class StripePaymentsProvider implements ServiceProviderInterface
{
public function register(Container $c): void
{
$c->definitions()->bind(PaymentGateway::class, StripeGateway::class);
}
}
final class PaypalPaymentsProvider implements ServiceProviderInterface
{
public function register(Container $c): void
{
$c->definitions()->bind(PaymentGateway::class, PaypalGateway::class);
}
}
Step 3: Choose and import the provider in bootstrap.
$provider = getenv('PAYMENT_DRIVER') === 'paypal'
? PaypalPaymentsProvider::class
: StripePaymentsProvider::class;
$c->registration()->import($provider);
Step 4: Resolve the feature service normally.
$checkout = $c->get(CheckoutService::class);
echo $checkout->checkout(1200);
This will now act as:
A feature boundary for payment wiring.
A single switch point for runtime variant selection.
A reusable module you can share across apps/tests.
5 · Working in “injection-less” mode#
Set injection:false to turn off reflection.
Every class must then be fully described via registration:
$c->options()->setOptions(injection:false);
$c->registration()
->registerClass(PlainOldClass::class, [123])
->registerMethod(PlainOldClass::class, 'init', [456])
->registerProperty(PlainOldClass::class, ['flag' => true]);
$val = $c->getReturn(PlainOldClass::class); // all good 🤝
Cheat-Sheet#
Call |
Purpose |
|---|---|
|
Constructor wiring |
|
Post-construction bootstrap |
|
Field overrides (private/static OK) |
|
Bulk registration via provider class |
See also : Definition Manager API for service IDs and Options & Feature Toggles to fine-tune autowiring, attributes, lazy loading, scopes, etc.