Migrating from PHP-DI 4.x to 5.0

PHP-DI 5.0 is a new major version that comes with backward compatibility breaks.

This guide will help you migrate from a 4.x version to 5.0. It will only explain backward compatibility breaks, it will not present the new features (read the release notes or the blog post for that).

Package name

PHP-DI has moved:

While upgrading, you should change your composer.json to require php-di/php-di. Rest assured that you can still install older versions with mnapoli/php-di (backward compatibility is kept).

PHP version requirement

PHP-DI 5 requires a PHP version greater or equal than 5.4 (was previously 5.3).


DI\ContainerInterface was deprecated since v4.1 and has been removed. It was replaced by the standard Interop\Container\ContainerInterface.



Annotations are now disabled by default. If you use annotations, simply enable them in your config:

$builder = new ContainerBuilder();

$container = $builder->build();

And install the Composer dependency by running:

composer require doctrine/annotations

The DI\link() function helper has been deprecated (but still works). The reason for this is that when importing the function in PHP 5.6 with use function DI\link, it conflicts with PHP's native link() function. This was a silly oversight when designing PHP-DI 4, sorry about that :)

DI\get() has been introduced as a replacement and is preferred over DI\link() (which still works). It has the advantage of being shorter and more explicit:

return [
    EntityManager::class => DI\object(...),

    // Preferred version
    'entity_manager' => DI\get(EntityManager::class),
    // Deprecated but still works
    'entity_manager' => DI\link(EntityManager::class),

    // Example in an object definition:
    'MyClass' => DI\object()

If you are using PHP 5.6 you can opt for the shorter (and awesome) syntax:

use function DI\get;
use function DI\object;

return [
    'entity_manager'     => get(EntityManager::class),
    EntityManager::class => object()


Closures are now considered as factories automatically:

return [
    'foo' => DI\factory(function () {
        return new Foo();
    // can now be shortened to:
    'foo' => function () {
        return new Foo();

If you defined a closure as a value (e.g. to have the closure injected in a class), you need to wrap the closure with the new DI\value() helper:

    // the closure will be injected instead of being called
    'foo' => DI\value(function () {
        return new Foo();


The internal implementation of scopes has been simplified: this results in one less Composer dependency and better performances. Backward compatibility is kept so you don't have to change anything, however if you want you can replace the use of static methods (which might be deprecated in the future) with the constants.


return [
    'MyClass' => DI\object()
        ->scope(Scope::PROTOTYPE()), // static method


return [
    'MyClass' => DI\object()
        ->scope(Scope::PROTOTYPE), // constant

Again, this change is optional, the static methods still work.

Lazy injection

The ProxyManager package comes with a lot of dependencies and is only useful only for lazy injection. This package is not installed by default in v5.0 in order to lighten PHP-DI's dependencies.

If you want to use lazy injection, you now need to install it explicitly:

composer require "ocramius/proxy-manager:~1.0"

Read more in #198 or in the Lazy Injection documentation.


Caching library

The doctrine/cache library isn't required by PHP-DI by default anymore (in order to make the package lighter). If you set up a cache for PHP-DI, you need to require it:

composer require doctrine/cache

Caching and dynamic definitions

Note: this section might look complicated and confusing to you: it concerns a change for an edgy use case and you probably don't have to worry about this.

Caching works the same in PHP-DI 5, however it is no longer possible to add definitions to a container on the fly when using a cache:

$builder = new ContainerBuilder();
$builder->setDefinitionCache(new ApcCache());
$container = $builder->build();

// This still works: you can set values
$container->set('foo', 'hello');
$container->set('bar', new MyClass());

// This doesn't work anymore: you can't set definitions using ->set() when using a cache
$container->set('foo', DI\object('MyClass'));

The reason for this is that definitions are cached (not values). If you set a definition dynamically, then it will be cached, which could lead to very weird bugs (because dynamic definitions should of course not be cached since they areā€¦ dynamic).

The conclusion is: if you are using a cache, all you definitions should be static, i.e. you should add them to the ContainerBuilder:

$builder = new ContainerBuilder();
$builder->setDefinitionCache(new ApcCache());

// Good
    'foo' => DI\object('MyClass'),

Be reassured however that everything still works when you are not using a cache:

$container = ContainerBuilder::buildDevContainer();

// All of this still works
$container->set('foo', 'hello');
$container->set('bar', new MyClass());
$container->set('baz', DI\object('MyClass'));

Internal changes

If you were overriding or extending some internal classes of PHP-DI, be aware that they may have changed. For example definition sources have been refactored into a much simpler design.