Comment aborder le développement WordPress moderne (Partie 2)

Publié: 2022-03-11

WordPress est la technologie de site la plus utilisée sur la planète et pour cause. Pourtant, le code hérité dans son cœur est un gâchis, et ce problème se répercute sur les développeurs tiers. Certains développeurs prennent cela comme une excuse pour couper les coins ronds dans leur propre code PHP WordPress, mais cette approche est plus coûteuse à long terme pour tous les changements, sauf les plus insignifiants.

Dans la partie 1 de notre série en deux parties, nous nous sommes concentrés sur les outils globaux de projet et de flux de travail, suivis du développement frontal. Il est maintenant temps de prendre le taureau par les cornes et de lutter avec PHP : plus précisément, comment suivre les meilleures pratiques lorsque vous travaillez avec du code WordPress back-end. Vous pourriez considérer cela comme un tutoriel PHP/WordPress, mais un peu plus avancé, en supposant que vous avez déjà effectué une personnalisation du back-end WordPress.

Alors, quels principes de conception de logiciels modernes et fonctionnalités PHP vous donneront le meilleur rapport qualité-prix ? Voici 10 pratiques de développement WordPress et PHP que je recommande fortement.

Meilleure pratique de développement WordPress moderne n ° 1 : suivez la « séparation des préoccupations »

La séparation des préoccupations signifie que les parties du code PHP WordPress ayant des fonctionnalités ou des objectifs différents ne doivent pas être mélangées. Au lieu de cela, ils doivent être organisés en sections ou modules distincts, se transmettant des données via une interface définie. (Une interface est un ensemble de paramètres définis qu'un module prend en entrée et ce qu'il renvoie.) Un terme étroitement lié est le principe de responsabilité unique : chaque module de code (ou fonction) ne doit être responsable que d'une seule chose.

Le but ultime de suivre ces principes est de produire un code modulaire, et donc maintenable, extensible et réutilisable.

C'était assez long, alors regardons un exemple de WordPress PHP (du noyau de WordPress) qui emmêle tout. Ce style de codage est souvent appelé "code spaghetti" car il est presque impossible de comprendre son fonctionnement interne. L'extrait ci-dessous a été expurgé par souci de brièveté ; cependant, le style et la mise en forme d'origine ont été conservés.

 $id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; <table class="form-table"> <?php $blog_prefix = $wpdb->get_blog_prefix( $id ); $sql = "SELECT * FROM {$blog_prefix}options WHERE option_name NOT LIKE %s AND option_name NOT LIKE %s"; $query = $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', '%' . $wpdb->esc_like( 'user_roles' ) ); $options = $wpdb->get_results( $query ); foreach ( $options as $option ) { if ( strpos( $option->option_value, "\n" ) === false ) { ?> <tr class="form-field"> <th scope="row"><label for="<?php echo esc_attr( $option->option_name ); ?>"><?php echo esc_html( ucwords( str_replace( '_', ' ', $option->option_name ) ) ); ?></label></th> <?php if ( $is_main_site && in_array( $option->option_name, array( 'siteurl', 'home' ) ) ) { ?> <td><code><?php echo esc_html( $option->option_value ); ?></code></td> <?php } else { ?> <td><input class="<?php echo $class; ?>" name="option[<?php echo esc_attr( $option->option_name ); ?>]" type="text" value="<?php echo esc_attr( $option->option_value ); ?>" size="40" <?php disabled( $disabled ); ?> /></td> <?php } ?> </tr> <?php } } // End foreach </table>

Tout d'abord, c'est totalement incompréhensible. Et j'aime que le seul commentaire soit End foreach , qui est complètement redondant. Nous avons l'interrogation de la base de données, le traitement des résultats de la requête, un traitement supplémentaire intégré dans HTML (il y a un if / else imbriqué ici si vous ne l'aviez pas remarqué), l'échappement de sortie et la modélisation HTML, tous mélangés. Un autre problème est le paramètre $id venant directement du $_REQUEST global au lieu de passer un paramètre réel à une fonction.

En regardant cela, il est parfaitement compréhensible que le noyau de WordPress soit resté pratiquement le même pendant des années. Refactoriser ce type de code, en particulier tout en conservant le comportement existant, est une tâche vraiment épique que personne ne voudrait faire.

Alors comment le faire correctement ? Eh bien, une chose à retenir est qu'il n'y a pas qu'un seul vrai chemin. Nous avons mentionné ci-dessus les qualités que nous devrions rechercher : Nous avons besoin que le code PHP personnalisé de WordPress soit maintenable et modulaire. Voyons comment nous pouvons diviser le code ci-dessus en modules.

  • Les requêtes SQL doivent être dans un module séparé, évidemment. WordPress a déjà une classe WP_Query bien abstraite qui devrait être utilisée comme exemple.
  • Tout le HTML va dans un modèle. Nous aborderons les modèles PHP plus loin.
  • Le code PHP restant doit être encapsulé dans une fonction - plusieurs fonctions si le code est trop long ou complexe pour une fonction. Les paramètres tels que $id sont passés via des arguments de fonction.

Il s'agit d'une réécriture très simplifiée de l'exemple ci-dessus :

 function betterSiteSettings($args) { $data = WP_Settings_Query($args); // process $data here $context = array_merge([], $data_processed, $other_data); return Template::render('template.name', $context); }

Meilleure pratique de développement WordPress moderne #2 : Évitez les variables globales

WordPress a beaucoup trop de variables globales. Pourquoi les variables globales sont-elles mauvaises ? Ils rendent votre code PHP WordPress difficile à suivre et rendent l'état de l'application peu fiable. Tout morceau de code PHP - et cela signifie tout plugin installé dans WordPress - peut lire et écrire des variables globales, il n'y a donc aucune garantie qu'elles contiennent des données valides. Essayer de comprendre quelles variables globales sont utilisées dans quelque chose comme la boucle n'est pas non plus une tâche triviale.

Regardons cela sous un angle pratique. Cet exemple provient de WooCommerce. Tous les développeurs WordPress savent probablement ce que c'est : la boucle :

 <?php while ( have_posts() ) : the_post(); ?> <?php wc_get_template_part( 'content', 'single-product' ); ?> <?php endwhile; // end of the loop. ?>

L'extrait ci-dessus affiche un modèle de produit. Comment sait-il quel produit afficher, étant donné qu'il n'y a pas de paramètres passés à wc_get_template_part ? En regardant le modèle, nous voyons qu'il commence par global $product; , c'est donc là que l'objet produit actuel est stocké.

Imaginons maintenant que nous ayons une page de catalogue qui recherche et filtre les produits, et que nous voulions afficher une fenêtre contextuelle "détails du produit" tout en restant sur la même page. Dans les coulisses, le script frontal effectue une requête AJAX pour obtenir ce modèle de produit particulier. Nous ne pouvons pas simplement appeler wc_get_template_part('content', 'single-product') car il n'utilise pas de paramètres, nous devons donc définir quelques variables globales pour que cela fonctionne.

Des cas d'utilisation plus complexes impliqueraient plus d'un modèle, des crochets déclenchés dans ces modèles et des plugins tiers ajoutant leurs rappels à ces crochets. Cela peut s'aggraver rapidement. Nous n'avons aucun moyen de savoir sur quel état global ces rappels reposent. Les plugins tiers sont libres de modifier n'importe quelle variable globale dans leurs rappels. Au lieu d'utiliser le système, nous commençons à nous battre avec le système, rencontrant des bogues étranges qui proviennent d'un état global peu fiable.

Ne serait-il pas plus judicieux de passer cet ID produit en paramètre ? Ensuite, nous pourrions réutiliser ce modèle sans nous soucier de gâcher les variables globales utilisées par WordPress.

Meilleure pratique de développement WordPress moderne #3 : Utiliser la programmation orientée objet (POO)

La modularité conduit au concept d'objets et à la programmation orientée objet. Au niveau le plus basique, la POO est une façon d'organiser le code. Les fonctions et les variables sont regroupées dans des classes et sont appelées respectivement méthodes et propriétés de classe. Le manuel du plugin WordPress recommande d'utiliser la POO pour organiser votre code PHP personnalisé WordPress.

Un principe important de la POO est de limiter l'accès aux méthodes et aux propriétés - ou en termes PHP, de les désigner comme private ou protected - afin que seules les autres méthodes de classe puissent y accéder et les modifier. Un terme OOP pour cela est encapsulation : les données sont encapsulées à l'intérieur de la classe, et la seule façon de modifier ces données est d'utiliser les méthodes de classe fournies.

Cela rend le débogage et la maintenance de votre code beaucoup plus faciles que lors de l'utilisation de variables globales qui peuvent être modifiées n'importe où dans l'ensemble de la base de code. Considérez la variable de post globale de WordPress. Vous pouvez y accéder n'importe où dans votre code, et de nombreuses fonctionnalités dépendent de son utilisation. Et si vous pouviez limiter les modifications aux seules fonctions de base de WordPress, mais la lecture serait autorisée pour n'importe qui ? Cacher ou encapsuler la variable post globale dans une classe et construire une interface autour d'elle rendrait cela possible.

Ceci n'est qu'une description très basique de la POO et de la façon dont elle peut être utilisée dans le développement WordPress moderne. Pour une étude plus approfondie, je recommande vivement le livre électronique de Carl Alexander, Découvrez la programmation orientée objet à l'aide de WordPress, qui contient le contenu le plus complet et le plus utile disponible sur le sujet de la POO dans WordPress.

Il est important de se rappeler que la POO n'est pas une solution miracle : un mauvais code peut être écrit avec la POO aussi facilement qu'avec n'importe quel autre paradigme de programmation.


Plongeons-nous dans quelques conseils spécifiques sur l'utilisation de PHP pour le développement WordPress.

Meilleure pratique PHP moderne #1 : Ciblez PHP 7.0+

L'utilisation des fonctionnalités PHP modernes nécessite, eh bien, une version moderne de PHP. Il n'y a tout simplement aucune raison de prendre en charge les versions PHP inférieures à 7.0. Même le cœur de WordPress nécessitera PHP 7.0 dès la fin de 2019.

Néanmoins, c'est une bonne pratique de vérifier votre version minimale pour éviter «l'écran blanc de la mort» dans des environnements incompatibles. L'extrait ci-dessous montre comment utiliser un en-tête de plugin pour déclarer une version minimale de PHP avec une condition de garde dans le code.

 <?php /** * Plugin Name: My Awesome Plugin * Requires PHP: 7.0 */ // bails if PHP version is lower than required if (version_compare(PHP_VERSION, '7.0.0', '<')) { // add admin notice here return; } // the rest of the actual plugin here

Meilleure pratique PHP moderne #2 : Adoptez les normes de l'industrie PHP (Guide de style de codage PSR-2)

Les PSR sont des recommandations publiées par le PHP Framework Interop Group. Ce sont les normes de facto de l'industrie dans tout flux de travail PHP moderne, et on peut dire en toute sécurité que la communauté PHP dans son ensemble suit ces normes. PSR-2 est une recommandation qui décrit le style de codage. Les frameworks PHP populaires tels que Symfony et Laravel suivent PSR-2.

Pourquoi utiliseriez-vous PSR-2 au lieu de la norme de codage WordPress ? Principalement parce que les normes WordPress sont obsolètes et n'utilisent aucune des nouvelles fonctionnalités du langage. C'est compréhensible car le noyau de WordPress doit suivre ses propres normes. Il devait supporter PHP 5.2 jusqu'à très récemment, et PSR-2 n'est pas compatible avec PHP 5.2.

Ce n'est peut-être pas évident, mais il n'est pas nécessaire d'utiliser les normes de codage WordPress à moins que vous ne vous engagiez au cœur. Il n'y aurait aucun problème à soumettre un plugin qui suit la norme PSR-2 dans le répertoire des plugins WordPress. En fait, il y a de très bons arguments pour le faire.

Meilleure pratique PHP moderne #3 : Utiliser un moteur de template PHP

PHP n'est pas un moteur de template. Il a commencé comme un, mais a ensuite évolué vers un langage de programmation complet, et il n'y a aucune raison de continuer à l'utiliser pour la création de modèles. Les deux moteurs de modèles les plus populaires pour PHP sont Twig et Blade, qui sont utilisés respectivement par Symfony et Laravel. Cet article va utiliser Twig comme exemple de moteur de template ; cependant, Blade a des caractéristiques et des fonctionnalités comparables. Je vous implore d'examiner les deux et de décider par vous-même ce qui vous convient le mieux.

L'exemple ci-dessous compare un template PHP et son template Twig correspondant. L'affichage et l'échappement de la sortie sont particulièrement verbeux dans l'exemple PHP :

 foreach ( $options as $option ) { ?> <tr class="form-field"> <th scope="row"> <label for="<?php echo esc_attr( $option->option_name ); ?>"> <?php echo esc_html( strtolower( $option->option_name ) ); ?> </label> </th> </tr> <?php } // End foreach

Dans Twig, c'est plus concis et lisible :

 {% for option in options %} <tr class="form-field"> <th scope="row"> <label for="{{ option.option_name }}"> {{ option.option_name }} </label> </th> </tr> {% endfor %}

Les principaux avantages de Twig sont :

  • Syntaxe lisible et concise
  • Sortie automatique s'échappant
  • Extension de modèle via l'héritage et les blocs

En termes de performances, Twig se compile en modèles PHP et n'a pratiquement aucune surcharge. Twig n'a qu'un sous-ensemble de constructions de langage PHP limité à la création de modèles uniquement. Cela oblige les développeurs à supprimer la logique métier des modèles, imposant ainsi la séparation des préoccupations.

Il existe même Twig pour WordPress. C'est ce qu'on appelle Timber, et c'est un excellent moyen de commencer à créer de meilleurs modèles. Le thème de démarrage Timber est un exemple parfait d'organisation de thèmes à la manière OOP.

Meilleure pratique PHP moderne #4 : Utiliser Composer

Composer est un gestionnaire de dépendances pour PHP. C'est un outil qui permet de déclarer les bibliothèques qu'un projet utilise, puis d'automatiser leurs téléchargements, installations et mises à jour. Ensuite, il vous suffit d'inclure le fichier de chargement automatique vendor/autoload.php de Composer au lieu d'exiger manuellement chaque bibliothèque.

Les plugins et thèmes WordPress n'utilisent pas souvent de bibliothèques tierces. C'est en partie parce que WordPress possède une API étendue qui répond à presque tous les besoins, et en partie à cause d'éventuels conflits de version. Considérez deux plugins nécessitant la même bibliothèque PHP mais des versions différentes de celle-ci. Le plugin qui s'exécute en premier obtient la version correcte et le second plugin obtient également cette version. Il s'agit très probablement d'une autre situation d'écran blanc de la mort.

Pour éviter les conflits, la gestion des dépendances doit être utilisée au niveau de l'application, c'est-à-dire du site WordPress dans son ensemble. C'est ce que fait Roots (Bedrock, plus précisément). Lorsqu'il est utilisé au niveau du package (plugin ou thème), Composer peut provoquer des conflits lors de l'utilisation de bibliothèques tierces. C'est un problème connu. La seule solution qui existe jusqu'à présent consiste à renommer les espaces de noms de cette bibliothèque externe en quelque chose d'unique, et ce n'est pas une tâche triviale.

Cependant, Composer a toujours une utilité : le chargement automatique de vos propres classes. Mais avant d'aller plus loin avec le chargement automatique, nous devons nous familiariser avec les espaces de noms PHP.

Meilleure pratique PHP moderne #5 : Utiliser des espaces de noms

Le noyau de WordPress étant un projet hérité, il utilise un espace de noms global ou, dit différemment, aucun espace de noms du tout. Toutes les classes ou fonctions déclarées globalement (c'est-à-dire pas à l'intérieur d'une autre classe ou fonction) sont visibles n'importe où dans l'ensemble de la base de code. Leurs noms doivent être uniques non seulement dans votre base de code, mais aussi pour tous les plugins et thèmes qui sont utilisés actuellement ou qui pourraient être utilisés à l'avenir.

La collision de noms (par exemple, déclarer une fonction avec un nom qui existe déjà) conduit généralement à l'écran blanc de la mort, et nous ne le voulons pas. Le Codex WordPress conseille de préfixer toutes vos fonctions et classes avec quelque chose d'unique. Ainsi, au lieu d'avoir des noms de classe simples comme Order , nous obtenons quelque chose comme Akrte_Awesome_Plugin_Order où "Akrte" est le préfixe unique que je viens d'inventer.

Les espaces de noms peuvent être considérés comme des groupes - ou des dossiers, si nous utilisons une analogie avec le système de fichiers - qui aident à organiser le code et à éviter les collisions de noms. Vous pouvez avoir des espaces de noms complexes séparés par une barre oblique, tout comme les dossiers imbriqués. (Les espaces de noms PHP utilisent en particulier une barre oblique inverse.)

Ces parties d'espace de noms sont appelées sous-espaces de noms. Notre exemple de classe Akrte_Awesome_Plugin_Order serait Akrte\Awesome_Plugin\Order si cela était fait en utilisant des espaces de noms. Ici, Akrte et Awesome_Plugin sont des parties d'espace de noms (ou sous-espaces de noms) et Order est le nom de la classe. Ensuite, vous pouvez ajouter une instruction use et utiliser uniquement le nom de la classe par la suite. C'est définitivement mieux :

 use Akrte\Awesome_Plugin\Order; $a = new Order;

De toute évidence, les espaces de noms doivent être uniques ; ainsi, nous devrions donner au premier sous-espace de noms "racine" un nom unique, et c'est généralement un nom de fournisseur. Par exemple, la classe WooCommerce WC_REST_Order_Notes_V2_Controller pourrait être refaite avec des espaces de noms comme celui-ci :

 namespace WooCommerce\RestApi\V2\Controllers; class OrderNotes {}

La base de code WooCommerce utilise aujourd'hui des espaces de noms ; par exemple, dans la version 4 de l'API REST WooCommerce.

Meilleure pratique PHP moderne #6 : Utiliser un chargeur automatique

Dans la plupart des flux de travail PHP, la manière habituelle de lier des fichiers PHP est d'utiliser des instructions require ou include . Au fur et à mesure qu'un projet grandit, vous obtenez des dizaines d'instructions require dans votre fichier de plugin principal. Un chargeur automatique automatise l'inclusion de fichiers et ne le fait qu'en cas de besoin. Techniquement, c'est une fonction qui require un fichier sa contenant une classe ou une fonction lors de sa première rencontre dans le code. Il n'est plus nécessaire d'ajouter d'instructions require manuellement.

Souvent, il y a aussi un gain de performances significatif car un chargeur automatique ne charge que les modules qui sont utilisés dans une requête particulière. Sans chargeur automatique, toute votre base de code est incluse même si une requête n'utilise que, disons, 10 % de votre code.

Une fonction de chargement automatique doit savoir dans quels fichiers se trouvent vos classes et vos fonctions. Et il existe une norme PHP-FIG, PSR-4, pour cela.

Il indique qu'une partie d'un espace de noms, le préfixe, est désignée pour correspondre à un dossier de base. Les sous-espaces de noms qui le suivent correspondent aux dossiers à l'intérieur du dossier de base. Enfin, le nom de la classe correspond au nom du fichier. Un exemple de classe Akrte\AwesomePlugin\Models\Order aurait besoin de la structure de dossiers ci-dessous :

 /awesome-plugin awesome-plugin.php /includes /Models Order.php

Le préfixe d'espace de noms Akrte\AwesomePlugin\ correspond au dossier includes tel que spécifié dans la configuration de l'autochargeur décrite ci-dessous. Le sous-espace de noms Models a un dossier Models correspondant et la classe Order est contenue dans Order.php .

Heureusement, il n'est pas nécessaire d'implémenter vous-même une fonction de chargement automatique. Composer peut créer un chargeur automatique pour vous :

  1. Installer Compositeur
  2. Créez un fichier composer.json dans le dossier racine de votre projet. Il doit contenir ces lignes :
 { "name": "vendor-name/plugin-name", "require": {}, "autoload": { "psr-4": { "Akrte\\AwesomePlugin\\": "includes/" } } }
  1. Exécutez composer install .
  2. Incluez vendor/autoload.php en haut du fichier PHP principal de votre plugin comme ceci :
 <?php /** * Plugin Name: My Awesome Plugin */ defined('ABSPATH') || exit; require __DIR__ . '/vendor/autoload.php'; // do stuff

Outre les espaces de noms, la dernière base de code de WooCommerce utilise également un chargeur automatique Composer.


Une fois ces principes de conception PHP couverts, il est temps de lier toutes nos leçons PHP à la personnalisation du back-end WordPress avec une dernière recommandation.

Meilleure pratique de développement WordPress moderne #4 : Envisagez d'utiliser la pile Roots

Roots est le workflow de développement WordPress moderne le plus complet qui soit. Cependant, je dirais qu'il ne devrait pas nécessairement être utilisé dans tous les projets WordPress, car :

  • Roots doit être utilisé dès le départ. La raison la plus courante, vraiment. Refactoriser un projet existant serait trop coûteux.
  • C'est opiniâtre. Bon quand on est d'accord, mauvais quand on ne l'est pas. Vous préférerez peut-être d'autres façons d'organiser votre thème, par exemple. Les projets d'opinion nécessitent également du temps pour apprendre "leur chemin".
  • Tout le monde ne le sait pas. Le développeur qui viendra après vous pour maintenir le site que vous avez construit avec la pile Roots peut n'avoir aucune idée de ce que c'est et se demander ce qui s'est passé avec les dossiers WordPress. Nous devrions penser à nos collègues développeurs WordPress.

En général, vous voudriez bien comprendre tous les avantages et les inconvénients de tout projet fortement opiniâtre avant de vous engager à l'utiliser.

Principes PHP et logiciels modernes : Rendre le développement back-end WordPress robuste

Il est assez évident qu'il n'y a pas qu'une seule véritable façon d'écrire un logiciel. Les concepts, tels que la séparation des préoccupations, datent de plusieurs décennies ; cependant, sa signification pratique a toujours été contestée. Prenez, par exemple, CSS. Au début, nous l'avons intégré en tant que style en HTML, puis nous avons décidé que des feuilles CSS séparées étaient la séparation des préoccupations.

Avance rapide d'une décennie : les applications JavaScript actuelles utilisent des composants comme une implémentation de séparation des préoccupations. Les développeurs front-end gravitent autour de CSS-in-JS, ce qui signifie essentiellement intégrer à nouveau CSS dans HTML (enfin, ce n'est pas si simple, mais vous voyez l'idée). La boucle est bouclée !

Les meilleures pratiques ont toujours consisté à améliorer l'expérience des développeurs :

Les programmes doivent être écrits pour que les gens les lisent, et seulement accessoirement pour que les machines les exécutent.

Abelson & Sussman, Structure et interprétation des programmes informatiques

Certaines des pratiques de ce tutoriel PHP WordPress sont rapides et faciles à mettre en œuvre dans votre projet. Par exemple, autoloader : faites-le une fois par projet et profitez-en. D'un autre côté, les nouvelles idées d'architecture logicielle nécessitent du temps, de la pratique et de nombreuses itérations pour être pratiques et confortables. Les récompenses sont cependant bien plus importantes. Vous serez non seulement plus efficace dans ce que vous faites, mais vous apprécierez également davantage ce que vous faites. Et le plaisir régulier du travail que vous faites pour les clients est peut-être le seul moyen de le pérenniser.