[fr] injection de dépendances, containers & php-di
DESCRIPTION
[French] Présentation du principe d'injection de dépendances, des containers et de PHP-DI.TRANSCRIPT
INJECTION DEDÉPENDANCES,
CONTAINERS & PHP-DI
BONJOURMATTHIEU NAPOLI
My C-Sense ( ), Lyonmyclabs
/ mnapoli.fr @matthieunapoli
github.com/mnapoli
PHP-DIContainer d'injection de dépendances
depuis Mars 2012
DI ≠ DIC
INJECTION DE DÉPENDANCESConstruire son code pour ne plus créer ses dépendances
=> méthode
CONTAINERConstruit des objets et injecte les dépendances
=> outil
INJECTION DEDÉPENDANCES
WORKFLOW D'UN CODE CLASSIQUEApplication :
$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();}
WORKFLOW D'UN CODE CLASSIQUE=> dépendances choisies lors de l'écriture du code (hard-coded)
Tests : tester du code qui utilise un service web, des fichiers, uneBDD, du cache, …Extensibilité : mon système de log loggue dans un fichier, j'aimeraiqu'il écrive en BDD et les envoie par email…Coupling : je veux changer de système de cache (ou sa config) -> jedois changer le code partout…
TRANSFORMATION
TRANSFORMATIONAvant :
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(); }}
WORKFLOW D'UN CODE UTILISANT L'INJECTIONDE DÉPENDANCES
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();}
INVERSION OF CONTROL=> dépendances choisies lors de l'exécution
Possibilité de remplacer les dépendances injectéesDans les classes, on ne gère plus les dépendances, leursconfigurations…Code plus générique :
code against interfaces
CODE AGAINST INTERFACESclass StoreService {
public function __construct(GeolocationService $geolocationService) { … }
}
interface GeolocationService {
public function getCoordinates($address);
}
class GoogleMaps implements GeolocationService { … }
class OpenStreetMap implements GeolocationService { … }
MAISNécessite de gérer les dépendances dans l'application
$bim = new Bim();$bar = new Bar($bim);$foo = new FooController($bar);
CONTAINER
WORKFLOW D'UN CODE UTILISANT UNCONTAINER
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
ATTENTION !$foo = $container->get('FooController');
Service Locator : anti-pattern
CONFIGURATION DU CONTAINERNécessite de configurer le container pour qu'il injecte les bons objets
Chaque container se configure différement
EXEMPLE SIMPLE$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
CALLBACKS$container = new Container();
$container->set('Bim', function() { return new Bim();});$container->set('Bar', function() use ($container) { return new Bar($bim);});
Verbeux, mais efficace
FICHIERS DE CONFIGURATIONTableau PHP
YAML
XML
…
Formats différents pour chaque librairie
Symfony DI
Zend\Di (+ tous les autres)
Pimple
Aura DI
Laravel
Mouf
Orno
PPI
…
PHP-DI
POURQUOI ?PratiqueVolontairement copieurFramework-agnostic
PHP-DIv1.0 (août 2012) : injections via annotations (PC)
v2.0 (décembre 2012) : refactoring, version stable
v3.0 (avril 2013) : annotations, config PHP, YAML, …
…
v3.5 (octobre 2013)
REFLECTIONRésolution de dépendances intelligente
class Foo { public function __construct(Bar $param1) { }}
~ 90% des cas -> 0 configuration
ANNOTATIONSInspiré de Java/Spring
use DI\Annotation\Inject;
class UserController { /** * @Inject */ 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
CONFIGURATION PHP$container->set('Bar');
$container->set('Foo') ->withConstructor(array('Bar'));
$container->set('My\Interface') ->bindTo('My\Implementation');
YAMLBar:
Foo: constructor: [ Bar ]
My\Interface: class: My\Implementation
FUTURPHP-DI : v4.0, intégration avec d'autres frameworks
: collaboration pour une PSR ContainerInterfaceCommunauté : diffusion, meilleure doc et best-practices dans ZF2
et Symfony 2…
PHP-FIG
MERCI
Site officiel :
GitHub :
Twitter :
php-di.org
mnapoli/PHP-DI
@PHPDI