Aller au contenu
AFUP AFUP Day 2025 Baromètre Planète PHP PUFA
 

Unplug the HTTPlug !

Description

ll y a beaucoup de librairies qui permettent de faire des appels HTTP depuis nos applications. Parfois un projet utilise plus d'un "client" et il devient compliqué de savoir et contrôler comment ces appels sont déclenchés.

HTTPlug est un petit écosystème (librairies, adapteurs, bridges avec les frameworks, actif dans la création des PSRs…) qui peut aider à créer une abstraction autour du client HTTP. Il contient les adapteurs vers les librairies les plus connues (Guzzle, cURL, …) et adopte complètement les PSR7 et PSR18. En utilisant quelque chose comme HTTPlug, vous aurez la possibilité de normaliser le comportement et d'avoir un seul point d'entrée pour interagir avec les APIs.

Avec ce talk, l'objectif est de présenter l'écosystème, ses avantages, inconvénients et comment il peut aider votre projet à être plus solide.

Conférence donnée lors du AFUP Day 2020 Lyon, ayant eu lieu le 24 juin 2020.

Informations complémentaires

Vidéo

Le speaker

Stéphane HULARD

Depuis 2006, il baigne dans le web et son écosystème. Consultant et formateur indépendant, il apprécie particulièrement travailler sur des projets legacy pour accompagner les équipes à les reprendre en main et il s'obstine à la mise en place des méthodes d'ingénierie logicielle sur le web (intégration et déploiement continu, tests unitaires, documentation…). Il essaie de rendre à la communauté au maximum à travers des contributions Open Source. Il télétravaille presque à 100% ce qui lui permet de vivre à l'étranger la moitié de l'année.

Commentaires

Très intéressant pour connaître les tenants et les aboutissants des clients HTTP en PHP ! En tout cas, la quantité d'implémentations de clients HTTP en PHP est au final assez dommage parce qu'il n'y a du coup pas de réel standard, et surtout, on peut se sentir frustré qu'il n'y ait aucune solution native à PHP et que des bibliothèques externes doivent faire le travail !
Alex Rock, le 24/06/2020
Je ne connaissais pas, très intéressant ! Un peu abstrait pour moi qui n'ai jamais rencontré ce besoin mais la présentation était claire, merci !
Marine Gandy, le 24/06/2020

Transcript

[Musique] [Musique] ? On va attaquer la prochaine session.

Tu es déjà là Stéphane impeccable Pour la prochaine prochaine session on reçoit Stéphane Hulard Il baigne dans le web et son écosystème Il est consultant et formateur indépendant et c'est un des habitués de l'AFUP et de l'AFUP Lyon.

L'année dernière nous avait présenté lors de l'édition 2019, il nous avait parlé de Laravel.

Cette année il lui tenait à coeur de nous présenter HTTPlug et tout son écosystème.

Donc je te laisse la parole Stéphane.

? Bonjour à tous, j'espère que vous m'entendez bien C'est parti ...

Je vais refaire un petit tour d'horizon u peu plus précis Je suis consultant indépendant J'aime bien travailler sur des projets legacy et accompagner les équipes à reprendre le contrôle de leur code ca rejoint un peut le talk d'Alexandre de ce matin Je suis aussi formateur un peu dans la même veine autour de tout ce qui est lié aux méthodes de développement et à la qualité logicielle et j'essaye aussi de contribuer autant que je peux même si ce n'est pas autant que je voudrait, autour de l'open source et le partage de connaissances, ca permet toujours de rencontrer des personnes super intéressantes et de bénéficier de l'expertise et soutient de la communauté.

Aujourd'hui on va donc parler d'HTTPlug C'est un ensemble de librairies qui permettent de gérer les appels HTTP dans les applications Les mainteneurs ont été très impliqués dans les PSR ils ont essayé de structurer cette partie là qui est de plus en plus centrale dans les projets.

Moi j'ai découvert ces outils il y a quelques années.

C'était vraiment le début de ces librairies et j'ai même contribué au développement de l'adapteur ReactPHP.

Je reviendrai dessus un petit peu plus tard Aujourd'hui je ne démarre pas un projet sans utiliser HTTPlug c'est même un critère de choix dans les dépendances que je vais installer.

Donc HTTPlug, pourquoi ça a été créé ? Ca permet d'écrire des librairies et des applications qui nécessitent un client HTTP sans pour autant les lier à une implémentation spécifique.

Alors un client qu'est-ce que c'est? C'est un objet à qui on va donner une request et on va obtenir une réponse.

On est en mode HTTP, on à l'habitude de voir ce genre de choses quand on navigue sur le web.

La plupart des applications aujourd'hui elles vont faire des appels à des API, ce genre de choses HTTPlug va être le noeud central par lequel va, tout transiter.

On centralise ainsi tous les appels et ça va permettre d'aider à érer des logs ou à contrôler le comportement autour de tous ces appels.

Ca facilite pas mal le debug et les tests parce qu'on sait toujours pas où on va avoir besoin de regarder quand on a un soucis autour de nos petites connexions HTTP.

Et après on s'intéressera en fait directement au fait que le client soit capable de faire des appels réseau, ça c'est très important, on est vraiment en train de parler d'une abstraction.

On veut juste savoir que ce client il est capable de répondre à une certaine signature et qu'il existe.

Avant que cet écosystème là ait été mis en place il y avait des outils qui permettaient de faciliter l'utilisation des différents clients du marché.

Il y avait une histoire d'essayer d'adapter un petit peu le comportement, de normaliser.

Mais l'arrivée des PSR a permis de faire émerger une nouvelle solution.

HTTPlug c'est un écosystème.

Ce n'est pas une simple librairie, Ce n'est pas une grosse brique qu'on va installer.

C'est plein de petit bouts qu'on va pouvoir composer ensemble pour pouvoir ... [problème de son] ... projet Cela permet de limiter les dépendances, ça c'est aussi important.

Les mainteneurs ont porté la PSR 18 qui a été aujourd'hui validée il y a quand même plus de 2 ans maintenant. Presque deux ans.

Et, cette PSR est liée au comportement du client HTTP.

Ils ont mené au bout cette recommandation et ils ont sorti en fait la version 2.0 en octobre 2018 qui était complètement compatible PSR 18.

Le fait que HTTPlug était compatible dès la sortie de la PSR ça l'a un petit peu imposée pour moi comme une référence.

Et aujourd'hui il y a de plus en plus de librairies qui utilisent les interfaces et on commence vraiment à avoir un bon découplage autour de cette problématique.

Donc l'éventail des solutions proposées est assez large.

Et ça permet de s'habituer aux habitudes de chaque équipe.

Parce que des clients HTTP tout le monde en utilise même parfois inconsciemment.

On a tous petit peu nos habitudes sur ce que l'on aime et qu'on aime pas.

C'est une défense parce que si on est habitué à utiliser CURL, on va pouvoir continuer à utiliser un connecteur CURL, si on préfère faire du GUZZLE, on peut faire aussi avec GUZZLE.

Donc aujourd'hui c'est plus un ensemble d'interfaces et d'implémentations que l'on va pouvoir inter-changer en fonction des besoins.

Après chaque implémentation est spécifique et elle va pouvoir apporter son petit lot de fonctionnalités.

Par exemple le client HTTP de Symfony qui gérait le HTTP2 à sa sortie, était une de ses grandes forces par rapport aux autres clients HTTP du marché qui ne le faisaient pas.

Le fait que ce soit interchangeable ça veut dire que je peux choisir de passer demain à Symfony, au client HTTP de Symfony, et bénéficier directement de tout ce qu'il va pouvoir m'apporter sans pour autant changer en fait le comportement de mon code.

Ca je vais pouvoir le faire grâce à un simple composer require de la bonne librairie du bon client et de l'intégrer dans mon projet.

Bon après, c'est bien beau tout ça mais ça semble quand même un peu compliqué.

Une abstraction s'est jamais quelque chose qu'on accueille à bras ouverts en tout cas ça veut dire remettre un petit peu en cause nos habitudes changer notre façon de coder.

On sait déjà faire des appels HTTP.

Souvent en mode simpliste on passe même par des petits file_get_contents comme ça sans trop le dire on les cache pour éviter que le lead dev ou la personne un petit peu en charge de la review puisse le voir.

Mais c'est des choses qu'on a l'habitude de manipuler de façon assez simple.

Pourquoi venir ajouter une nouvelle abstraction dans notre code ? Pour ça il faut qu'on prenne un petit peu de recul : dépendre d'une implémentation spécifique ça va demander aux développeurs de maintenir et faire évoluer le code de l'application avec cette implémentation.

Par exemple GUZZLE a changé son namespace de base en passant de la version 3 la version 4.

Si on dépendait de GUZZLE directement Ça voudrait dire qu'il faudra repasser sur notre code modifier notre implémentation pour coller à la nouvelle version ou au nouveau namespace.

En utilisant une abstraction on va s'affranchir de ça.

On va juste intégrer le connecteur vers la version de GUZZLE mais l'outil qu'on utilise pour centraliser nos appels lui, il va pas du tout changer Ensuite on a les PSR qui viennent améliorer l'interopérabilité entre les projets.

Il y a une vraie dynamique ces dernières années autour de ces recommandations.

HTTPlug s'inscrit complètement dans cette dynamique.

Donc en fait HTTPlug ça n'apporte pas une vision d'un client, d'une certaine façon façon de coder, mais c'est plutôt une façon de bien intégrer ces recommandations là, dans votre projet.

Maintenant on va voir un petit peu de code.

Donc si on utilise des PSR : Les PSR bon qu'est-ce que c'est ? qu'est-ce que ca veut dire ? C'est des interfaces qu'on va pouvoir composer ensemble pour créer des solutions qui vont être capables de répondre à certains besoins de notre application.

Le premier point : La PSR 18 va nous définir ce que c'est qu'un client HTTP.

Si on a besoin d'une fonctionnalité de type client HTTP, on ne se prend pas la tête on va dépendre de la PSR.

On veut juste un objet de type client / interface et on l'utilise.

Ensuite on a la PSR 7 qui elle va définir le comportement des messages qui sont envoyés à travers le client HTTP.

Des requests, des réponses.

Et tout ce qui va avec.

Pour construire mon objet là dans mon petit exemple qui s'appelle AwesomeDTO j'ai besoin d'une réponse de type PSR 7.

Et donc je vais pouvoir utiliser mon client et obtenir cette réponse à partir d'une requête.

Ici c'est pas important de connaître les interfaces ou les PSR dans le détail.

Ca montre juste que ce code n'est absolument pas dépendants d'HTTPlug.

Ici on a un objet qui est relativement simple qui décrit ses dépendances et ses paramètres.

HTTPlug va permettre de fournir des implémentations concrètes pour cet objet sans avoir à écrire un code plus complexe.

Aujourd'hui le code que je vous montre ici est complément découplé de l'implémentation.

Mon implémentation va pouvoir ressembler à ça par exemple.

Ce sont des exemples qui sont volontairement simplifiés pour que ca tienne dans les slides mais, montre la quantité de code nécessaire à réussir à utiliser un client.

Donc là j'ai choisi d'importer l'adapteur React donc j'ai fait mon petit "composer require" dans mon projet. On imagine en tout cas que je l'ai installé quelque part Et je vais pouvoir utiliser mon client react.

Je mets entre parenthèses dans mon constructeur les options, il ya plusieurs options à passer en paramètre en fonction du client.

Ca c'est de la configuration.

Je ne vais pas rentrer dans le détail ici.

Mais, je construit mon client et vu qu'il est compatible avec PSR 18 je peux directement passer mon objet dans le constructeur de mon petit MyApiDataRetriever que j'utilise la ligne 6.

Donc là on est compatible PSR18, mon client HTTPlug je peux l'utiliser en lieu et place de mon ClientInterface que l'on a vu juste avant.

Ensuite pour construire la requete à passer j'utilise une factory.

On est dans les PSR et on va essayer de poursuivre un petit peu sur le chemin de ces recommandations.

Il y a la PSR 17 qui défini des comportements de construction de messages HTTP Je vous laisserai creuser un petit peu si vous voulez, plus dans le détail.

Mais on est dans cet espace là : RequestFactory de Laminas.

Ca fait partie des librairies compatibles PSR 17 et qui nous permettent d'injecter et de créer des nouveaux messages HTTP dans mon application.

Et là l'automatisation ... [Ca y'est pardon]...

Pour aller un peu plus loin, on a un nouveau composant en fait qui fait partie d'HTTPlug et qui permet de découpler encore plus qu'avec client spécifique parce que dans l'exemple que je vous montre juste avant on a un client que j'ai choisi qui est le client REACT, que j'importe et j'instancie.

Ici en fait on va utiliser un composant qui s'appelle Discovery.

Ce composant Discovery qu'est ce qu'il fait ? Ben il va être capable de dire : "Bon est-ce-que dans les dépendances qui sont présentes dans mon contexte actuel j'ai quelque chose qui ressemble un client HTTP ? ".

Pour ça, il va utiliser différentes stratégies : Soit un petit tableau de mapping entre des noms d'objets ou ce genre de choses...

Ou des outils tiers comme Puli qui avait été utilisé un petit moment pour faire de la détection automatique de dépendances.

L'avantage de ça en fait qu'est ce que c'est ? C'est que mon code je dis juste qu'il a besoin d'un client.

Si j'ai un des vendors qui arrive avec son propre client je peux très bien choisir de l'utiliser sans me poser la question J'ai pas des besoins très particuliers sur les comportements que j'ai besoin d'atteindre ou les fonctionnalités que je veux implémenter donc je veux juste un client HTTP.

S'il y en a déjà un je l'utilise.

Donc ça ça nous permet de remonter l'abstraction d'un niveau, mais, ça montre surtout que je ne suis absolument pas obligé de changer le comportement de mon code.

Les lignes 7, 8, 9, c'est les mêmes que dans l'exemple d'avant.

J'obtiens mon client juste d'une autre façon et je l'utilise.

Donc on a une complexité relativement faible tout en s'appuyant sur les recommandations de la communauté et les interfaces qui sont définies pour chacun des besoins.

La séparation qu'on obtient entre le client et les différents messages ce sont des choses qui ont été beaucoup discutées sur les mailings list du PHP FIG.

Ca va permettre vraiment d'atteindre un niveau de séparation dans le comportement et dans la responsabilité des objets qui est intéressant parce que du coup on va pouvoir mieux contrôler ce qui se passe.

Moi ce que j'ai l'habitude de faire dans mes projets en fait c'est typiquement dans ma configuration des services de Symfony je créé directement mes objets REQUEST qui sont des objets définis à l'avance parce que je vais toujours appeler la même URL j'ai toujours les mêmes HEADERS.

En fait mon message en lui même la requête que je vais manipuler c'est un objet à part entière que je vais pouvoir injecter dans mes objets grâce au conteneur et l'injection de dépendances.

Le fait qu'on ait cette séparation nous aide à avoir un code assez simple on a peu de code, on a assez peu de logique à comprendre pour réussir à savoir ce qui se passe.

Ca permet aussi de définir les pré-requis pour chacun des objets donc on sait qu'on dépend d'un client, on veut pouvoir faire des appels.

Je vais donc pouvoir ajouter mon client ou ma requête comme on l'a vu juste avant.

Le fait qu'on ait des interfaces assez simples on va aussi pouvoir assez facilement, grâce à des petits design patterns, adapter le code fourni pour intégrer des comportements spécifiques à notre application.

Typiquement ici, je vais avoir un décorateur Ce décorateur, il va être un client HTTP Il implémente l'interface : ClientInterface qui est l'interface de la PSR 18.

Donc en fait, en faisant ça je déclare que mon objet AwesomeDecorator, c'est un client HTTP.

Sauf que je n'ai pas envie de m'embêter a venir re-définir entièrement le comportement de mon client HTTP.

Je ne vais pas re-coder un client HTTP.

Donc ce que je fais c'est que je vais venir intégrer dans mon constructeur un autre client HTTP.

Que je vais venir décorer.

C'est un pattern assez simple qui nous permet de vraiment, découpler les comportements.

Et l'implémentation de ma fonction sendRequest qui est la seule fonction utile à l'intérieur de mon client, qui va vraiment prendre la requête et me renvoyer une réponse.

En fait je délègue la responsabilité à mon objet decorated à ligne 12 mais par contre juste avant et juste après je vais pouvoir potentiellement faire des traitements sur ma requête, vérifie que j'ai bien certains HEADERS qui soient en place.

Ou faire des traitements juste après l'exécution de la requête sur la réponse comme vérifier qu'on a bien tout ce qu'il faut.

C'est typiquement ce comportement là en fait qui est utilisé à travers les plugins, qui sont développés par la communauté HTTPlug, pour tout ce qui est authentification, cache et logs.

C'est vraiment assez facile grâce à ce petit pattern de venir ajouter des fonctionnalités, un peu en mode middleware en fait pour ceux qui connaissent le principe.

Donc on voit qu'implémenter aujourd'hui un client HTTP, c'est juste quelques petites librairies qu'on va vous pouvoir assembler tout en s'appuyant sur les PSR.

Ca reste relativement simple, pas forcément facile à faire, mais en tous cas, on a pas de code on a un résultat assez efficace Ensuite, c'est toujours important de savoir faire ça, on va avoir besoin de tester nos appels HTTP.

On va avoir besoin de le faire. Pourquoi ? Par ce qu'on veut que notre code, si jamais on lui donne la bonne requête ou qu'il reçoive la bonne réponse, qu'il fonctionne correctement.

C'est souvent compliqué de tester des appels HTTP.

En tous cas c'est ce que l'on entend.

Est-ce que ça veut dire mocker des comportements de services externes ? Simuler les appels vers des outils ? Et donc du coup le fait d'avoir cette complexité ben c'est souvent le point sensible dans les suites de test.

Ce sont des tests qui vont prendre du temps où des tests qui vont être complexes.

Simplement parce qu'on doit comprendre en profondeur l'implémentation du client qu'on utilise.

Grâce aux interfaces, aux PSR et à l'écosystème, on va réussir à avoir une séparation entre le vrai appel réseau qui nous pose problème dans nos tests parce que il ajoute une grosse complexité et il ajoute potentiellement des problèmes qui ne viennent pas de notre code, et le réel besoin.

Aujourd'hui on a dit qu'un client HTTP c'est juste un objet. Je lui donne une requête et il me renvoie une réponse.

Et grâce aux interfaces présentes dans les PSR je peux, à l'intérieur de mes tests, créer une sorte de faux client qui va être capable de créer des fausses requêtes et de me renvoyer de fausses réponses Pour ça, on a un client qui est développé et maintenu par la communauté qui sappelle Mock Client Il va permettre, assez facilement de dire : Si jamais on détecte une requête d'un certain type, sur une certaine URL, on renvoie une certaine réponse.

C'est intéressant parce que du coup ça permet de dire: Ok là par exemple mon RequestMatcher, si j'ai une requête qui est faite sur le domaine exemple.com et qui est faite sur le path /api, alors on renvoie une réponse spécifique.

L'avantage de cette approche là c'est qu'on a vraiment une logique de match c'est à dire que je ne suis pas obligé de mocker toute les requêtes ou toutes les réponses dans mon application.

Je vais mocker celles qui m'intéressent pour mon besoin particulier.

Et en faisant, je créé entièrement la réponse qui m'intéresse aussi.

Je vais pouvoir facilement créer des faux problèmes pour pouvoir valider mes tests, ou créer les cas dans lesquels c'est censé fonctionner pour valider aussi que mon code tourne correctement.

On va se retrouver avec notre RequestMatcher qui lui a pour objectif de dire : "Est-ce que ma requête passe dans mes tests ?" Et l'implémentation du constructeur de réponses qui lui va avoir pour rôle de juste me renvoyer un objet réponse correctement construit.

Au final, on ne change, encore une fois, absolument pas le comportement de notre client C'est à dire ... pardon ... de notre code applicatif, on a toujours un client HTTP Qui est un objet, de type PSRClientInterface et on a toujours notre MessageFactory qui va nous construire notre requête.

Tout ça en fait on a implémenté différentes façons de créer des appels réseau, de renvoyer des réponses, en ne changeant absolument pas le poids de notre application.

Et ça pour moi, c'est le gros avantage de cet écosystème.

C'est qu'on va vraiment se retrouver avec quelque chose de très souple et finalement utiliser le principe de client: "réponse/requêtes" dans pleins de contextes différents.

Aussi bien du test, que de l'application concrète comme on vient de le voir, ou sûrement d'autres cas un plus "tordus" que l'on pourrait imaginer.

En faisant ces tests de cette façon on a vraiment un contexte de tests qui est complètement maîtrisé.

On va avoir des tests qui vont s'exécuter rapidement parce qu'on a aucun appel réseau qui est fait.

Et on peut même imaginer intégré à l'intérieur de la construction de notre réponse des fichiers qui sont des fixtures intégrées pendant les tests et qui seront renvoyées en fonction de cas plus ou moins moins complexes On peut aller assez loin dans la construction de cette réponse dynamique.

Et enfin, vous avez le pouvoir: Vous avez une nouvelle vision des clients HTTP.

Pour ceux qui connaissaient pas en tout cas cet écosystème là.

De leur impact et des solutions qui sont à dispositions.

Aujourd'hui moi j'aime vraiment la simplicité du code qui va avec cette partie.

Entre les PSR qui définissent les comportements, HTTPlug qui fournit des implémentations relativement légères et la centralisation.

Ca m'a permis plusieurs fois de mieux comprendre les séquences d'appels qui sont faites dans les applications où on a de grosses séquences d'appels très complexes.

pour faire transiter des fichiers entre plusieurs serveurs ou ce genre de choses.

Ben ça m'a permis vraiment d'avoir une bonne vision de "qu'est ce qui se passe à quel moment" "dans quel ordre" "qu'est ce qui est vraiment passé comme header" "comme réponse", et aussi d'échanger avec le client autour de logs très exhaustifs en fait sur la réalité de ces appels.

On est dans un contexte très technique mais ça a été vraiment utile de pouvoir centraliser en fait ces appels aussi bien, ce qui était fait à l'intérieur des vendors que ce qui était fait dans mon application.

Et enfin, au niveau des tests c'est vrai que les outils qui sont intégrés ils ont vraiment permis d'avoir une très bonne maîtrise et de valider ces séquences de traitement qui étaient très complexes.

Voilà c'est bon pour moi merci de votre attention.

Je ne sais pas exactement à combien de temps j'en suis ? Merci à toi Stéphane, il te reste quasiment 1mn30.

Je ne sais pas s'il y a des questions.

S'il y a des gens qui ont des questions ou autre.

Pour le moment la liste est vide.

Donc n'hésitez pas on a le temps pour en prendre une.

? C'est vrai que c'etait assez rapide.

Au pire sinon je serai dans les parages sur les slides y a mon twitter .

Ha ! il y a une question ? il y a une question de Frédéric oui: ?On espère que GUZZLE ne va pas encore casser sa compatibilité à l'avenir [rires] ... ? Ben du coup oui et non en fait.

C'est toujours le travers de ce genre d'abstraction quoi.

Moi aujourd'hui j'y trouve mon compte et je trouve que c'est très pertinent dans un contexte où on veut pouvoir s'adapter et faire évoluer quelque chose dans le temps.

C'est vraiment dans un objectif d'être maintenable.

Si demain GUZZLE disparait et que j'ai plus que CURL comme possibilité, Je n'ai pas envie de devoir re-développer toute la code de traitement de mes client HTTP Et donc vraiment s'appuyer sur une abstraction va au moins permettre de s'affranchir de ça.

Donc une fois qu'on est dans cette dynamique là, que GUZZLE change son namespace ben finalement ....

Ca a fait beaucoup de bruit au moment où ça a été fait parce que qu'il n'y avait pas encore tout cet écosystème autour.

Mais maintenant je pense que ce serait un moindre mal.

? Ok et ben merci beaucoup [Musique]