Chapitre 4 sur 20

Conventions de code WordPress

Laisser un commentaire

Nous l’avons dit, WordPress n’est pas tout jeune. De ce fait, aussi éprouvé et efficace que soit son code, ce n’est pas le plus beau. Entre 2003 et aujourd’hui, les best practices ont évolué. Il n’est cependant ni possible de réécrire le code tous les 2 ans, ni souhaitable de casser la rétro-compatibilité.

Legacy code

Certains fichiers du code source de WordPress font un peu peur. Évidemment, le code marche et a été éprouvé par des millions de sites. De ce fait, on ne va pas tout réécrire juste pour le plaisir. Les core devs refactorisent le code lorsque c’est nécessaire et qu’ils le font évoluer.

Sans surprise, comme pour PHP, il y a des disparités dûes à l’histoire de l’outil : snake case vs camel case, ordre des paramètres, procédural vs objet etc.

Par ailleurs, vous devrez être vigileant sur ce point, certaines fonctions retournent les données tandis que d’autres les affichent directement. De manière générale, les fonctions préfixées par get_ retournent une valeur, tandis que les autres affichent ou effectuent une action.

get_the_ID(); // retourne l'id de l'élément courant
the_ID(); // affiche l'id de l'élément courant

Cependant get_footer et get_header ne répondent pas vraiment à cette convention car ces fonctions affichent respectivement le header et le footer.

Your code

WordPress possède du code qui n’est pas toujours le plus beau et moderne des codes. Qu’importe, car d’une part il est efficace, d’autre part nous n’avons pas tellement à aller le lire et encore moins à le modifier.

Étant donné que nous ne sommes pas tous développeurs PHP, nous n’avons pas forcément en tête les conventions de PHP. Par ailleurs, WordPress diverge quelque peu des conventions classiques.

Conventions de WordPress

WordPress propose ses propres conventions pour le PHP, mais aussi le HTML, le CSS et le JavaScript. Mon avis : à moins que vous ne codiez un thème ou plugin open source destiné à la communauté, peu importe.

Évidemment, si votre code est destiné à être repris par la communauté, autant en adopter les conventions. Sinon, l’important, comme dans tout projet, quel que soit le langage, c’est d’être cohérent.

Conventions de vos projets

En PHP, le standard est le PSR-1. Si vous êtes dev PHP et que vous respectez PSR dans tous vos projets, faites de même pour WordPress. Me concernant, j’aime l’homogénéité. J’apprécie la cohérence au sein des différentes parties d’un même projet (HTML, CSS, JS & PHP) et entre mes différents projets si possible.

Par exemple, la convention WordPress est de mettre des espaces entre les parenthèses et les arguments (structures de contrôle, function…). Pourtant, le style guide de PHP les déconseille :

There MUST be one space after the control structure keyword
There MUST NOT be a space after the opening parenthesis

Je ne fais pas ça non plus quand je code en JavaScript. Donc dans mon code WordPress, il n’y a pas d’espaces. Par exemple, en tant qu’afficionado du JS, j’utilise beaucoup les closures. Je formaterai les closures de la même manière en PHP qu’en JS.

// Espace entre function et ()
// ----------------> |
$anonymous = function () {

}

// Pas d'espace quand elle est nommée
function fonctionNormale() {

}

Par ailleurs, j’utiliserai aussi les mêmes conventions qu’en JS pour les accolades.

if (condition) { // crochet ouvrant sur la même ligne
    // bla bla
}

// Je laisse respirer le code avec une ligne vide
// avant la condition suivante (autre if, else, elseif, etc)
// qui est toujours sur sa propre ligne
else {
    // blu blu
}

Alors que WordPress conseille d’imbriquer.

// Question de goût, mais l'espace vertical est gratuit
if (condition) {

} else {

}

Les tableaux

PHP depuis sa version 5.4, supporte la syntaxe courte pour les array.

// Syntaxe classique
$array = array(…);

// Syntaxe courte
$array = […];

Comme toujours, c’est une histoire de goût. WordPress recommande la version longue, je préfère la courte (similaire à la syntaxe JavaScript).

Indentation

Ahhh ! C’est la partie la plus sensible du formatage du code. Des devs sont morts au combat pour défendre leur manière de faire. WordPress suggère les tabs tandis que PSR est partisan de 4 espaces. Comme partout ailleurs j’utilise 4 espaces, ce sera 4 espaces dans mon PHP aussi.

Soyez particulièrement méfiants à l’égards des espaces vs tabs. Comme ce ne sont pas des caractères visibles, on a vite fait de se retrouver avec un mix, surtout si vous copiez du code depuis le web.

Le mieux est de configurer votre éditeur de code pour transformer les tabs en espaces automatiquement. Ou d’afficher les caractères invisibles. En ce qui me concerne, je les affiche lorsqu’ils sont sélectionnés. Je peux donc visualiser cela si j’ai un doute.

Single vs double quotes

C’est une question que l’on se pose dans de nombreux langages : PHP bien entendu, mais aussi JavaScript et même HTML. En HTML la convention veut que les attributs soient entourés pas des guillemets doubles (même si les simples marchent aussi).

<p class="ma classe">
    Ceci est un paragraphe, youpi !
</p>

En revanche, en PHP, les simples et les doubles ne fonctionnent pas de la même manière. En effet, les doubles en PHP offrent l’interpolation de variables (de manière similaire aux templates string en JS).

$name = 'Buzut';
// Aucune différence ici avec
$name = "Buzut";

// En simple, il faut concaténer la chaîne
// pour placer la valeur d'une variable
$hello = 'Bonjour' . $name . ' comment ça va ?';

// Alors qu'avec le double quote
$hello = "Bonjour $name comment ça va ?";

// De plus, on peut marquer la limite de la variable
// si elle n'est pas évidente (pas d'espace)
$hello = "Bonjour {$name}, comment ça va ?";

De manière générale, on préfère donc l’usage des simples à l’exception des endroits où elles apportent un plus. On évite par exemple d’échapper une apostrophe : "What's up?" est plus clean que 'What\'s up?'.

Performance

Il y a aussi une différence de performance entre les deux formats. Étant donné que PHP interprète les valeurs qui peuvent se trouver dans les doubles, c’est forcément un peu moins performant.
Cependant, soyons honnête, il s’agit là de quelques ms de différence, si la partie la moins performante de votre code provient des quotes, alors chapeau, vous devriez postuler à la NASA !

Néanmoins, cette différence de performance est tout de même bonne à savoir, lorsque les deux formats conviennent, autant utiliser le plus rapide.

Si la gestion des String en PHP vous intéresse, voici un très bon article article [en] qui vous en apprendra d’avantage.

HTML dans le PHP

Avec WordPress, sauf si vous utilisez un moteur de template tel que Timber, vous générez le HTML directement depuis PHP. Il n’y a aucun problème avec ça, car c’est exactement pour cela que PHP a été conçu au départ. Cependant, comme pour les single vs double quotes, il y a plusieurs manières de faire.

Cela va sans dire, si vous utilisez echo pour des gros morceaux de HTML, cela va vite devenir illisible. Lorsque l’on est dans un fichier de template, la majeure partie du fichier est constitué de HTML. Autant qu’il soit directement en HTML plutôt que contenu dans le PHP.

De cette manière, on bénéficie de la coloration syntaxique du HTML et le PHP s’efface pour n’être que ce qu’il doit être dans ce fichier : un langage de template. Pour cela, il ne faut pas hésiter à souvent fermer et rouvrir les balises PHP.

// Exemple court avec HTML dans PHP
if (!empty($tabs)) {
    foreach ($tabs as $tab) {
        $category_slug = sanitize_title($tab['title']);
        echo '<a class="tab" href="#' . $category_slug . '">'. $tab['title'] . '</a>';
    }
}
// Même chose avec le HTML augmenté par le PHP
<?php if (!empty($tabs)) : ?>
    <?php foreach ($tabs as $tab) : ?>
        <a class="tab" href="#<?= sanitize_title($tab['title']) ?>">
            <?= $tab['title'] ?>
        </a>
    <?php endforeach; ?>
<?php endif; ?>

On ne s’en rend pas forcement compte dans cet exemple car il ne comporte qu’une ligne de HTML pour rester concis. Mais pensez qu’il faille générer des articles d’un blog. Vous aurez pour chacun : un titre, une photo, une date, l’auteur, des tags, un extrait et autant de balises div, article et header ; le tout sur une dizaine de lignes.

Vous vous voyez faire un echo pour chaque ligne et avoir tout cet HTML sans bénéficier de coloration syntaxique ? Impensable ! Il y a deux choses qui, je trouve, améliorent grandement la lisibilité des templates PHP :

La première syntaxe permet de facilement appliquer des conditions à des blocs quand bien même le parseur de PHP est fermé. Il suffit pour cela d’utiliser la syntaxe alternative. Cette dernière remplace l’accolade ouvrante par deux points : et la fermante par le nom de la condition précédé de end.

Disponibles pour if, while, for, foreach et switch, l’instruction de fermeture est respectivement endif;, endwhile;, endfor;, endforeach;, et endswitch;.

Espace or not ?

Nouvelle syntaxe, nouvelle question ! Espace ou non entre la structure de contrôle et les deux points ? PHP n’a pas d’avis. Je trouve que, de la même manière qu’on place un espace avant l’accolade ouvrante, il faut en mettre un avant les deux points. Cela permet de mieux distinguer leur présence.

Les short echo tags <?= $var ?> permettent à la fois d’ouvrir l’interpréteur PHP et de directement afficher le contenu des variables. C’est un raccourci à <?php echo $var; ?>. Il est de ce fait tout indiqué lorsque l’on veut afficher une variable dans du HTML.

La logique veut que le contexte prime. Si on a un peu de HTML dans de la logique PHP, alors on peut echo directement un peu de markup. Si l’on est en présence d’une majorité de HTML et que l’on a besoin de PHP pour récupérer quelques variables, alors on ouvrira et fermera les tags PHP juste lorsque l’on en a besoin.

Les hooks

On en parlera plus loin, mais avec WordPress on exécute souvent des fonctions via des hooks. Les hooks sont des événements de WordPress qui permettent d’exécuter des actions à des moments précis.

Pour déclarer une fonction qui sera appellée lorsqu’un évènement se produit, on doit normalement créer une fonction puis passer le nom de cette fonction à add_action. Ce que je n’aime pas avec cette méthode est que l’on se retrouve à déclarer de nombreuses fonctions qui ne servent qu’à un seul endroit.

Pour éviter cela, on peut tout simplement utiliser les fonctions anonymes !

// On ajoute un login via lien
function login_with_magic_link() {
    …
}
add_filter('init', 'login_with_magic_link');

// Sinon, plutôt que de déclarer une fonction puis de l'appeler immédiatement,
// on peut passer la logique directement en paramètre via une ƒn anonyme
add_filter('init', function () {
    …
});

Comme toujours, ce n’est qu’une opinion mais je trouve cela beaucoup plus clean et ça empêche de se retrouver avec des add_action séparés de la fonction appellée.

Par ailleurs, dans certains hooks, vous voudrez parfois simplement retourner true ou false pour activer/désactiver un comportement. Sachez que WordPress propose des raccourcis pour cela : __return_true et __return_false.

// Suppression de l'éditeur visuel
add_filter('user_can_richedit' , '__return_false');

Namespaces

Si le terme namespace ne vous parle pas, mais que vous êtes familiers des modules en JS, alors le fonctionement en est assez similaire. Un namespace contient tout ce qui est déclaré en son sein, sans l’ajouter à l’espace global. Avec ce méchanisme, comme dans les modules JS, on ne pollue pas l’espace global et on évite ainsi le risque de collision de noms de variables ou fonctions.

Dans WordPress, les namespaces ne sont généralement pas utilisés. De ce fait, pour éviter tout risque de fonction qui serait déclarée ailleurs (dans le core ou par un plugin par exemple), il est recommandé de préfixer ses noms de fonctions. On réalise à cet instant le second avantage procuré par les fonctions anonymes dans les add_action.

// L'exemple précédant devient
function myproject_login_with_magic_link(&$scripts) {
    …
}

La raison pour laquelle les namespaces sont peu utilisés est qu’ils ont fait leur apparition dans PHP assez tard (PHP 5.3). Dans sa tradition de ne pas casser la rétro-compatibilité, le core WordPress ne les a pas introduit et de ce fait, la plupart des développeurs WordPress ne les utilisent pas non plus.

Je suspecte que pour nombre d’entre eux, ce soit par pure ignorance ou négligence. Pourtant, rien de plus simple, voici l’exemple précédent avec namespace.

namespace Monprojet;

// L'exemple précédant devient
function login_with_magic_link(&$scripts) {
    …
}

// Appel depuis un hook par exemple, on précise le namespace
add_filter('init', 'Monprojet\login_with_magic_link');

À l’heure où j’écris ces lignes, la version la plus vieille de PHP à être supportée est la 7.3. Pour cette raison, vous ne devriez pas exécuter du code en production sur une version plus ancienne. Cela veut aussi dire que les namespaces sont supportés sans problème dans toute codebase qui se respecte.

Dans la réalité, la plupart des hébergeurs proposent encore du PHP 5.x car d’anciens projets ne tournent tout simplement pas sur PHP 7 et plus (la dernière version majeure est la 8). Mais pour un projet récent ou maintenu, tourner sur PHP 5, c’est dangereux, peu performant et c’est un peu la honte.

Tôt ou tard, les namespaces – qui sont un standard de l’industrie – finiront par être adoptés dans le core. Alors faites les choses bien, jetez un œil à la doc et menez la danse. Votre code gagnera en lisibilité.

Fichiers de templates et fonctions

C’est un élément de sécurité, il est important de prendre le réflexe d’empêcher l’exécution des fichiers qui n’ont pas à être appelés directement – c’est à dire 99% des fichiers dans lesquels on travaille dans WP puisque c’est le router qui les invoque. Un petit snippet au début de chacun de nos fichiers et c’est réglé.

defined('ABSPATH') || exit;

Cette ligne vérifie simplement si la constante ABSPATH, qui est initialisée dans le wp-config.php, existe. Si ce n’est pas le cas, comme via un accès direct en appellant l’url d’un fichier, alors l’exécution s’arrête.

Maintenant que l’on sait où l’on va pour bien formater notre code, voyons comment organiser le projet.

Commentaires

Rejoignez la discussion !

Vous pouvez utiliser Markdown pour les liens [ancre de lien](url), la mise en *italique* et en **gras**. Enfin pour le code, vous pouvez utiliser la syntaxe `inline` et la syntaxe bloc

```
ceci est un bloc
de code
```