Retour sur le Forum PHP 2024
Découvrez un résumé concis des conférences qui nous ont le plus marqué lors du Forum PHP 2024 !
La documentation de Symfony2 explique très bien ce que c'est qu'un service et le conteneur de dépendances. Mais dans une application on a souvent besoin d'injecter plusieurs services ou paramètres. Vous vous êtes déjà dit que c'était plus simple d'injecter le container de service directement plutôt que d'injecter toutes les dépendances les unes après les autres ? Voici les "pour" et les "contre".
Tout d'abord, quand un service ressemble à ça,
class MyService
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
// ....
}
... en lisant le constructeur on ne sait pas quelles sont les dépendances du service. On serait obligé d'aller lire toute la classe pour savoir de quoi elle a réellement besoin.
Deuxièmement, il faudra utiliser le conteneur de service dans les tests, ou avoir un mock du conteneur si on l'injecte. Par défaut, il ne contiendra pas de services, donc il faudra quand même mocker ceux utilisés par notre service. Il faudra donc lire toute la classe pour ne pas oublier des dépendances. Ce qui impose d'écrire du code (inutile) en plus, tout en rendant les tests plus compliqués et longs à écrire... et à maintenir.
Aussi, si on demande un service particulier au conteneur à plusieurs endroits, on n'est plus aussi découplé du reste du code que ce qu'on devrait. On est couplé avec le DIC au lieu de la classe directement, mais cela va toujours à l’encontre du principe d'injection de dépendances.
Il existe quand même des cas où injecter le conteneur de services directement peut être utile : quand vous avez un service dans un scope (champ d'application) "container" qui a besoin d'un service du scope "request". Exemple, une extension Twig qui aurait besoin de Request pour récupérer une URL.
Les extensions Twig doivent être dans le scope "container" puisqu'elle dépendent de Twig qui est dans ce scope. Mais injecter Request directement demande que le scope soit réduit à "request". Pour résoudre ce problème, on va injecter le service container dans l'extension et vérifier si une request est disponible :
class MyExtension extends \Twig_Extension
{
private $container;
private $request;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
if ($this->container->isScopeActive('request')) {
$this->request = $this->container->get('request');
}
}
// ...
}
Note : les extensions Twig ne bénéficient pas du lazy loading, et sont donc toujours chargées, même si vous lancez une commande. Puisqu’il n'y a pas de Request dans une commande, nous avons besoin de vérifier son existence avant d’essayer d'y accéder.
EDIT : Depuis symfony 2.4, un nouveau scope, "request_stack", a été introduit (cf l'article de Fabien Potencier). Le problème décrit ci-dessus ne se pose donc plus.
Auteur(s)
Marie Minasyan
Astronaute Raccoon @ ElevenLabs_🚀 De retour dans la Galaxie.
Vous souhaitez en savoir plus sur le sujet ?
Organisons un échange !
Notre équipe d'experts répond à toutes vos questions.
Nous contacterDécouvrez nos autres contenus dans le même thème
Découvrez un résumé concis des conférences qui nous ont le plus marqué lors du Forum PHP 2024 !
Le composant Symfony ExpressionLanguage : qu'est-ce que c'est ? Quand et comment l'utiliser ? Comment créer des expressions lors de cas plus complexes ?
Découvrez comment réaliser du typage générique en PHP : introduction et définition du concept, conseils et explications pas-à-pas de cas pratique.