layout | title | author | date |
---|---|---|---|
blogpost |
PHP-DI 5.0 released |
Matthieu Napoli |
June 10th 2015 |
I am very excited to announce that, after 8 months of work, PHP-DI 5.0 is released.
Note: if you have never heard of PHP-DI before, I recommend you visit the home page to get an overview of what PHP-DI can do for you.
This is a new major version that comes with:
- minor backward compatibility breaks (nothing to get too alarmed about)
- API and syntax simplifications (aka syntactic sugar)
- new major features for modular applications (aka bundles, modules, plugins, …)
- performance improvements (thanks Blackfire) and a much lighter package (from 10 to 3 Composer dependencies, less files, …)
- a new website, logo and rewritten documentation
- a Silex integration
- a demo application
But the best news of all is that this new major version wasn't produced out of thin air: it has been used in Piwik and developed as a rolling release (i.e. always stable and deployed) to test and evaluate new features hands on. Because of that, PHP-DI 5 has been in production since January 2015. More that 100 000 Piwik installs in the wild are running PHP-DI 5 already!
So let's start and have a look at what's new.
PHP-DI has been moved from my own GitHub account to a separate PHP-DI organization on GitHub. The project can now be found at github.com/PHP-DI/PHP-DI. Thankfully GitHub sets up redirects from the old URL so nothing bad should happen here.
The Composer package has also changed name: from mnapoli/php-di
to php-di/php-di
.
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-DI 5 doesn't break major features so you will not have a lot of work to upgrade. Here are the main changes:
- PHP 5.3 is no longer supported (5.4 or above is required)
- annotations are now disabled by default
- some optional dependencies are not installed by default anymore (ProxyManager for lazy injection, doctrine/annotations, doctrine/cache) -> 7 less Composer dependencies
DI\link()
is deprecated in favor of a newDI\get()
(but still works, so not really a BC break)
The complete list of changes are detailed extensively in the migration guide to 5.0.
PHP-DI wants to be the DI container for humans, simplicity of use is one of its most important feature. To improve that even more, a few things have been added.
Most of these changes have been explained in the previous blog article: Syntactic sugar in PHP-DI 5. Here is a reminder of the most important improvements:
<?php
use ...;
use function ...; // PHP 5.6 example
return [
'path.root' => __DIR__ . '/..',
// new DI\string() helper to write string expressions
'path.cache' => string('{path.root}/var/cache'),
// DI\factory() is now optional when using closures
PdfGenerator::class => function () {
return ...;
},
// array of services, yay!
'notification.handlers' => [
get(EmailHandler::class),
get(TextHandler::class),
get(PushHandler::class),
],
// nest definitions in other definitions!
Cache::class => object(JsonFileCache::class)
->constructor(string('{path.cache}/cache.json')),
];
The main motivation behind PHP-DI 5 was to improve support for scenarios involving several configuration files. The best illustration for this are application built using modules/bundles/plugins, which is exactly what was needed for Piwik and its plugin system.
Playing with lists is one of the most important feature. It involves defining array of services as well as adding new items to an existing array.
Let's illustrate that with an example: your application can support many "authentication providers". By default, you can sign up to the application and create an account which will be stored in a database (using an email and a password):
// application config file
return [
'auth.providers' => [
get(DatabaseAuthProvider::class),
],
];
However you can have modules that can provide new authentication systems:
// Facebook login module
return [
'auth.providers' => DI\add([
get(FacebookAuthProvider::class),
]),
];
// Twitter login module
return [
'auth.providers' => DI\add([
get(TwitterAuthProvider::class),
]),
];
Those modules can be registered by simply adding the configuration files:
$builder = new ContainerBuilder();
$builder->addDefinitions('app/config.php');
$builder->addDefinitions('src/FacebookModule/config.php');
$builder->addDefinitions('src/TwitterModule/config.php');
As a result getting the auth.providers
list will return the 3 items merged:
[
get(DatabaseAuthProvider::class),
get(FacebookAuthProvider::class),
get(TwitterAuthProvider::class),
]
For those familiar with Symfony, the same result can be achieved using tags. The approach chosen for PHP-DI is a little different for multiple reasons:
- adding an item to an array is more similar to what we do in vanilla PHP
- tags require to write compiler passes which are verbose and not trivial
- tags don't work if the container isn't compiled
While tags offer an approach with more freedom, manipulating lists instead of tags is simpler and feels more natural to use.
A new DI\decorate()
helper was added, allowing to decorate a previous entry using a closure. A common scenario for using this is to override object using the decorator pattern.
Here is an example where a module replaces the default "Product DAO" for by decorating it with a cache wrapper:
// application config
ProductDaoInterface::class => DI\object(ProductDaoMySQL::class)
// module config
ProductDaoInterface::class => DI\decorate(function ($previous, ContainerInterface $c) {
return new ProductDaoCached($previous);
})
The first argument of the closure is the previous object (the one we decorate), the second argument is the container.
The example above is equivalent to:
$dao = new ProductDaoCached(new ProductDaoMySQL());
A new framework integration comes with this new version: the Silex micro-framework. If you are interested to learn about it, head over to the documentation.
I hope you will like this new version, as well as the new website. If you want the complete list of changes, head over to the change log.
If you are migrating from a 4.x version, have a look at the detailed migration guide.
If something isn't right in the package or the documentation, please open an issue. You can also find support in the Gitter chatroom.