Skip to content

PHP-DI

Developer UtilitiesDependency InjectionPHP

What it is

PHP-DI is a dependency injection container with autowiring, PHP-based configuration and support for the PSR-11 container interface.

The container resolves constructor dependencies by type hint. Definitions cover interfaces, scalars and factories where reflection is not enough.

Installation

composer require php-di/php-di

Getting started

The smallest useful thing you can do with it, and what each part means.

Autowiring and definitions
<?php
use function DI\{create, get, autowire, factory};

$builder = new DI\ContainerBuilder();
$builder->addDefinitions([
    // Interfaces need an explicit binding — reflection cannot guess.
    BookRepository::class => autowire(DoctrineBookRepository::class),

    // Scalars must be provided.
    'db.dsn' => 'pgsql:host=localhost;dbname=app',

    PDO::class => factory(fn ($c) => new PDO(
        $c->get('db.dsn'),
        options: [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
    )),
]);

$builder->enableCompilation(__DIR__ . '/var/cache');   // production
$container = $builder->build();

$service = $container->get(BookService::class);   // constructor autowired
Compilation is essential in production: without it PHP-DI reflects on every class on every request, which is measurable overhead.

Advanced usage

Where the library earns its place over a simpler alternative.

Decorators and environment-specific definitions
<?php
return [
    BookRepository::class => DI\decorate(function ($previous, $container) {
        return new CachingBookRepository($previous, $container->get(Cache::class));
    }),

    // Attribute injection for properties (must be enabled explicitly).
    // #[Inject] private LoggerInterface $logger;
];

// Layer environment overrides on top of the base definitions.
$builder->addDefinitions('config/common.php');
if ($env === 'test') {
    $builder->addDefinitions('config/test.php');   // later wins
}
Later definition files override earlier ones, which is how test doubles are substituted without touching the production configuration.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Entry cannot be resolved: parameter $x has no type
Autowiring needs type hints. Add one, or define the parameter explicitly.
Changes to definitions have no effect
Compiled definitions are cached. Clear the compilation directory after changing them.

Best practices

  • Enable compilation in production; reflection on every request is a real cost.
  • Bind interfaces to implementations explicitly — autowiring cannot resolve them.
  • Prefer constructor injection over attribute or property injection.
  • Type-hint Psr\Container\ContainerInterface if you must accept the container at all.

Background

Why it exists, and what it was reacting to.

PHP-DI made autowiring mainstream in PHP frameworks that lack their own container, using reflection on constructor type hints so most classes need no registration at all.