Symfony ExpressionLanguage : Comment utiliser ce composant ?
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 ?
Symfony 2 c'est plusieurs composants -dont le domaine d'application est spécifique- qui forment les parpaings d'une maison ; pour assembler tout ça, un autre composant existe, à la fois le parpaing et le ciment : l'EventDispatcher. Son rôle est de distribuer des événements qui seront traités par les divers composants.
Il ne s'agit pas dans cet article de revenir sur le fonctionnement de l'EventDispatcher, mais d'expliquer le rôle d'un événement mal connu, l'event "kernel.terminate".
Sachez tout d'abord qu'aucun code n'est exposé ici, je vous laisse cette démarche. D'autre part, si l'exemple est pris avec Symfony (qui simplifie le problème), ce n'est pas Symfony qui permet ce que nous allons étudier ici, mais l'implémentation du serveur PHP.
Avant de rentrer dans le vif du sujet, profitons-en pour rappeler les différents types d'événements kernel.* qui sont dispatchés. Bien entendu, une liste exhaustive et bien plus complète existe en anglais dans la documentation officielle (et une version très détaillée). Dans leur ordre d'apparition dans le cycle d'une requête HTTP jusqu'à sa réponse.
Si vous ouvrez votre arborescence Symfony 2, et plus particulièrement le fichier web/app.php -à savoir le point d'entrée de votre application- vous verrez très peu de lignes :
Deux étapes exposées précédemment devraient vous interroger. Je vous laisse réfléchir desquelles il s'agit 2 minutes, pendant que je mets un petit gif de chat.
[
Alors, trouvé ? Une fois la réponse envoyée ($response->send();) l'exécution du processus de votre serveur HTTP devrait se terminer étant donné que la réponse à été envoyée et probablement reçue par le client. Pourtant, on a de nouveau une instruction ensuite.
La vérité est un peu plus complexe en fait. Il existe plusieurs implémentations du serveur PHP. Comme je suis un peu oldschool, j'utilise toujours Apache, qui pour moi a toujours fait l'affaire dans mes projets personnels. D'autant plus que mon travail chez les clients n'est pas de m'occuper de l'administration système des serveurs. Dans le cadre d'Apache il a existé et existe encore la librairie mod_php. Le problème de celle-ci est de terminer l'exécution du processus une fois que la réponse HTTP a été renvoyée. Mais il existe une autre implémentation : PHP-FPM. Je ne prétends pas faire un article orienté admin', étant donné que ce n'est pas mon domaine. Mais pour simplifier, la plupart des handler PHP pour HTTP actuels, peuvent renvoyer une réponse HTTP puis continuer le traitement du script PHP impliqué (sauf mod_php sur Apache). Et ça, ça peut être très utile, d'où l'event kernel.terminate.
Cet événement est dispatché lorsque la réponse HTTP à été transmise au client. L'intérêt étant de pouvoir effectuer des traitements coûteux en temps. Si ces traitements étaient fait en amont de l'envoi de la réponse, l'utilisateur le ressentirait sur le délai de réponse de la page. Et à mon sens, cet événement est bien souvent sous-estimé.
Vous ne voyez toujours pas l'intérêt ? Un petit exemple pour vous l'expliquer pourrait vous aider à comprendre.
Prenons le cas suivant. Vous gérez un service d'upload de photos qui ajoute des filtres (comme sur Snapchat ou Instagram). Pour ces photos vous fournissez des liens à intégrer sur les différents supports numériques (sites, forums, réseaux sociaux, etc.). Vous gérez toute cette manipulation d'image en PHP via une surcouche Symfony ; ce n'est peut-être pas la meilleure solution, mais nous prendrons cet exemple. Naturellement, vous seriez obligés de passer par l'une des extensions PHP que sont GD ou ImageMagick pour manipuler les images.
Une première idée pourrait être de faire ce traitement dans votre contrôleur. Puis une fois celui-ci fait, retourner la réponse HTTP avec l'image et les liens. Admettons que ce traitement d'image nécessite 20 secondes. Cela implique à vos utilisateurs de s'impatienter pendant 20 longues secondes avec une page blanche. Vous même développeurs ne supportez pas d'attendre tout ce temps.
Alors pourquoi ne pas duper l'utilisateur ? 20 secondes pour une tâche informatique c'est assez long, ça l'est également pour l'utilisateur devant une page blanche. Pourquoi ne pas aborder le problème dans l'autre sens, donner gratification à l'utilisateur en lui affichant une page rapidement, tout en pariant sur sa non-réactivité. À la place de lui afficher directement l'image et tous les liens, pourquoi ne pas afficher que les liens et parier sur la non-réactivité de l'utilisateur. Un utilisateur c'est lent, très lent. Pourquoi ne pas lui donner le Kinder Surprise, sans le jouet dans la boîte jaune parce que vous n'aviez pas fini de le fabriquer. Et pendant qu'il mange tranquillement son Kinder, pourquoi ne pas profiter de son inattention pour insérer le jouet dans la boîte jaune une fois fabriqué ? Le principe est le même ici.
Ce pari, il a été pris par de nombreuses entreprises, qui anticipent vos déplacements sur leurs sites pour vous faire croire que celui-ci est fluide et réactif, et pour cacher certaines lenteurs dû à des processus parfois complexes. Et ça marche plutôt bien, alors pourquoi ne pas en profiter dans vos projets ? Pour plus d'informations, je vous renvoie une fois de plus à la documentation officielle pour cet événement.
Dans des grosses structures, telles que LinkedIn par exemple, on préférera utiliser des solutions asynchrones qui passent par des queues manager (exemple : RabbitMQ, Kafka, etc.) qui sont beaucoup plus scalables sur de larges architectures. Mais dans des projets de petite à moyenne envergure, dans les PME notamment, il n'est pas toujours simple de mettre en place ces solutions qui répondent à des problématiques de grande envergure. Ce serait comme pêcher du poisson avec un lance-roquettes.
Or ici, dans de plus petits projets, le fait de jouer avec cette notion de kernel.terminate prend tout son sens. De plus, il est très simple à mettre en place avec Symfony, il suffit de créer un listener ou un subscriber dessus.
Il y a des cas où l'événement ne peut pas être utilisé, il cible des problèmes particuliers. En effet parfois, vous aurez besoin d'attendre que votre traitement coûteux soit terminé pour fournir une réponse HTTP, car celle-ci dépend de votre traitement. Dans ce cas vous ne pourrez pas utiliser cet événement. Ce sera à vous donc d'optimiser au mieux votre algorithme, pour qu'il prenne le moins de temps possible dans votre contrôleur ; et ainsi retourner une réponse dans les meilleurs délais.
Notez aussi que si vous tournez sur Apache + mod_php, toute la logique liée à l'événement sera quand même exécutée, mais avant d'envoyer la réponse.
PHP n'est pas le langage le plus rapide du monde, mais il s'en sort quand même plutôt bien avec ce genre de petites optimisations, pour peu qu'on sache l'appréhender. Avec Symfony, cela rajoute un peu de complexité pour configurer l'optimisation aux petits oignons, mais c'est du devoir du développeur d'anticiper ces problèmes. J'ai pris l'exemple ici avec Symfony qui mâche le travail avec l'event kernel.terminate, mais sachez que c'est possible en PHP natif comme je le disais grâce à la fonction fastcgi_finish_request.
N'hésitez pas à faire un retour dans les commentaires, si vous constatez des inexactitudes, des améliorations à apporter, ou simplement si vous avez des questions sur des points un peu obscurs ;)
Auteur(s)
Alexandre Andre
Astronaut, backend developer, PHP & Symfony lover. I should write something about myself but you know... developers.
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
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.
Découvrez un cas d'usage d'intégration d'un CRM avec une application e-commerce, en asynchrone, avec Hubspot et RabbitMQ