Attribute Injection#
InterMix supports PHP 8+ native attributes for expressive, declarative injection. Two families exist:
Built-in tags shipped with InterMix (
Infuse/Autowire/Inject)Custom attributes you register at runtime through
Infocyph\InterMix\DI\Attribute\AttributeRegistry::register
Quick syntax#
use Infocyph\InterMix\DI\Attribute\{Infuse, Autowire, Inject};
class Service {
#[Infuse] private LoggerInterface $logger; // by type
#[Autowire('cfg.debug')] private bool $debug; // by key
#[Inject(strtotime: '+1 day')] private int $expires; // via function
}
class App {
#[Infuse(user: 'admin')] // method-level default
public function boot(
#[Inject('cfg.env')] string $env // parameter-level override
) {}
}
Custom Attribute Support#
Create any attribute & a resolver that implements
Infocyph\InterMix\DI\Attribute\AttributeResolverInterface.
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
class UpperCase {
public function __construct(public string $text) {}
}
use Infocyph\InterMix\DI\Attribute\AttributeResolverInterface;
class UpperCaseResolver implements AttributeResolverInterface {
public function resolve(
object $attribute,
Reflector $target,
Container $c
): mixed {
return strtoupper($attribute->text);
}
}
// Register once during bootstrap
$c->attributeRegistry()->register(
UpperCase::class,
UpperCaseResolver::class
);
Usage:
class Banner {
#[UpperCase('hello world')]
public string $title;
}
echo $c->get(Banner::class)->title; // HELLO WORLD
Hint
Multiple attributes may decorate the same target.
InterMix calls each registered resolver in discovery order; the first
non-null, non-IMStdClass result becomes the injected value.
Later resolvers can still run side-effect logic even if they don’t inject.
Method & Parameter Injection#
class Mailer {
public function send(
#[Infuse('cfg.smtp')] array $config,
#[Inject] LoggerInterface $log
) {}
}
Whole-method defaults:
class Worker {
#[Autowire(retries: 2, delay: 5)]
public function execute(int $retries, int $delay) {}
}
Arguments provided via Infocyph\InterMix\DI\Container::call,
Infocyph\InterMix\DI\Managers\RegistrationManager::registerMethod
or explicit arrays always override attributes.
Property Injection#
Enable with propertyAttributes: true:
class Controller {
#[Infuse] private Request $request; // by type
#[Autowire('cfg.csrf')] private string $csrf; // by key
#[UpperCase('admin')] private string $role; // custom
}
Properties are injected after construction.
Values set via Infocyph\InterMix\DI\Managers\RegistrationManager::registerProperty
win over attributes.
Resolution Workflow#
Built-in tag (
Infuse/Autowire/Inject) – first match winsCustom attributes – executed in registration order:
if a resolver returns non-null & not ``IMStdClass`` → injected
if resolver returns
nullorIMStdClass→ treated as “logic-only”
Enabling Attributes#
$c->options()->setOptions(
injection: true,
methodAttributes: true, // enable #[Infuse] on params / methods
propertyAttributes: true // enable #[Infuse] on properties
);
You may enable only one flag to limit scope.
Resolution Priority (high → low)#
registerClass()/registerMethod()/registerProperty()Supplied args (
call(),make(), etc.)definitions()mapBuilt-in tags (Infuse / Autowire / Inject)
Custom attributes via AttributeRegistry
Examples#
Inject scalar config:
class Analytics {
#[Inject('cfg.api_key')] private string $apiKey;
}
Global callable:
class Session {
#[Infuse('uuid_create')] private string $sessionId;
}
Logic-only attribute (no injection):
#[Attribute(Attribute::TARGET_METHOD)]
class LogCall {
public function __construct(public string $level = 'info') {}
}
class LogCallResolver implements AttributeResolverInterface {
public function resolve(object $attr, Reflector $target, Container $c): mixed {
$c->get('logger')->log($attr->level, "[DI] $target handled");
return null; // no injection, marks as handled
}
}
Debugging#
$c->options()->enableDebugTracing(true);
$c->get(MyService::class);
print_r($c->debug(MyService::class));
Summary#
Built-in tags: Infuse / Autowire / Inject
Register unlimited custom attributes with resolvers
Works on properties, parameters, or whole methods
First non-null result wins; others may perform side-effects only
Fully traceable with
enableDebugTracing()
Next → Service Lifetimes