Créer un plugin WordPress qui utilise des API de service, "De la soupe aux noix"

Publié: 2022-03-10
Résumé rapide ↬ Un nombre croissant d'API accessibles au public fournissent des services puissants pour étendre les fonctionnalités de nos applications. WordPress est un CMS incroyablement dynamique et flexible qui alimente tout, des petits blogs personnels aux principaux sites Web de commerce électronique et tout le reste. Une partie de ce qui rend WordPress si polyvalent est son puissant système de plug -ins, qui facilite incroyablement l'ajout de fonctionnalités. Nous verrons comment j'ai créé GitHub Pipeline, un plugin qui vous permet d'afficher les données de l'API GitHub sur les pages WordPress à l'aide de shortcodes. Je vais donner des exemples spécifiques et des extraits de code, mais considérez la technique décrite ici comme un modèle pour savoir comment consommer n'importe quelle API de service avec un plugin. Nous allons commencer par le début, mais une certaine familiarité avec WordPress et le développement de plugins est supposée, et nous ne passerons pas de temps sur des sujets pour débutants, comme l'installation de WordPress ou de Composer.

Un nombre croissant d'API accessibles au public fournissent des services puissants pour étendre les fonctionnalités de nos applications. WordPress est un CMS incroyablement dynamique et flexible qui alimente tout, des petits blogs personnels aux principaux sites Web de commerce électronique et tout le reste. Une partie de ce qui rend WordPress si polyvalent est son puissant système de plug -ins, qui facilite incroyablement l'ajout de fonctionnalités.

Nous verrons comment j'ai créé GitHub Pipeline, un plugin qui vous permet d'afficher les données de l'API GitHub sur les pages WordPress à l'aide de shortcodes. Je vais donner des exemples spécifiques et des extraits de code, mais considérez la technique décrite ici comme un modèle pour savoir comment consommer n'importe quelle API de service avec un plugin.

Lectures complémentaires sur SmashingMag :

  • WordPress Essentials : Comment créer un plugin WordPress
  • Comment déployer des plugins WordPress avec GitHub à l'aide de transitoires
  • Trois approches pour ajouter des champs configurables à votre plugin

Nous allons commencer par le début, mais une certaine familiarité avec WordPress et le développement de plugins est supposée, et nous ne passerons pas de temps sur des sujets pour débutants, comme l'installation de WordPress ou de Composer.

Plus après saut! Continuez à lire ci-dessous ↓

Tu auras besoin:

  • un environnement PHP, avec une nouvelle installation de WordPress ;
  • un compte GitHub (ou un autre fournisseur d'API si vous souhaitez improviser) ;
  • Compositeur (recommandé).

Choisir une API

La première étape dans l'écriture de ce type de plugin est de choisir une API. Dans ce tutoriel, nous utiliserons l'API GitHub. Si vous envisagez d'utiliser une autre API, tenez compte de quelques facteurs importants qui peuvent affecter la récompense de votre projet.

L'une des premières choses que je regarde est la rigueur et la qualité de la documentation de l'API. S'il est clairsemé ou obsolète, soyez prêt à passer du temps à parcourir le code source pour répondre à vos propres questions. Tenez également compte de la maturité de l'API et de la responsabilité avec laquelle le fournisseur l'a versionnée. Rien n'est pire que d'investir du temps dans la création de quelque chose de génial, seulement pour qu'il casse à cause des changements apportés à l'API en amont. Recherchez un système de gestion des versions dans les URL des points de terminaison.

 // good https://api.stable.com/v1/user/ // danger https://api.dodgy.com/user/

Au risque d'énoncer une évidence, la programmation par rapport à une API tierce implique une relation de confiance, et toutes les API ne sont pas créées égales.

Magasiner pour une bibliothèque

Les marques établies distribuent souvent des bibliothèques ou des SDK pour faciliter le travail avec leur API. Si ce n'est pas le cas, n'oubliez pas de rechercher si quelqu'un d'autre a déjà écrit une bibliothèque avant de partir et de réinventer la roue. Google et GitHub sont deux excellents endroits pour commencer vos recherches. Le nombre d'étoiles ou de fourches sur GitHub est une bonne indication de l'efficacité d'une bibliothèque. L'âge des commits les plus récents et/ou le nombre de problèmes ouverts sont une indication de l'activité avec laquelle il est maintenu. Dans mon cas, j'ai eu la chance de trouver la belle API PHP GitHub, de KNP Labs.

Écrire le vôtre avec Guzzle

S'il n'y a pas de bibliothèque satisfaisante pour votre fournisseur, vous ne devriez toujours pas recommencer à zéro car des outils comme Guzzle facilitent beaucoup le travail avec les requêtes HTTP. Guzzle enveloppe la bibliothèque cURL de PHP et supprime bon nombre des maux de tête généralement associés à la configuration et à la création manuelle de requêtes. Même si vous ne faites qu'une ou deux requêtes, je vous recommande quand même de l'utiliser car cela rendra votre code plus robuste, et l'installer avec Composer est un jeu d'enfant.

Configuration du plugin

Nous commencerons par le squelette de base d'un plugin WordPress minimal, un répertoire avec deux fichiers. Le choix d'un nom de dossier descriptif et unique est important pour éviter les conflits avec d'autres plugins. Si le nom de votre plugin est quelque peu générique, envisagez d'ajouter un préfixe unique.

 github-api/ readme.txt github-api.php

Le readme.txt contient les métadonnées de votre plugin qui apparaîtront sur wordpress.org si vous décidez de le publier là-bas. En savoir plus sur la publication de plugins WordPress dans la documentation, ou consultez l'exemple complet readme.txt.

Le fichier PHP contient également des métadonnées dans l'en-tête qui seront utilisées pour afficher des informations sur votre plugin dans le tableau de bord. Commencez avec un en-tête qui ressemble à ceci :

 <?php /** Plugin Name: GitHub API description: >- Add GitHub project information using shortcode Version: 1.0 Author: Your Name License: GPLv2 or later Text Domain: github-api */

Pour des raisons de sécurité, il est également conseillé de refuser l'accès direct au fichier, comme ceci :

 defined( 'ABSPATH' ) or die( 'No script kiddies please!' );

À ce stade, le plugin ne fera rien, mais lorsque vous copiez les fichiers dans wp-content/plugins , il devrait apparaître dans la liste des plugins et vous devriez pouvoir l'activer.

Page des plugins du tableau de bord WordPress
Page des plugins du tableau de bord WordPress. (Voir la grande version)

Ensuite, nous voudrons inclure la bibliothèque qui gérera les requêtes API. Dans l'exemple de code suivant, nous incluons l'API PHP GitHub de KNP Labs, mais vous pouvez inclure n'importe quelle dépendance en remplaçant knplabs/github-api par le package que vous utilisez.

 $ cd wp-content/plugins/github-api $ composer require knplabs/github-api

Maintenant, votre structure de fichiers devrait ressembler à ceci :

 github-api/ composer.json composer.lock github-api.php readme.txt vendor/

Maintenant que les fichiers sont en place, nous devons exiger le fichier vendor/autoload.php qui a été créé lorsque vous l'avez installé.

 require_once 'vendor/autoload.php';

Si tout a été configuré correctement, vous devriez pouvoir instancier une classe à partir de la bibliothèque sans qu'une erreur fatale ne soit générée.

 $testing = new \Github\Client();

Mais ce n'est qu'un moyen rapide de tester que vos dépendances sont disponibles. Je recommande de définir une nouvelle classe qui étend la bibliothèque.

 class MyGithub extends \Github\Client {};

Ce n'est pas critique, mais cela rend votre code plus flexible de plusieurs façons. Le moyen le plus évident est que vous pouvez modifier ou ajouter de nouvelles fonctionnalités si vous en avez besoin. Mais cela facilite également la vie si vous souhaitez changer de bibliothèque à l'avenir, car vous n'aurez à effectuer les modifications qu'une seule fois, plutôt que dans tout votre code.

Codes courts WordPress

Il est maintenant temps de configurer notre premier shortcode, qui est un extrait spécial qui vous permet de générer du contenu en le plaçant dans un article ou une page. Si vous êtes complètement novice en matière de shortcodes ou si vous souhaitez mieux comprendre leur fonctionnement, consultez la documentation officielle sur les shortcodes. Dans mon exemple, je vais afficher une liste de problèmes GitHub partout où l'auteur place ce shortcode : [github_issues]

Comme nous l'avons fait précédemment, commençons par un exemple minimal pour nous assurer que le shortcode est correctement enregistré avant de travailler sur l'appel de l'API.

 function github_issues_func( $atts ) { return "Hello world!"; } add_shortcode( "github_issues", "github_issues_func" );

Maintenant, si vous publiez une page contenant [github_issues] , vous devriez voir "Hello world" lorsque vous visitez la page. Maintenant que cela fonctionne, configurons l'appel API.

Dans la section précédente, nous avons configuré le chargement automatique pour notre bibliothèque GitHub et défini notre propre classe pour l'étendre. Maintenant, nous pouvons commencer à l'utiliser pour effectuer des appels d'API. Nous allons donc l'ajouter à la fonction de rappel du shortcode. Comme preuve de concept, récupérons tous les problèmes dans le référentiel GitHub Pipeline. La documentation de cette méthode est disponible.

 function github_issues_func( $atts ) { // Instantiate our class $gh = new MyGithub(); // Make the API call to get issues, passing in the GitHub owner and repository $issues = $gh->api('issue')->all('TransitScreen', 'wp-github-pipeline'); // Handle the case when there are no issues if ( empty($issues) ) return "<strong>" . __("No issues to show", 'githup-api') . "</strong>"; // We're going to return a string. First, we open a list. $return = "<ul>"; // Loop over the returned issues foreach( $issues as $issue ) { // Add a list item for each issue to the string // (Feel free to get fancier here) // Maybe make each one a link to the issue issuing $issue['url] ) $return .= "<li>{$issue['title']}</li>"; } // Don't forget to close the list $return .= "</ul>"; return $return; } add_shortcode( 'github_issues', 'github_issues_func' );

Vous devriez maintenant pouvoir afficher la même page que celle que nous avons utilisée ci-dessus pour tester le shortcode, et cette fois, vous devriez voir une liste non ordonnée de problèmes. Mais attendez! Avant de continuer, faisons une optimisation pour que notre code soit plus facile à tester. (Vous testez, n'est-ce pas ? !) Au lieu d'utiliser new dans notre fonction pour instancier la classe de bibliothèque GitHub, passons-la en tant que paramètre et instancions-la uniquement si nécessaire.

 function github_issues_func( $atts, $gh=null ) { // Conditionally instantiate our class $gh = ( $gh ) ? $gh : new MyGithub(); …

Cette modification ressemble au modèle d'injection de dépendance, ce qui rend notre fonction beaucoup plus facile à tester. Les tests unitaires WordPress dépassent le cadre de ce didacticiel, mais il est facile de voir comment cette nouvelle version nous permet de transmettre facilement de "fausses" données d'API à la fonction afin que les assertions de notre test sachent exactement à quoi s'attendre.

Ce que nous avons fait jusqu'à présent est très bien pour un projet interne qui ne sera utilisé que sur un seul référentiel, mais il serait beaucoup plus utile si les utilisateurs pouvaient configurer le référentiel à partir duquel récupérer les problèmes. Mettons cela en place.

Tout d'abord, nous allons utiliser un crochet WordPress pour enregistrer notre nouvelle page de paramètres, et la fonction de rappel définira les étiquettes et les titres du menu. Nous utiliserons la même approche incrémentielle pour le faire fonctionner.

 // Register the menu. add_action( "admin_menu", "gh_plugin_menu_func" ); function gh_plugin_menu_func() { add_submenu_page( "options-general.php", // Which menu parent "GitHub", // Page title "GitHub", // Menu title "manage_options", // Minimum capability (manage_options is an easy way to target administrators) "github", // Menu slug "gh_plugin_options" // Callback that prints the markup ); } // Print the markup for the page function gh_plugin_options() { if ( !current_user_can( "manage_options" ) ) { wp_die( __( "You do not have sufficient permissions to access this page." ) ); } echo "Hello world!"; }

Vous devriez maintenant pouvoir vous connecter au tableau de bord et voir le nouveau menu GitHub sous "Paramètres", puis cliquer dessus pour voir une page de paramètres vierge avec "Hello world!"

Page de plugins WordPress vide
Page de plugins WordPress vide. (Voir la grande version)

Ensuite, nous remplacerons le Hello word par le balisage réel du formulaire. Je vais vous donner un exemple simple que vous pouvez styliser aussi peu ou autant que vous le souhaitez.

 ?> <form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="update_github_settings" /> <h3><?php _e("GitHub Repository Info", "github-api"); ?></h3> <p> <label><?php _e("GitHub Organization:", "github-api"); ?></label> <input class="" type="text" name="gh_org" value="<?php echo get_option('gh_org'); ?>" /> </p> <p> <label><?php _e("GitHub repository (slug):", "github-api"); ?></label> <input class="" type="text" name="gh_repo" value="<?php echo get_option('gh_repo'); ?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Save", "github-api"); ?>" /> </form> <?php

Les classes CSS que j'ai incluses correspondent à celles utilisées par le noyau de WordPress, ce qui est un bon moyen de donner à votre page d'options personnalisées un aspect décent sans effort supplémentaire.

Page de paramètres WordPress personnalisée avec des styles de base
Page de paramètres WordPress personnalisée avec des styles de base. (Voir la grande version)

Il y a deux choses importantes à comprendre à propos de ce formulaire. Tout d'abord, le point de terminaison auquel il se soumet doit être /wp-admin/admin-post.php . Vous pouvez coder ce chemin en dur, mais si le site Web est installé dans un sous-répertoire, cela ne fonctionnera pas. Donc, nous utilisons le admin_url() pour le créer dynamiquement.

Deuxièmement, notez l'entrée masquée avec le nom action . Ce champ indique comment WordPress sait quoi faire avec la demande de publication soumise par le formulaire. La value correspond au nom du crochet d'action que nous utiliserons pour définir la fonction de rappel. Le nom de l'action à laquelle nous nous connecterons sera cette valeur, préfixée par admin post . Donc, dans notre cas, nous devons ajouter ceci :

 add_action( 'admin_post_update_github_settings', 'github_handle_save' );

Je trouve que c'est l'un des aspects les plus bizarres et les moins intuitifs de la création de menus d'administration personnalisés, mais une fois que vous vous y êtes habitué, c'est assez indolore. Comme vous l'avez peut-être deviné, le deuxième paramètre de add_action() est le nom de notre fonction de rappel qui enregistrera les valeurs dans la base de données.

 function github_handle_save() { // Get the options that were sent $org = (!empty($_POST["gh_org"])) ? $_POST["gh_org"] : NULL; $repo = (!empty($_POST["gh_repo"])) ? $_POST["gh_repo"] : NULL; // Validation would go here // Update the values update_option( "gh_repo", $repo, TRUE ); update_option("gh_org", $org, TRUE); // Redirect back to settings page // The ?page=github corresponds to the "slug" // set in the fourth parameter of add_submenu_page() above. $redirect_url = get_bloginfo("url") . "/wp-admin/options-general.php?page=github&status=success"; header("Location: ".$redirect_url); exit; }

L'exemple est également assez minime. Dans un environnement de production, vous souhaiterez probablement ajouter une validation. De plus, dans cet exemple, nous renvoyons automatiquement status=success à l'URL. Le balisage du formulaire ne l'utilise pas encore. Pour afficher conditionnellement un message de réussite, ajoutez quelque chose comme ce qui suit au-dessus du formulaire dans gh_plugin_options() :

 if ( isset($_GET['status']) && $_GET['status']=='success') { ?> <div class="updated notice is-dismissible"> <p><?php _e("Settings updated!", "github-api"); ?></p> <button type="button" class="notice-dismiss"> <span class="screen-reader-text"><?php _e("Dismiss this notice.", "github-api"); ?></span> </button> </div> <?php }

Si vous ajoutez une validation, utilisez ce même modèle pour renvoyer différents statuts et messages afin que l'utilisateur sache si et pourquoi sa soumission du formulaire a échoué.

Vous devriez maintenant pouvoir enregistrer les nouvelles valeurs de propriétaire et de référentiel. Testez-le en entrant le propriétaire et le référentiel de tout projet public sur GitHub. La dernière étape consiste à revenir à la fonction de rappel de code github_issues_func() et à remplacer les valeurs de propriétaire et de référentiel codées en dur.

 … $issues = $gh->api("issue")->all(get_option("gh_org"), get_option("gh_repo")); …

Une fois que cela est en place, revisitez la page où vous avez ajouté le shortcode. Vous devriez maintenant voir les problèmes du projet que vous avez défini.

Partie bonus : authentification OAuth 2.0

L'approche que nous avons utilisée ci-dessus fonctionne très bien pour les référentiels publics, mais que se passe-t-il si nous voulons utiliser ce plugin avec un référentiel privé qui nécessite une authentification ? L'API GitHub s'authentifie à l'aide du protocole OAuth 2.0, ce que vous rencontrerez en travaillant avec les API les plus populaires de nos jours. Le flux de travail OAuth 2.0 de base ressemble à ceci :

  1. Vous enregistrez une application (parfois appelée « client ») auprès du fournisseur et recevez un identifiant unique et une clé secrète.
  2. Votre application envoie une demande au point de terminaison d'authentification du fournisseur, en transmettant les informations d'identification ci-dessus ainsi qu'une URL de redirection que le fournisseur utilise pour rediriger la demande vers un point de terminaison de votre application.
  3. L'utilisateur est alors invité à accepter votre demande d'accès. Si tel est le cas, le fournisseur utilise l'URL que vous avez envoyée avec la demande pour rediriger l'utilisateur vers votre application, ainsi qu'un code temporaire.
  4. Votre application capture ce code, puis effectue une deuxième demande, en transmettant ce code au fournisseur. Le fournisseur répond avec un jeton d'accès, que votre application utilise ensuite pour s'authentifier auprès du fournisseur.

Nous allons commencer par enregistrer une application auprès de GitHub. Comme pour la plupart des API populaires, cette section se trouve dans la zone développeur du site Web.

Nouvelle page d'enregistrement d'application GitHub
La nouvelle page d'enregistrement des applications de GitHub. (Voir la grande version)

La chose la plus importante ici est l'URL de rappel d'autorisation. L'URL de redirection transmise à l'étape 3 doit correspondre à ce que vous entrez ici ou l'inclure. Ainsi, lors du développement, j'entre généralement dans la page d'accueil du site Web. De cette façon, mon application peut rediriger vers n'importe quel chemin.

Ensuite, nous ajouterons un deuxième formulaire à notre page de paramètres, dans gh_plugin_options() , pour soumettre l'ID client et le secret de l'application.

 <form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="oauth_submit" /> <h3>Oauth 2.0</h3> <p> <label><?php _e("GitHub Application Client ID:", "github-api"); ?></label> <input class="" type="text" name="client_id" value="<?php echo get_option('client_id')?>" /> </p> <p> <label><?php _e("GitHub Application Client Secret:", "github-api"); ?></label> <input class="" type="password" name="client_secret" value="<?php echo get_option('client_secret')?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Authorize", "github-api"); ?>" /> </form>

Pour me simplifier la vie, j'utiliserai le fournisseur GitHub pour le client OAuth 2.0 de The League of Extraordinary Packages. Donc, d'abord, utilisons à nouveau Composer pour ajouter la dépendance :

 composer require league/oauth2-github

Notez que la bibliothèque GitHub de KNP Labs intègre également la prise en charge d'OAuth 2.0. Ainsi, dans mon exemple spécifique, cela est quelque peu redondant. Mais je voulais présenter cette bibliothèque car elle appartient à une suite de bibliothèques clientes OAuth 2.0 spécifiques au fournisseur qui étendent toutes le même cadre maintenu par la puissante League of Extraordinary Packages. Vous pouvez afficher une liste complète des fournisseurs pris en charge ou lire des instructions sur la façon d'étendre le cadre pour prendre en charge un nouveau fournisseur.

Créons une fonction pour demander et enregistrer le jeton lorsque l'utilisateur soumet le formulaire. Étant donné que GitHub est l'un des fournisseurs déjà pris en charge, je peux copier l'exemple dans sa documentation avec seulement quelques modifications.

 function handle_oauth() { // If the form was just submitted, save the values // (Step 1 above) if ( isset($_POST["client_id"]) && isset($_POST["client_secret"]) ) { update_option( "client_id", $_POST["client_id"], TRUE ); update_option("client_secret", $_POST["client_secret"], TRUE); } // Get the saved application info $client_id = get_option("client_id"); $client_secret = get_option("client_secret"); if ($client_id && $client_secret) { $provider = new League\OAuth2\Client\Provider\Github([ "clientId" => $client_id, "clientSecret" => $client_secret, "redirectUri" => admin_url("options-general.php?page=github"), ]); } // If this is a form submission, start the workflow // (Step 2) if (!isset($_GET["code"]) && $_SERVER["REQUEST_METHOD"] === "POST") { // If we don't have an authorization code, then get one $authUrl = $provider->getAuthorizationUrl(); $_SESSION["oauth2state"] = $provider->getState(); header("Location: ".$authUrl); exit; // Check given state against previously stored one to mitigate CSRF attack // (Step 3 just happened and the user was redirected back) } elseif (empty($_GET["state"]) || ($_GET["state"] !== $_SESSION["oauth2state"])) { unset($_SESSION["oauth2state"]); exit("Invalid state"); } else { // Try to get an access token (using the authorization code grant) // (Step 4) $token = $provider->getAccessToken("authorization_code", [ "code" => $_GET["code"] ]); // Save the token for future use update_option( "github_token", $token->getToken(), TRUE ); } }

Et, comme nous l'avons fait avec l'autre formulaire, nous devons ajouter le crochet d'action pour que la fonction soit appelée lorsque le formulaire est enregistré.

 add_action( "admin_post_oauth_submit", "handle_oauth" );

L'enregistrement du formulaire devrait maintenant vous envoyer à la page d'autorisation du fournisseur d'API. Après autorisation, votre application peut utiliser le jeton enregistré pour demander des données à des référentiels privés auxquels vous avez accès. La bibliothèque que j'utilise par KNP Labs a une méthode pratique pour cela.

 $gh = new MyGithub(); $gh->authenticate( get_option("github_token"), NULL, Github\Client::AUTH_HTTP_TOKEN);

Les bibliothèques différeront sur la façon précise dont elles gèrent l'authentification, mais d'une manière ou d'une autre, vous transmettrez le jeton, qui fera ensuite des demandes authentifiées au nom d'un utilisateur.

Conclusion

Nous avons couvert beaucoup de terrain, et j'ai essayé de garder les exemples aussi minimes que possible afin que le flux de travail global reste clair. Le code source complet de ce tutoriel est disponible. J'espère que vous avez maintenant une compréhension claire des éléments mobiles impliqués dans la création d'un plugin WordPress qui consomme des API de services tiers, et j'espère que vous êtes inspiré pour écrire votre propre plugin d'API WordPress.

Remarques finales

  • Codex WordPress (documentation)
  • API GitHub (documentation)
  • OAuth 2.0 (documentation)
  • Compositeur (documentation)
  • La Ligue des forfaits extraordinaires
  • Documentation Guzzle