Cinq techniques testées au combat que votre développeur d'API WordPress n'utilise pas
Publié: 2022-03-11L'un des meilleurs moyens d'élever votre statut de développeur WordPress, du moins aux yeux de vos clients, est de devenir habile à consommer des API. Voici un scénario courant pour la mise en œuvre de l'API WordPress : votre client vous demande d'ajouter un widget à son site, par exemple, un widget d'abonnement par e-mail. Vous récupérez du code de leur service de messagerie tiers - il s'agit peut-être d'une balise de script ou d'un iframe
- collez-le dans la page et répondez à votre client : "Compris !"
Malheureusement, vous avez affaire à un client un peu plus exigeant et il constate les imperfections suivantes :
- Bien que le widget, comme le reste du site, comporte une police sans empattement, ce n'est pas tout à fait la bonne. Le widget utilise Helvetica au lieu de la police personnalisée que vous avez installée.
- Le formulaire d'abonnement du widget déclenche un nouveau chargement de page, qui peut être perturbateur s'il est placé au milieu d'un article.
- Le widget semble prendre un moment supplémentaire pour se charger après le reste de la page, ce qui semble discordant et bon marché.
- Le client souhaite que les abonnés soient étiquetés avec des métadonnées basées sur la publication à partir de laquelle ils se sont abonnés, et le widget n'offre rien qui ressemble à distance à cette fonctionnalité.
- Le client trouve agaçant de devoir désormais gérer deux tableaux de bord (wp-admin et la zone d'administration du service de messagerie).
À ce stade, l'une des deux choses pourrait raisonnablement se produire. Vous pouvez déclarer ces éléments « agréables à avoir » et rassurer votre client sur les mérites d'une solution 80/20, ou vous pouvez répondre à ces demandes. D'après mon expérience personnelle, j'ai découvert que répondre à de telles demandes, c'est-à-dire démontrer la maîtrise des services tiers, est un moyen fiable de convaincre le client que vous êtes une sorte d'assistant WordPress. De plus, c'est souvent amusant à faire.
Au cours de la dernière décennie, j'ai utilisé WordPress comme plate-forme de consommation d'API contre probablement 50 API différentes. Certaines des API les plus courantes sont MailChimp, Google Analytics, Google Maps, CloudFlare et Bitbucket. Mais que se passe-t-il si vous avez besoin d'en faire plus, et si vous avez besoin d'une solution personnalisée ?
Comment développer un client API WordPress
Dans cet article, je vais développer par rapport à une API générique de "service de messagerie", en essayant de mon mieux de garder les choses aussi agnostiques que possible. Cependant, je pense qu'il est raisonnable de supposer que nous avons affaire à une API JSON REST. Voici quelques sujets d'arrière-plan qui pourraient vous aider à apprécier les points techniques de cet article :
- La famille de fonctions WordPress HTTP
- JSON
- DU REPOS
Si vous vous trouvez peu familier avec ces sujets et que vous souhaitez approfondir, faites une pause maintenant et téléchargez l'excellente application Postman. Il vous permet de communiquer avec les API sans écrire de code.
Cependant, si vous n'êtes pas du tout familier avec ceux-ci, continuez à lire quand même. Un public technique ayant une certaine expérience de WordPress tirera le meilleur parti de cet article, mais je prendrai soin d'expliquer la valeur de chaque technique de manière moins technique. Un lecteur non technique sortira de cet article capable d'évaluer le retour sur investissement de chaque point avant de le parrainer et de juger de la qualité de la mise en œuvre une fois livrée.
Remarque : Si vous avez besoin d'un cours de remise à niveau rapide, vous pouvez consulter notre guide de l'API WordPress REST.
Sans autre préambule, permettez-moi de partager avec vous une poignée de techniques différentes que j'apprécie sur la plupart des API, projets et équipes avec lesquels je travaille.
Transitoires : quand les tenir, quand les replier
Dans mon paragraphe d'ouverture, j'ai noté que le client trouvait ennuyeux de chevaucher deux zones d'administration : wp-admin et le tableau de bord de son service de messagerie. Un bon moyen de résoudre ce problème serait de leur fournir un widget de tableau de bord dans wp-admin, pour afficher un résumé de leur activité récente d'abonné.
Mais encore une fois, cela pourrait nécessiter plusieurs requêtes HTTP à l'API distante (l'API fournie par le service de messagerie), entraînant de longs chargements de page. La solution à ce problème de performances consiste à stocker les appels d'API en tant que transitoires. Cet article du Codex fournit une excellente explication que vous devriez certainement lire, mais je vais le résumer ainsi :
- Obtenez les données de l'API distante.
- Stockez-le en utilisant
set_transient()
avec un délai d'expiration de votre choix en fonction de votre propre jugement sur les performances, les limites de débit et la marge d'erreur lors de l'affichage de données obsolètes dans cette application particulière. - Procédez dans votre logique métier - traitement des données, retour d'une valeur, quel que soit le cas.
- Lorsque vous avez à nouveau besoin des données, comme lors du chargement de la page suivante, vérifiez-les dans le cache transitoire à l'aide
get_transient()
avant de conclure que vous devez les obtenir à partir de l'API.
Je considère que c'est une base utile et viable, mais vous pouvez aller plus loin si vous pensez un instant aux verbes REST. Parmi les cinq méthodes les plus courantes (GET, POST, PATCH, PUT, DELETE), une seule appartient à votre cache transitoire. Pouvez-vous deviner lequel? C'est OBTENIR. Dans mes plugins, j'ai presque toujours une classe PHP dédiée à l'abstraction des appels à l'API distante en question, et un argument lors de l'instanciation de cette classe est la méthode HTTP. S'il ne s'agit pas d'un appel GET, je n'invoquerai aucune couche de mise en cache.
De plus, s'il ne s'agit pas d'un appel GET, il va de soi que je prends des mesures pour modifier les données distantes d'une manière ou d'une autre, peut-être en ajoutant, modifiant ou supprimant un abonné par e-mail. C'est peut-être le bon moment pour invalider le cache existant pour cette ressource, via delete_transient()
.
Pour revenir à notre exemple d'API d'abonnement aux e-mails WordPress, voici comment cela fonctionnerait en pratique :
- Un widget de tableau de bord pour afficher les abonnés récents va appeler le point de terminaison API pour
/subscribers
via une requête GET. Comme il s'agit d'une requête GET, elle est stockée dans mon cache transitoire. - Un widget de barre latérale pour s'abonner à la liste de diffusion va appeler le point de terminaison API pour
/subscribers
via une requête POST. Comme il s'agit d'une requête POST, non seulement cela va éviter mon cache transitoire, mais cela va aussi me provoquer à supprimer la partie pertinente de mon cache transitoire, afin que le widget du tableau de bord reflète ce nouvel abonné. - Lorsque je nomme les transitoires, je les organise souvent en les nommant littéralement d'après l'URL de l'API distante que j'appelle. C'est un moyen pratique d'identifier le bon transitoire à supprimer. S'il s'agit d'un point de terminaison qui prend des arguments, je vais les concaténer dans une chaîne et les ajouter également au nom transitoire.
En tant que client ou autre partie prenante moins technique, vous devez demander spécifiquement la mise en cache transitoire - ou à tout le moins une discussion à ce sujet - chaque fois que l'application extrait des données d'un service distant. Vous devez vous familiariser avec l'excellent plugin Query Monitor pour avoir une vue sur le fonctionnement des transitoires. Il vous donnera une interface pour parcourir quelles données sont stockées en tant que transitoires, à quelle fréquence et pendant combien de temps.
Parfois, les transitoires ne suffisent pas
Certains services d'hébergement WordPress premium ne vous permettent en fait pas d'utiliser des transitoires en production. Ils ont du code en cours d'exécution, peut-être sous la forme d'un plug-in MU ou d'un autre script, qui interceptera votre tentative d'utilisation de l'API transitoire et stockera ces informations via le cache d'objets à la place. WP-Engine, dans sa configuration la plus courante, en est un excellent exemple.
Si vous stockez et récupérez simplement des données, vous n'avez en fait pas à vous en soucier et vous ne remarquerez peut-être même pas que cela se produit. Toute la famille de *_transient()
vous fournira le même résultat final, juste filtré pour utiliser le cache d'objets au lieu du cache transitoire. Cependant, vous pourriez rencontrer des problèmes lorsque vous tentez de supprimer des transitoires. Voici pourquoi.
Si votre intégration d'API est suffisamment complexe pour mériter sa propre page de paramètres, vous souhaiterez peut-être inclure une interface utilisateur pour permettre à l'utilisateur administrateur d' effacer l'intégralité du cache transitoire de votre plugin . L'utilisation la plus courante de ce bouton serait lorsque le client modifie certaines données directement sur le service distant et souhaite invalider le cache que nous stockons dans WordPress. Ce bouton peut également être utile si le client modifie les informations d'identification du compte, les clés API ou simplement en tant que bouton de « réinitialisation d'usine » pour le débogage.
Même si vous étiez assez intelligent pour nommer toutes vos clés transitoires afin d'avoir un espoir d'identifier chacune d'entre elles pour delete_transient()
, le meilleur scénario implique probablement toujours du SQL brut, ce que j'essaie toujours d'éviter dans WordPress :
<?php // Purge all the transients associated with our plugin. function purge() { global $wpdb; $prefix = esc_sql( $this -> get_transient_prefix() ); $options = $wpdb -> options; $t = esc_sql( "_transient_timeout_$prefix%" ); $sql = $wpdb -> prepare ( " SELECT option_name FROM $options WHERE option_name LIKE '%s' ", $t ); $transients = $wpdb -> get_col( $sql ); // For each transient... foreach( $transients as $transient ) { // Strip away the WordPress prefix in order to arrive at the transient key. $key = str_replace( '_transient_timeout_', '', $transient ); // Now that we have the key, use WordPress core to the delete the transient. delete_transient( $key ); } } ?>
Pas pratique, pas efficace. Au lieu de cela, cette situation nécessite la mise en cache d' objets, car la mise en cache d'objets nous offre un moyen pratique de regrouper les valeurs mises en cache . De cette façon, lorsque vous devez vider toutes les valeurs mises en cache liées à votre plugin, il s'agit d'un simple appel à wp_cache_delete( $key, $group )
.
Je résumerais tout cela en disant : vous ne pouvez pas être un expert en consommation d'API si vous n'êtes pas encore un expert en gestion du cache pour ces données.
En tant que client, l'élément clé à surveiller est le comportement aberrant du cache entre les environnements de staging et de production. En d'autres termes, bien que tester un nouveau lot de travail en staging soit toujours une bonne pratique, la mise en cache est quelque chose qui doit également être testé en production avec le même soin.

L'API distante peut aider à informer votre hiérarchie de classes PHP
Lors de la présentation des différentes classes PHP pour mon plugin, je trouve souvent utile d'imiter la façon dont les points de terminaison de l'API sont définis. Par exemple, qu'est-ce que les points de terminaison suivants semblent avoir en commun ?
- https://api.example-email-service.com/v1/subscribers.json
- https://api.example-email-service.com/v1/lists.json
- https://api.example-email-service.com/v1/campaigns.json
Ils renvoient tous des collections , j'entends par là le résultat d'une requête GET, renvoyant des résultats de zéro à plusieurs où chaque résultat est membre d'un tableau. Cela peut sembler assez évident, mais je trouve que c'est une invite utile pour la structure de classe suivante dans mon code PHP :
-
class.collection.php
, une classe abstraite -
class.subscribers.php
étend la classe abstraite,Collection
. -
class.lists.php
étend la classe abstraite,Collection
. -
class.campaigns.php
étend la classe abstraite,Collection
.
La classe abstraite prendrait comme seul argument un tableau de paramètres de requête : des éléments tels que la pagination, la colonne de tri, l'ordre de tri et les filtres de recherche. Il aurait des méthodes pour les tâches courantes telles que l'appel de l'API distante, la gestion des erreurs et peut-être la transformation des résultats en un menu HTML <select>
ou un jQueryUI AutoSuggest. Les classes qui instancient la classe abstraite peuvent être assez courtes, ne faisant peut-être rien de plus que spécifier la chaîne à utiliser dans l'URL du point de terminaison de l'API *.json
.
De même, qu'est-ce que les paramètres suivants ont en commun ?
- https://api.example-email-service.com/v1/subscribers/104abyh4.json
- https://api.example-email-service.com/v1/lists/837dy1h2.json
- https://api.example-email-service.com/v1/campaigns/9i8udr43.json
Ils renvoient tous un élément , j'entends par là exactement un membre spécifique et unique d'une collection : des choses comme un abonné à un e-mail particulier, une liste d'e-mails ou une campagne d'e-mails. Par conséquent, j'aime utiliser la structure suivante dans mon code PHP :
-
class.item.php
, une classe abstraite -
class.subscriber.php
étend la classe abstraiteItem
. -
class.list.php
étend la classe abstraite,Item
. -
class.campaign.php
étend la classe abstraiteItem
.
La classe abstraite prendrait comme seul argument une chaîne pour identifier l'élément spécifique demandé. Encore une fois, les classes instanciées peuvent être assez courtes, ne faisant peut-être guère plus que spécifier la chaîne à utiliser dans */duy736td.json
.
Il existe de nombreuses approches pour structurer l'héritage de classe, mais même si vous adoptez une approche différente de ce que j'ai décrit ci-dessus, je parie qu'il y a de fortes chances que la structure de l'API distante puisse aider à informer la structure de votre application.
En tant que client, un symptôme courant d'une mauvaise architecture est lorsque vous vous retrouvez à devoir demander le même changement à plusieurs reprises dans une application. Par exemple, si vous demandez que les rapports renvoient 100 résultats par page au lieu de 10, et que vous devez répéter cette demande pour les rapports sur les abonnés, les rapports de campagne, les rapports de désabonnement, etc., vous détectez peut-être une mauvaise architecture de classe. Dans cette situation, il vaut la peine de demander à votre équipe si elle bénéficierait d'un cycle de refactoring : un corps de travail où le but n'est pas de changer le comportement du produit mais plutôt d'améliorer le code sous-jacent afin qu'il devienne plus facile de changer le comportement du produit à l'avenir.
Le cas d'utilisation parfait pour WP_Error
Je suis gêné d'admettre qu'il m'a fallu des années de plus que nécessaire pour commencer correctement à utiliser la famille de fonctions WP_Error
dans mon code. J'avais tendance à simplement coder mon chemin, soit en supposant qu'il n'y aurait jamais d'erreurs dignes d'être prises en compte par programme, soit en les traitant au cas par cas. Travailler avec des API distantes traverse cette mentalité comme un faisceau laser, car il présente un cas d'utilisation extrêmement pratique et puissant pour l'utilisation de WP_Error
.
Rappelez-vous plus tôt que j'ai mentionné que j'ai souvent une classe PHP dont le but est de faire des requêtes HTTP à l'API distante. Lorsque vous retirez tout le passe-partout, toutes les manipulations de données, toutes les préoccupations secondaires, cette classe revient en fait à appeler wp_remote_request()
afin d'obtenir un objet de réponse HTTP de l'API. De manière pratique, wp_remote_request()
renverra à la place un WP_Error
si l'appel ne s'exécute pas pour une raison quelconque, mais qu'en est-il si l'appel réussit à renvoyer une réponse HTTP d'un type défavorable ?
Par exemple, nous avons peut-être appelé le point de terminaison /lists.json
, mais ce compte particulier n'a pas encore de listes configurées. Cela renverrait une réponse HTTP valide, mais avec un code d'état de 400. Bien que ce ne soit pas exactement une erreur fatale en soi, du point de vue d'un code frontal qui souhaite transformer cet appel d'API en menu déroulant, un 400 pourrait aussi être un WSOD ! Par conséquent, je trouve utile de faire une analyse supplémentaire sur le résultat de wp_remote_request()
, renvoyant potentiellement un WP_Error
après tout :
<?php function call() { $response = wp_remote_request( $this -> url, $this -> args ); $code = wp_remote_retrieve_response_code( $response ); $first_digit = $code[0]; $good_responses = array( 2, 3 ); if( ! in_array( $first_digit, $good_responses ) { $body = wp_remote_retrieve_body( $response ); $out = new WP_Error( $code, $body ); } else { $out = $response; } return $out; } ?>
Ce modèle peut aider à simplifier le code qui appelle notre classe appelante, car nous savons que nous pouvons nous fier en toute sécurité à is_wp_error()
avant de continuer avec notre sortie.
En tant que client, vous devez parfois jouer le rôle d'un utilisateur malveillant, d'un utilisateur confus et d'un utilisateur impatient. Utilisez l'application d'une manière qui n'est pas destinée à être utilisée. Faites les choses que vos développeurs ne semblent pas vouloir que vous fassiez. Prenez note de ce qui se passe. Recevez-vous des messages d'erreur utiles ? Avez-vous des messages d'erreur du tout? Si ce n'est pas le cas, cela peut valoir la peine de parrainer un ensemble de travaux autour d'une meilleure gestion des erreurs.
La belle puissance de débogage de ob_get_clean()
Le Web programmable moderne, où presque chaque site consomme les API d'autres sites et est lui-même consommé via sa propre API, est devenu une arène incroyablement puissante pour le code. Mais c'est précisément cette qualité qui peut aussi le rendre assez lent.
Il est courant que les requêtes HTTP distantes soient les parties les plus chronophages du chargement d'une page donnée. Pour cette raison, de nombreux composants pilotés par API s'exécutent via Ajax ou cron. Par exemple, une suggestion automatique de recherche dans une liste d'abonnés par e-mail devrait probablement envoyer un ping à la source de données distante à la demande, à chaque frappe, plutôt que de charger les 100 000 abonnés dans le DOM lors du chargement de la page. Si ce n'est pas une option, une grande requête pourrait peut-être se synchroniser sur une tâche cron nocturne, de sorte que les résultats puissent être extraits d'un miroir local plutôt que de l'API distante.
Le problème avec cette approche est qu'elle peut être difficile à déboguer. Au lieu de simplement WP_DEBUG
et de laisser les messages d'erreur s'afficher dans la fenêtre de votre navigateur, vous êtes coincé à regarder dans la console réseau du navigateur ou à suivre un fichier journal pendant qu'une tâche cron (espérons-le ?) est en cours d'exécution. Je trouve cela inconfortable.
Une façon d'améliorer cette situation consiste à effectuer des appels prudents et stratégiques à error_log()
. Mais encore une fois, un problème courant avec la journalisation est qu'avec des applications volumineuses ou occupées, les journaux d'erreurs peuvent devenir trop volumineux, ou croître trop rapidement, pour être utiles pour la surveillance ou l'analyse. Par conséquent, nous devons être sélectifs avec ce que nous enregistrons, en y réfléchissant autant qu'avec notre logique d'application réelle . Il est dommage d'avoir pris le temps de consigner une erreur de cas marginal exotique qui ne semble se produire que par intermittence sur une tâche cron peu fréquente pour se rendre compte que la véritable nature du bogue vous a encore une fois échappé parce que vous n'avez pas enregistré un membre particulier du tableau , disons, de la valeur incriminée.
Par conséquent, ma philosophie est devenue, je ne me connecte pas toujours, mais quand je le fais, je journalise tout . En d'autres termes, après avoir identifié une fonction particulièrement inquiétante, je vais l'enregistrer avec un réseau aussi large que possible :
<?php function debug( $bug ) { ob_start(); var_dump( $bug ); $out = ob_get_clean(); error_log( $out ); } ?>
Cela équivaut à var_dump()
'ing toute la valeur boguée en une seule entrée dans le fichier journal des erreurs.
En tant que client, il vaut la peine de vérifier périodiquement l'utilisation totale de la mémoire des fichiers pour votre application. Si vous remarquez que vous vous heurtez soudainement aux limites de stockage de votre compte d'hébergement, il y a de fortes chances qu'un journal d'erreurs devenu sauvage soit à blâmer. Vos développeurs bénéficieront d'un cycle de travail axé sur une meilleure journalisation, et vos clients aussi !
Pas exactement un clickbait, mais ça ira
Veuillez pardonner la structure de la liste de cet article. Je n'ai pas pu forcer ces points dans un thème d'article plus fédérateur car ces modèles sont très génériques : ils s'appliquent à tout point de terminaison JSON REST et à toute sortie WordPress .
Ce sont les modèles que je vois se produire encore et encore, quelle que soit l'API distante ou la raison pour laquelle nous l'utilisons dans WordPress. Je suis allé jusqu'à rassembler toutes ces sortes de principes dans un plugin standard qui accélère considérablement mon travail. Avez-vous des points similaires que vous retenez pour chaque projet ? S'il vous plaît partagez-les afin que je puisse les voler et les ajouter à mon passe-partout !