Ridiculously simple dependency injection (DI) container for PHP.
- All services are singletons; each dependency is instantiated only once and shared (lazy-loading).
- Extremely lightweight: just 86 lines of code.
- Supports automatic constructor injection using type hints (autowiring).
- Fully compatible with the PSR-11 container interface.
A dependency injection container is a tool that automatically creates and manages the objects your application needs, wiring their dependencies together for you. It centralizes how services are configured and connected, making your code easier to maintain, test, and extend.
Without a container:
// You must manually create and pass every dependency, everywhere you need them.$logger = newFileLogger('/tmp/app.log'); $mailer = newMailer($logger); $userService = newUserService($logger, $mailer); $orderService = newOrderService($logger, $mailer); // If you need these services in other places, you have to repeat this process.// If you want to swap the logger implementation, you must update every place it's used.// Adding a new dependency or changing the wiring means editing code in many places.With a container:
// Configure how your objects should be instantiated.$config = [ LoggerInterface::class => FileLogger::class, FileLogger::class => function(){returnnewFileLogger('/tmp/app.log')} Mailer::class => function(ServiceContainer$container){returnnewMailer($container->get(LoggerInterface::class))} ]; $container = newServiceContainer($config); // The container will automatically inspect the constructor and inject its dependencies.$userService = $container->get(UserService::class); $orderService = $container->get(OrderService::class); // If you want to swap the logger implementation, just change the config in one place!Install using composer:
composer require wilaak/picodiRequires PHP 8.0 or above
Here's a basic usage example.
useWilaak\PicoDI\ServiceContainer; interface LoggerInterface{publicfunctionlog(string$message): void} class FileLogger implements LoggerInterface{publicfunction__construct(privatestring$file){} publicfunctionlog(string$message): void{file_put_contents($this->file, $message . PHP_EOL, FILE_APPEND)} } class UserService{publicfunction__construct(privateLoggerInterface$logger){} publicfunctioncreateUser(string$username): void{$this->logger->log("Created user: $username")} } $config = [ LoggerInterface::class => FileLogger::class, FileLogger::class => function(ServiceContainer$container){// You can use $container->get to resolve other dependencies if needed// For this example, we just instantiate FileLogger directlyreturnnewFileLogger('/tmp/app.log')} ]; $container = newServiceContainer($config); $userService = $container->get(UserService::class); $userService->createUser('alice');The configuration array specifies how the container resolves dependencies. The following key value types are supported:
Defines an alias for the class or interface specified in the key. You can use either the class name resolution operator or plain strings, but the ::class syntax is preferred for readability and IDE support.
$config = [ LoggerInterface::class => FileLogger::class, ];Use a callable (e.g anonymous functions) to explicitly return the desired value.
$config = [ LoggerInterface::class => function(ServiceContainer$container){$logger = newFileLogger(file: '/var/log/app.log'); // Optionally, inject other services from the container if needed:// $dependency = $container->get(OtherService::class);return$logger}, ];This container is based on the one from the article A Stupidly Simple PHP Dependency Injection Container from Evan Hildreth.