Le WordPress d'aujourd'hui, décodé pour les développeurs

Performance

Antipattern : appeler get_option pour chaque ligne d’un tableau de résultats

get_option répété dans une boucle d'affichage coûte moins cher qu'on l'imagine grâce à l'autoload, mais reste une habitude à corriger. Explication et alternative.

Par WordPress Développement • 13 février 2023 • 5 min de lecture • Aucun commentaire
Antipattern : appeler get_option pour chaque ligne d'un tableau de résultats

foreach ( $resultats as $ligne ) { $devise = get_option( 'devise_defaut' ); } — ce genre de motif traîne dans beaucoup de templates WordPress, et il déclenche presque toujours la même réaction chez qui le découvre en relecture de code : « ça doit taper la base à chaque tour de boucle ».

Cette réaction est compréhensible mais techniquement fausse dans l’immense majorité des cas. Ce qui se passe réellement mérite d’être détaillé, parce que la correction à apporter n’est pas celle qu’on croit, et que la confusion mène parfois à des optimisations inutiles ailleurs.

Ce qu’on observe en relecture de code

Sur un tableau de cent lignes affichant chacune une conversion de devise, un appel à get_option( 'devise_defaut' ) placé à l’intérieur de la boucle donne l’impression d’une centaine d’allers-retours vers la table wp_options. C’est le genre de code que les outils de revue signalent volontiers comme suspect, et à raison sur le principe de la propreté du code, même si l’impact réel est faible.

Pourquoi c’est moins grave qu’il n’y paraît

WordPress charge au démarrage de chaque requête l’ensemble des options marquées comme « autoload », dans une seule requête SQL exécutée par wp_load_alloptions(). Cette fonction alimente un cache mémoire interne pour toute la durée du chargement de la page. Un appel ultérieur à get_option() sur une option autoloadée ne touche donc jamais la base de données : il lit une valeur déjà présente en mémoire PHP.

Concrètement, que get_option( 'devise_defaut' ) soit appelé une fois ou cent fois dans la même requête, le coût en base de données reste rigoureusement identique : zéro requête supplémentaire, à condition que l’option soit bien autoloadée, ce qui est le comportement par défaut pour la grande majorité des options créées via add_option() ou l’API de réglages.

Où se cache alors le vrai coût

Le coût réel de cent appels répétés n’est pas nul pour autant, mais il se situe ailleurs : dans l’exécution de la fonction elle-même. Chaque appel à get_option() traverse plusieurs filtres (pre_option_{$option}, option_{$option}), vérifie l’existence en cache, gère la désérialisation éventuelle de la valeur. Sur cent tours de boucle, cela représente une centaine d’exécutions de cette chaîne de filtres, pour un résultat strictement identique à chaque fois.

Sur un serveur PHP moderne, ce surcoût reste de l’ordre de la microseconde par appel dans la plupart des cas. Il devient mesurable uniquement sur des boucles de plusieurs milliers d’itérations, ou quand un filtre personnalisé accroché à option_{$option} effectue lui-même un traitement lourd, une fonction de formatage coûteuse par exemple.

L'essentiel à retenir : get_option lit un cache mémoire, pas la base à chaque appel ; Le vrai coût vient du calcul répété, pas de la lecture ; Une variable locale suffit à éliminer l'appel superflu

La bonne pratique malgré tout

Même à faible coût, répéter un appel dont le résultat ne change jamais pendant l’exécution d’une boucle reste un défaut de lisibilité et une source potentielle de bug si l’option venait à changer de valeur en cours de traitement (ce qui n’arrive jamais dans ce contexte, mais que le code ne garantit pas explicitement).

<?php
$devise = get_option( 'devise_defaut' );
foreach ( $resultats as $ligne ) {
    echo esc_html( $ligne['montant'] . ' ' . $devise );
}

Cette réécriture ne change rien aux performances mesurables du site, mais clarifie l’intention du code : la devise est une constante pour la durée de la boucle, et non une valeur susceptible de varier ligne par ligne.

Le cas où l’option n’est pas autoloadée

Le raisonnement change du tout au tout si l’option a été enregistrée avec add_option( $nom, $valeur, '', 'no' ), désactivant explicitement l’autoload. Dans ce cas précis, chaque appel à get_option() déclenche bien une requête SQL individuelle, faute de présence dans le cache des options autoloadées.

  • Option autoloadée (comportement par défaut) : zéro coût SQL supplémentaire en boucle
  • Option explicitement non autoloadée : une requête SQL par appel non mis en cache localement
  • Option volumineuse changée régulièrement : à retirer de l’autoload pour ne pas alourdir le chargement de toutes les pages, voir l’article dédié à ce piège précis

Avant de corriger un appel « en boucle », il vaut toujours mieux vérifier si l’option concernée est autoloadée. Le correctif à apporter n’est pas le même selon la réponse, et le second cas mérite bien plus d’attention que le premier.

Ce qu’il faut retenir

Un appel à get_option() répété dans une boucle d’affichage n’est pas l’antipattern qu’on imagine spontanément, tant que l’option reste autoloadée : le coût en base est nul, seul un léger surcoût d’exécution PHP subsiste. La bonne pratique reste néanmoins d’extraire la valeur dans une variable locale avant la boucle, pour la lisibilité du code plutôt que pour la performance. Le vrai chantier de performance se trouve du côté des options non autoloadées ou trop volumineuses, un sujet bien distinct qui mérite un diagnostic séparé.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi