Une extension de facturation propose un champ pour saisir un numéro de TVA intracommunautaire. Un client signale que son numéro disparaît systématiquement après l’enregistrement du formulaire de réglages : il tape sa valeur, clique sur « Enregistrer les modifications », la page se recharge, et le champ est de nouveau vide. Aucune erreur PHP dans les logs, aucun message d’échec affiché à l’écran. WordPress se comporte comme si l’enregistrement s’était bien passé.
C’est exactement ce genre de silence qui rend ce bug difficile à repérer : tout le mécanisme visible fonctionne, la page se recharge normalement, le message « Réglages enregistrés » s’affiche même. Le problème est ailleurs, dans un maillon qu’on oublie souvent d’inspecter en premier : le callback de nettoyage.
Le symptôme précis
Le champ se vide uniquement pour certains formats de saisie, jamais pour d’autres. Après quelques tests, on remarque que le numéro de TVA français, qui commence par les lettres FR, disparaît, alors qu’un numéro purement numérique est conservé. Ce détail est le fil à tirer.
Le diagnostic

La cause se trouve dans la déclaration du réglage via register_setting(), où l’argument sanitize_callback définit la fonction chargée de nettoyer la valeur avant son enregistrement en base. Voici le code fautif :
register_setting(
'facturation_options',
'numero_tva',
array(
'sanitize_callback' => function( $valeur ) {
if ( ! preg_match( '/^[0-9]{11}$/', $valeur ) ) {
return ''; // ce qui vide silencieusement le champ
}
return $valeur;
},
)
);
Le développeur voulait valider un numéro de TVA purement numérique à onze chiffres, mais a oublié que les numéros français commencent par deux lettres suivies de neuf ou dix chiffres. L’expression régulière rejette donc systématiquement les valeurs conformes au format réel, et le callback retourne une chaîne vide au lieu de la valeur saisie. WordPress enregistre fidèlement ce que le callback lui a rendu, c’est-à-dire rien.
C’est la confusion classique entre sanitisation et validation. Un sanitize_callback est censé nettoyer une valeur, retirer des caractères indésirables, encoder ce qui doit l’être, jamais rejeter silencieusement une saisie légitime. La validation, elle, doit produire un message d’erreur explicite via add_settings_error(), pas un simple retour vide.
Le correctif
La bonne pratique sépare clairement les deux responsabilités. Le nettoyage retire les espaces et met en majuscules, la validation vérifie le format et, en cas d’échec, restaure l’ancienne valeur tout en signalant l’erreur à l’utilisateur :
register_setting(
'facturation_options',
'numero_tva',
array(
'sanitize_callback' => 'nettoyer_numero_tva',
)
);
function nettoyer_numero_tva( $valeur ) {
$valeur = strtoupper( trim( $valeur ) );
if ( ! preg_match( '/^[A-Z]{2}[0-9A-Z]{2,12}$/', $valeur ) ) {
add_settings_error(
'numero_tva',
'format_invalide',
__( 'Le numéro de TVA saisi ne correspond à aucun format européen reconnu.', 'facturation' )
);
return get_option( 'numero_tva' ); // on conserve l'ancienne valeur
}
return $valeur;
}
Ce correctif change tout du point de vue de l’utilisateur : en cas d’erreur, un message rouge explicite apparaît en haut de la page de réglages, et l’ancienne valeur reste affichée, plutôt que de disparaître sans explication.
Points de vigilance supplémentaires
- Vérifier que
add_settings_field()lit bien la valeur viaget_option()au moment du rendu, et non une variable mise en cache plus tôt dans la requête. - Ne jamais faire confiance à une regex écrite de tête pour un format international : les numéros de TVA varient en longueur et en structure d’un pays européen à l’autre.
- Tester systématiquement avec une valeur volontairement invalide avant la mise en production, pas seulement avec le cas nominal qui fonctionne toujours.
Un piège voisin : deux callbacks qui se contredisent
Une variante de ce bug apparaît quand un développeur ajoute un second callback sur le filtre sanitize_option_{$option} en plus de celui déclaré dans register_setting(). Les deux fonctions s’exécutent alors en chaîne, et si la seconde ne connaît pas les règles appliquées par la première, elle peut retraiter une valeur déjà nettoyée et la corrompre à son tour. La règle à retenir est simple : un seul point d’entrée de nettoyage par option, documenté clairement dans le code, évite ce genre de collision.
Un champ de réglage qui se vide sans message d’erreur n’est jamais un hasard, c’est toujours un callback qui a menti sur ce qu’il faisait.
En résumé
Face à un réglage qui se réinitialise silencieusement, le réflexe à avoir est de remonter directement au sanitize_callback déclaré dans register_setting(), avant même de suspecter un problème de formulaire ou de nonce. Séparer nettoyage et validation, et toujours utiliser add_settings_error() pour communiquer un rejet, transforme un bug frustrant en une expérience de réglage compréhensible pour l’utilisateur final.