« J’ai ajouté mon add_action mais rien ne se passe. » C’est probablement la phrase la plus tapée dans les forums de support WordPress depuis quinze ans. Neuf fois sur dix, le problème n’est pas dans la fonction accrochée mais dans la compréhension de l’ordre d’exécution : à quel moment le hook se déclenche, avec quelle priorité, et avec quels arguments.
Cet article revient sur les mécanismes du système de hooks de WordPress, actions et filtres, avec une attention particulière portée aux subtilités qui piègent même des développeurs expérimentés : la gestion de la priorité, le nombre d’arguments, et les hooks qui ne se déclenchent qu’une seule fois par requête.
Actions et filtres : une distinction qui a un sens
Une action, déclenchée par do_action(), exécute du code sans attendre de retour. Elle sert à « faire quelque chose » : enregistrer un type de contenu, envoyer un e-mail, écrire un log. Un filtre, déclenché par apply_filters(), transforme une valeur et attend systématiquement un retour :
// Action : on ne retourne rien
add_action( 'init', function () {
register_post_type( 'produit', [ /* ... */ ] );
} );
// Filtre : on DOIT retourner la valeur, même inchangée
add_filter( 'the_title', function ( $title ) {
if ( is_singular( 'produit' ) ) {
$title .= ' — en stock';
}
return $title;
} );
Oublier le return dans un filtre est une erreur fréquente chez les développeurs qui débutent : la fonction retourne alors null par défaut, et la valeur filtrée disparaît purement et simplement pour tous les filtres exécutés après le vôtre.
La priorité : un ordre d’exécution, pas une importance
Le troisième argument de add_action() et add_filter() est la priorité, avec une valeur par défaut de 10. Plus le nombre est bas, plus le callback s’exécute tôt :
add_action( 'init', 'ma_fonction_precoce', 5 );
add_action( 'init', 'ma_fonction_normale' ); // priorité 10 implicite
add_action( 'init', 'ma_fonction_tardive', 20 );
Un cas d’usage classique : retirer un hook ajouté par le cœur de WordPress ou par une autre extension. Si cette dernière a accroché sa fonction avec la priorité 10, votre remove_action doit préciser exactement la même priorité, sinon WordPress ne trouvera rien à retirer :
// Ne fonctionne que si l'extension tierce a utilisé la priorité 10
remove_action( 'wp_head', 'wp_generator' );
// Si l'extension a utilisé une priorité différente, il faut la connaître
remove_action( 'wp_head', [ $instance_extension_tierce, 'afficher_meta' ], 15 );
Ce deuxième cas révèle une vraie difficulté : retirer un hook attaché en tant que méthode d’objet nécessite d’avoir accès à la même instance d’objet que celle qui a fait l’ajout. Si cette instance a été créée dans une closure sans être exposée, il est tout simplement impossible de retirer le hook depuis l’extérieur. C’est un argument fort pour, en tant qu’auteur d’extension, toujours documenter et si possible exposer les instances de classes qui s’accrochent aux hooks.

Le nombre d’arguments, souvent oublié
Le quatrième paramètre d’add_action précise combien d’arguments WordPress doit transmettre au callback. Par défaut, il vaut 1, ce qui provoque une erreur silencieuse fréquente :
// save_post transmet normalement 3 arguments : $post_id, $post, $update
add_action( 'save_post', function ( $post_id, $post, $update ) {
// $post et $update seront TOUJOURS null ici
if ( $update ) {
// ce code ne s'exécute jamais
}
} );
// Correction : préciser le nombre d'arguments attendus
add_action( 'save_post', function ( $post_id, $post, $update ) {
if ( $update ) {
// fonctionne correctement
}
}, 10, 3 );
PHP n’émet aucune erreur dans ce cas : les paramètres surnuméraires de la closure reçoivent simplement leur valeur par défaut ou null. C’est un bug silencieux qui peut rester en production pendant des mois avant d’être détecté, en général le jour où la logique conditionnée par le troisième argument doit enfin se déclencher.
Un hook peut se déclencher plusieurs fois par requête
Certains hooks, comme the_content ou wp_footer, ne s’exécutent qu’une fois. D’autres, comme save_post ou pre_get_posts, peuvent se déclencher plusieurs fois dans une même requête HTTP : un enregistrement en cascade dans l’administration, une requête secondaire lancée par un widget. Sans garde-fou, une fonction accrochée à save_post qui déclenche elle-même une sauvegarde (par exemple via wp_update_post) peut provoquer une boucle infinie.
add_action( 'save_post', function ( $post_id ) {
// Retirer temporairement le hook évite la boucle infinie
remove_action( 'save_post', __FUNCTION__ );
update_post_meta( $post_id, '_derniere_maj', current_time( 'mysql' ) );
add_action( 'save_post', __FUNCTION__ );
} );
Ce schéma « désaccrocher, agir, raccrocher » est un classique du développement d’extensions WordPress et mérite d’être connu par cœur, car l’oubli provoque des ralentissements difficiles à diagnostiquer plutôt qu’un plantage franc.
Vérifier qu’un hook est bien enregistré
Deux fonctions natives aident au diagnostic sans installer d’extension de debug supplémentaire :
has_action( 'save_post', 'ma_fonction' )retourne la priorité si le hook est enregistré,falsesinon.did_action( 'init' )retourne le nombre de fois qu’une action a déjà été déclenchée, utile pour éviter une double exécution.
Avant de suspecter WordPress ou une extension tierce, on commence toujours par vérifier la priorité et le nombre d’arguments déclarés : dans notre expérience, ces deux points expliquent l’immense majorité des « hooks qui ne se déclenchent pas ».
Tableau récapitulatif
| Paramètre | Valeur par défaut | Piège fréquent |
|---|---|---|
| Priorité | 10 | Oubli lors d’un remove_action ciblant une autre priorité |
| Nombre d’arguments | 1 | Arguments supplémentaires silencieusement à null |
| Retour d’un filtre | aucun par défaut | Absence de return qui efface la valeur |
En résumé
Le système de hooks est d’une simplicité trompeuse. Sa puissance vient justement de règles précises — priorité, nombre d’arguments, obligation de retour pour les filtres — qu’il faut connaître pour éviter des heures de débogage. Avant de conclure qu’un hook « ne marche pas », vérifiez systématiquement ces trois points : ils résolvent l’écrasante majorité des cas.