My C-Sense (myclabs), Lyon
Container d'injection de dépendances
depuis Mars 2012
Construire son code pour ne plus créer ses dépendances
=> méthode
Construit des objets et injecte les dépendances
=> outil
Application :
$foo = new FooController();
$foo->loginAction();
Classe FooController
:
public function loginAction() {
$bar = new Bar(); // ou $bar = Bar::getInstance()
$bar->doSomething();
}
Classe Bar
:
public function doSomething() {
$bim = new Bim(); // ou $bim = Bim::getInstance()
$bim->doSomethingElse();
}
=> dépendances choisies lors de l'écriture du code (hard-coded)
Avant :
class FooController {
public function loginAction() {
$bar = new Bar(); // ou $bar = Bar::getInstance()
$bar->doSomething();
}
}
Après :
class FooController {
private $bar;
public function __construct(Bar $bar) {
$this->bar = $bar;
}
public function loginAction() {
$this->bar->doSomething();
}
}
Application :
$bim = new Bim();
$bar = new Bar($bim);
$foo = new FooController($bar);
$foo->loginAction();
Classe FooController
:
public function loginAction() {
$this->bar->doSomething();
}
Classe Bar
:
public function doSomething() {
$this->bim->doSomethingElse();
}
=> dépendances choisies lors de l'exécution
code against interfaces
class StoreService {
public function __construct(GeolocationService $geolocationService) { … }
}
interface GeolocationService {
public function getCoordinates($address);
}
class GoogleMaps implements GeolocationService { … }
class OpenStreetMap implements GeolocationService { … }
Nécessite de gérer les dépendances dans l'application
$bim = new Bim();
$bar = new Bar($bim);
$foo = new FooController($bar);
Application :
$foo = $container->get('FooController');
$foo->doSomething();
Classe FooController
:
public function loginAction() {
$this->bar->doSomething();
}
Classe Bar
:
public function doSomething() {
$this->bim->doSomethingElse();
}
$foo = $container->get('FooController');
=
$bim = new Bim();
$bar = new Bar($bim);
$foo = new FooController($bar);
Le container fait les injections, et aide à construire les graphes d'objets
$foo = $container->get('FooController');
Service Locator : anti-pattern
Nécessite de configurer le container pour qu'il injecte les bons objets
Chaque container se configure différement
$container = new Container();
$bim = new Bim();
$bar = new Bar($bim);
$container->set('Bim', $bim);
$container->set('Bar', $bar);
Problème : initialise tous les objets à chaque requête
$container = new Container();
$container->set('Bim', function() {
return new Bim();
});
$container->set('Bar', function() use ($container) {
return new Bar($container->get('Bim');
});
Verbeux, mais efficace
Formats différents pour chaque librairie
Résolution de dépendances intelligente
class Foo {
public function __construct(Bar $param1) {
}
}
~ 90% des cas -> 0 configuration
Inspiré de Java/Spring
use DI\Annotation\Inject;
class UserController {
/**
* @Inject
* @var UserRepository
*/
private $userRepository;
public function loginAction($email, $password) {
$user = $this->userRepository->login($email, $password);
// ...
}
}
Pas la solution à tout, mais très pratique dans les contrôleurs
$container->set('Bar');
$container->set('Foo')
->withConstructor(['Bar']);
$container->set('My\Interface')
->bindTo('My\Implementation');
Bar:
Foo:
constructor: [ Bar ]
My\Interface:
class: My\Implementation
ContainerInterface
,
chainage de containers…