Tutoriel Vidéo PHP PHP-DI, Conteneur d'injecteur de dépendance

Télécharger la vidéo

Lorsque l'on conçoit une application complexe en utilisant la programmation orientée objet en PHP on est souvent amené à faire communiquer plusieurs objets ensembles à travers un système d'injection de dépendance.

class Table
{

    protected $database;

    public function __construct(Database $database)
    {
        $this->database = $database;
    }

    // ...
}

Les classes deviennent alors dépendantes les unes des autres rendant la phase d'initialisation un peu verbeuse.

$table = new Table(new Database(new PDO('mysql:dbname=demo;host=localhost', 'root', 'root')));

Pour solutionner ce problème on peut utiliser un conteneur d'injection de dépendance qui va garder en mémoire les "recettes" qui vont servir à initialiser les objets. Aujourd'hui on va découvrir un de ses container : PHP-DI

Autowiring

Par défaut, PHP-DI est capable d'utiliser le Type-hinting de PHP afin d'instancier de manière automatique les classes au besoin.

class Bar {
}

class Foo
{
    private $bar;

    public function __construct(Bar $bar)
    {
        $this->bar = $bar;
    }
}

$containerBuilder = new \DI\ContainerBuilder();
$containerBuilder->useAutowiring(true);
$container = $containerBuilder->build();
// On récupère une instance (singleton) de Foo
$container->get(Foo::class);

Cet autowiring permet de couvrir la grande majorité des cas mais il est aussi possible de définir les dépendances

class Controller {

  private $view;

  public function __construct(ViewInterface $view) {
    $this->view = $view;
  }

  public function render(string $name, ?array $params): string {
    return $this->view->render($name, $params);
  }

}

Dans ce cas là, PHP-DI ne sera pas capable de trouver automatiquement la classe à utiliser pour construire cet objet. On pourra, à travers un fichier de configuration, indiquer à PHP-DI la méthode à utiliser pour initialiser notre objet.

return [
  Controller::class => object()->constructor(get(TwigView::class))
]

Mais on pourra aussi choisir de lui indiquer quelle implémentation utiliser pour une interface.

return [
  ViewInterface::class => object(TwigView::class)
]

Dans ce cas là l'autowiring injectera une instance de TwigView lorsque les objets demandent une instance d'une classe qui implémente la ViewInterface. Pour en savoir plus sur les options disponibles au niveau du fichier de définition est disponible sur la documentation.

Des annotations

Enfin, en plus du système d'autowiring et du système de définition, il est aussi possible d'utiliser des annotations pour gérer l'injection. On commencera dans ce cas là par installer la librairie d'annotation.

composer require doctrine/annotations

Puis on activera les annotations au niveau de notre container.

$containerBuilder->useAnnotations(true);

Il sera ensuite possible d'injecter une propriété dans une classe.

class Example
{
    /**
     * Annotation et PHPDoc fonctionne ensemble.
     *
     * @Inject
     * @var Database
     */
    private $database;

}

Intégration dans les frameworks

Le container de PHP-DI implémente l'interfaceContainer du PSR-11 (encore au stade de review) et peut donc s'intégrer "facilement" à différents frameworks comme Symfony, Slim ou Zend.