The reference dependency injection container for the Milpa PHP framework, built on
milpa/core. It implementsmilpa/core'sDIContainerInterfacewith reflection autowiring, lazy singleton resolution, and circular-dependency detection that reports the full chain — on top of a PSR-11 surface backed by Symfony'sContainerBuilder.
milpa/container is the concrete DIContainer behind milpa/core's
Milpa\Interfaces\Di\DIContainerInterface — the contract every Milpa component codes
against. No product coupling, no service-definition config format of its own: register
services by hand, or let the container find them by reflection.
composer require milpa/containerDIContainerInterface is deliberately conservative: it documents auto-resolution of
get()/has() as a MAY, not a MUST — a minimal, spec-conformant implementation is
allowed to throw/return false for anything not explicitly registered via
registerService(). resolve() is the one method whose contract is auto-wiring for
every implementation.
DIContainer exercises that MAY. Concretely, this implementation guarantees:
get()andhas()auto-resolve any existing, non-abstract, non-interface class whose constructor dependencies are themselves resolvable, recursively — not just identifiers registered viaregisterService().- Auto-resolved classes are cached as singletons on first resolution: later
get()calls for the same identifier return the same instance, unlessresolve()was called directly with$singleton = false. - Circular dependencies are detected and reported with the full chain —
AneedsBneedsA, directly or transitively, throwsCircularDependencyExceptionwith every class name in the cycle, not just a generic "circular dependency" message. tryGet()never throws — it returnsnullfor anything unregistered and unresolvable, instead of propagatingServiceNotFoundExceptionorContainerResolutionException.- Non-resolvable classes are cached as such (interfaces, abstract classes, classes with unresolvable constructor parameters), so repeated lookups don't re-run reflection.
If you need one of these guarantees, depend on DIContainer (or its documented behavior)
directly — DIContainerInterface alone does not promise them.
use Milpa\Container\DIContainer;
class Logger
{
public function log(string $message): void
{
echo $message . "\n";
}
}
class Greeter
{
public function __construct(private Logger $logger)
{
}
public function greet(string $name): void
{
$this->logger->log("Hello, {$name}!");
}
}
$container = new DIContainer();
// Auto-wiring: no registerService() call needed — Greeter's constructor
// dependency (Logger) is resolved recursively.
$greeter = $container->get(Greeter::class);
$greeter->greet('World'); // "Hello, World!"
// get() caches auto-resolved classes as singletons.
$container->get(Greeter::class) === $greeter; // true
// tryGet() never throws — null for anything unregistered/unresolvable.
$container->tryGet('Nonexistent\Service'); // null
// Explicit registration still wins over auto-wiring for the same id.
$container->registerService(Logger::class, new Logger());Circular dependencies fail loudly, with the chain that caused them:
class A { public function __construct(public B $b) {} }
class B { public function __construct(public A $a) {} }
$container->get(A::class);
// throws Milpa\Exceptions\CircularDependencyException:
// "Circular dependency detected while resolving: A -> B -> A."| Layer | Package | Owns |
|---|---|---|
| Contracts | milpa/core |
DIContainerInterface (extends PSR-11 ContainerInterface), ServiceNotFoundException, ContainerResolutionException, CircularDependencyException — the seam, not the engine. |
| Implementation | milpa/container (this package) |
The concrete DIContainer: reflection autowiring, singleton caching, circular-dependency detection, and safe (tryGet()) retrieval on top of Symfony's ContainerBuilder. |
| Your app | your host / plugins | Wiring decisions — which services to register explicitly vs. leave to autowiring, and when to call compileContainer(). |
- PHP ≥ 8.3
milpa/core^0.6psr/container^2.0symfony/dependency-injection^7.0
Full API reference: getmilpa.github.io/container — generated straight from the source DocBlocks and dressed with the Milpa design system.
Contributions are welcome — see CONTRIBUTING.md. Please report security issues via SECURITY.md, and note that this project follows a Code of Conduct.
Apache-2.0 © Rodrigo Vicente - TeamX Agency.
Milpa is designed, built, and maintained by Rodrigo Vicente - TeamX Agency.