Récemment, j’ai aidé un ami à essayer de comprendre du code et à voir comment le modifier pour qu’il fonctionne bien avec des appels asynchrones vers le backend. Un bonus serait de pouvoir l’écrire de façon plus modulaire et réutilisable.

Le code est un plugin WordPress écrit avec une approche très legacy : un mélange de logique métier et de logique frontend au même endroit. Des manipulations de données faites en plein milieu du code de template, et ainsi de suite.

Bref, ce qu’on voyait sur les sites PHP au début des années 2000, avant toutes les super additions OOP au langage et avant l’arrivée d’un tas de frameworks matures.

Le cas sur lequel j’ai planché aujourd’hui était le suivant : vous avez un tableau qui affiche plein d’entrées. Il fallait ajouter plusieurs filtres au tableau et le faire fonctionner via AJAX pour rafraîchir rapidement l’UI, sans recharger toute la page d’administration de WordPress.

Filtrer les données était facile : un petit array_filter en ligne pour filtrer les données avant de les passer au code du template (pour garder le même style de code, c’était aussi dans le fichier de template 😇).

Le problème est arrivé quand on a commencé à vouloir rendre le tableau affichable via AJAX.

Il se trouve au milieu d’un tas d’autre code de template et de code métier. Si on voulait limiter la sortie AJAX au seul tableau, on n’avait que quelques options.

La première serait d’entourer le HTML avant et après le tableau de GROS blocs if pour s’assurer qu’il ne soit pas rendu.

Une autre serait d’extraire le template dans un fichier séparé et de l’inclure dans l’action AJAX que l’on définirait dans le code du plugin.

Les fonctions anonymes à la rescousse !

En réfléchissant à la façon la plus simple de gérer ça, j’ai essayé de définir une fonction PHP normale contenant le code du tableau, mais il y avait un problème.

Le code s’exécute dans une méthode de classe. Il y a des références à $this dans le template du tableau. Si on ne veut pas trop changer le code, on voudrait passer $this en argument de la fonction. Eh bien ça ne marche pas trop, car on ne peut pas passer $this en paramètre 🤷‍♂️.

J’étais un peu déçu à l’idée de devoir réécrire un tas de choses dans le template, et il y avait pas mal de références qu’on voulait garder en tête pour que tout continue de fonctionner.

Puis je me suis rappelé que le site ne tourne pas sous PHP 5.2 mais sous PHP 7, et que les fonctions anonymes seraient donc supportées.

Elles ont cette fonctionnalité bien pratique qui permet de définir quelles variables du scope appelant seront passées à la fonction quand elle est appelée. Par exemple :

$table_function = function() use ( $pages, $current_page, $items ) {
    // ... Table render code
}

Cela vous permet de référencer directement $pages ou $items dans le corps de la fonction.

Ce qui est génial, c’est qu’on peut aussi passer $this comme variable dans le scope de la fonction !

On a donc pu extraire complètement le code de rendu du tableau dans une fonction séparée, qu’on peut appeler de façon isolée pendant l’appel AJAX !

Bien sûr, il faut faire très attention aux variables utilisées dans le corps de la fonction, car elles doivent être définies ; soyez donc prudent quant à l’endroit où la fonction AJAX peut être appelée.

Pour nous, cela signifie qu’on va ajouter une toute petite action dans la classe principale qui fera toujours le require du fichier, mais on aura plus de contrôle pour désactiver toutes les autres sorties qui se produiraient.