# Les hooks WordPress expliqués : actions, filtres et cycle de vie

> Comprendre add_action, add_filter, les priorités et le cycle de vie des hooks WordPress : la référence complète pour débuter et pour s'y retrouver au quotidien.

- Auteur : Clément Hadrot
- Publié le : 2026-09-25
- Mis à jour le : 2026-09-25
- URL : https://wpmoderne.dev.wordpress-developpement.fr/hooks-wordpress/

## L’essentiel

- Une action déclenche un traitement, un filtre transforme et retourne une valeur
- Le cycle de vie (init, wp_loaded, template_redirect...) détermine quand vos hooks se déclenchent
- Toujours retourner la valeur dans un filtre, sous peine de casser le site

Si vous avez déjà ouvert le code d'une extension WordPress, vous êtes forcément tombé sur des lignes commençant par `add_action` ou `add_filter`. Ce ne sont pas de simples fonctions parmi d'autres : elles forment le mécanisme central qui permet à WordPress de rester extensible sans toucher au cœur du logiciel. C'est ce mécanisme, le système de **hooks**, que cette page vous propose de décortiquer.

Un hook (littéralement « crochet » en anglais) est un point d'ancrage placé délibérément dans le code de WordPress, à un endroit précis de son exécution. Il permet à votre code — extension, thème ou *must-use plugin* — de venir s'y « accrocher » pour exécuter une action au bon moment, ou modifier une donnée avant qu'elle ne soit affichée ou enregistrée. Sans ce système, il faudrait modifier directement les fichiers du cœur pour personnaliser le moindre comportement : un cauchemar de maintenance, puisque chaque mise à jour de WordPress effacerait vos modifications.

Grâce aux hooks, le noyau reste intact, et toute la logique métier vit dans des extensions ou des thèmes qui viennent se greffer dessus. C'est ce découplage qui explique la richesse de l'écosystème WordPress : des dizaines de milliers d'extensions coexistent, chacune s'accrochant aux mêmes points d'entrée sans jamais réécrire le cœur.

## Actions vs filtres : la distinction fondamentale

WordPress propose deux grandes familles de hooks, et confondre les deux est l'une des premières erreurs des développeurs débutants.

Une **action** permet d'exécuter du code à un moment donné, sans attente de retour. On s'en sert pour *faire quelque chose* : envoyer un e-mail, enregistrer une ligne en base de données, afficher du HTML, planifier une tâche. Une action ne retourne rien et n'a pas besoin de le faire.

Un **filtre**, à l'inverse, sert à *transformer une valeur*. WordPress vous confie une donnée (un titre, un contenu, un tableau d'options...), votre fonction la manipule, puis — c'est le point essentiel — doit impérativement la **retourner**. Si votre callback de filtre ne retourne rien, la valeur d'origine sera remplacée par `null`, ce qui peut casser silencieusement une page entière.

| Caractéristique | Action | Filtre |
| --- | --- | --- |
| Fonction pour s'accrocher | `add_action()` | `add_filter()` |
| Fonction pour déclencher | `do_action()` | `apply_filters()` |
| Valeur de retour attendue | Aucune (ignorée) | La valeur transformée, obligatoirement |
| Objectif | Exécuter un effet de bord | Modifier une donnée avant usage |
| Exemples | `init`, `wp_footer`, `save_post` | `the_content`, `the_title`, `wp_mail` |

Une astuce simple pour ne plus se tromper : si dans la documentation la fonction qui déclenche le hook s'appelle `do_action( 'quelque_chose' )`, c'est une action. Si elle s'appelle `apply_filters( 'quelque_chose', $valeur )` et que le résultat est réinjecté dans le code (`$titre = apply_filters( 'the_title', $titre );`), c'est un filtre.

## Anatomie d'un hook : add_action, add_filter et leurs callbacks

La signature complète des deux fonctions d'accrochage est la suivante :

```
add_action( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );
add_filter( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );
```

- **$hook_name** : le nom du hook auquel on s'accroche (une chaîne de caractères).
- **$callback** : la fonction à exécuter.
- **$priority** : un entier qui détermine l'ordre d'exécution parmi les callbacks accrochés au même hook. La valeur par défaut est `10`.
- **$accepted_args** : le nombre d'arguments que WordPress doit transmettre à votre callback. Par défaut, un seul argument est transmis, même si le hook en propose davantage.

Du côté du déclenchement, le cœur (ou votre propre code) appelle :

```
do_action( string $hook_name, mixed ...$arg );
apply_filters( string $hook_name, mixed $value, mixed ...$arg );
```

Un callback peut prendre plusieurs formes. La plus courante est la fonction nommée :

```
function wpm_notifier_nouvel_article( $post_ID, $post, $update ) {
    if ( $update ) {
        return;
    }
    wp_mail( 'redaction@example.com', 'Nouvel article', 'ID : ' . $post_ID );
}
add_action( 'wp_insert_post', 'wpm_notifier_nouvel_article', 10, 3 );
```

Remarquez le `3` en dernier argument : sans lui, WordPress n'aurait transmis que `$post_ID`, et `$post`/`$update` seraient restés indéfinis.

On peut aussi utiliser une closure (fonction anonyme), pratique pour un traitement court sans polluer l'espace de noms global :

```
add_filter( 'excerpt_length', function ( $length ) {
    return 20;
} );
```

Ou encore une méthode de classe, sous forme de tableau `[ $objet, 'nom_methode' ]` :

```
class WPM_Gestion_Commandes {
    public function ajouter_meta_box() {
        add_meta_box( 'wpm_commande', 'Détails commande', [ $this, 'afficher_meta_box' ], 'commande' );
    }
}
$gestion = new WPM_Gestion_Commandes();
add_action( 'add_meta_boxes', [ $gestion, 'ajouter_meta_box' ] );
```

Ou enfin une méthode statique, référencée par `[ 'NomDeClasse', 'methode_statique' ]` ou par la chaîne `'NomDeClasse::methode_statique'` :

```
class WPM_Cache {
    public static function purger() {
        // logique de purge
    }
}
add_action( 'save_post', [ 'WPM_Cache', 'purger' ] );
```

## Priorités, ordre d'exécution et suppression de hooks

Lorsque plusieurs callbacks sont accrochés au même hook, ils s'exécutent dans l'ordre croissant de leur priorité : un callback en priorité `5` s'exécute avant un callback en priorité `10`, qui s'exécute lui-même avant un callback en priorité `20`. À priorité égale, l'ordre suit celui de l'enregistrement. C'est ce paramètre qui permet, par exemple, de s'assurer qu'un traitement se déroule après celui d'une extension tierce, ou au contraire avant.

Pour retirer un callback, on utilise `remove_action()` ou `remove_filter()`, avec la même signature que l'ajout :

```
remove_action( 'wp_head', 'wp_generator' );
remove_filter( 'the_content', 'wpautop', 10 );
```

Le piège classique : pour que le retrait fonctionne, il faut fournir **exactement** le même nom de hook, le même callback et la **même priorité** que ceux utilisés lors de l'ajout. Si l'extension d'origine a accroché sa fonction en priorité `20` et que vous tentez un `remove_action` sans préciser cette priorité (donc avec la valeur par défaut `10`), le retrait échoue silencieusement, sans erreur ni avertissement.

Autre piège : une closure anonyme ne peut pas être retirée, car il est impossible de la référencer une seconde fois de façon identique. Si une extension tierce accroche une fonction anonyme, vous ne pourrez pas la désaccrocher directement ; il faudra agir en amont ou en aval, sur un autre hook.

## Le cycle de vie d'une requête WordPress

Comprendre à quel moment chaque hook se déclenche est sans doute la compétence la plus utile à acquérir. Voici les principaux hooks déclenchés lors d'une requête front-end classique, dans leur ordre réel d'exécution.

1. **muplugins_loaded** : juste après le chargement des must-use plugins, avant les extensions classiques. Rarement utilisé.
2. **plugins_loaded** : toutes les extensions actives sont chargées. On y initialise des fonctionnalités dépendant d'autres extensions, ou on charge les traductions avec `load_plugin_textdomain`.
3. **setup_theme** : avant le chargement du thème, pour préparer des réglages qui doivent exister avant que le thème ne s'exécute.
4. **after_setup_theme** : le `functions.php` du thème vient d'être chargé. Endroit privilégié pour `add_theme_support` et les menus de navigation.
5. **init** : WordPress est presque entièrement initialisé. Hook le plus utilisé pour `register_post_type`, les taxonomies, ou l'enregistrement de scripts.
6. **wp_loaded** : WordPress est complètement chargé, mais la requête elle-même n'a pas encore été analysée.
7. **parse_request** : la requête entrante vient d'être analysée, avant toute résolution en objets de contenu.
8. **send_headers** : les en-têtes HTTP sont sur le point d'être envoyés ; utile pour des en-têtes personnalisés (cache, sécurité).
9. **pre_get_posts** : juste avant l'exécution de la requête principale (et de toute `WP_Query`). Le hook de référence pour modifier les critères d'une requête, par exemple exclure une catégorie de la page d'accueil.
10. **wp** : la requête principale est résolue, l'objet `$wp` est disponible ; pratique pour agir selon les conditions de page (`is_singular()`, `is_archive()`...).
11. **template_redirect** : juste avant le chargement du template, dernier moment pour rediriger ou intercepter l'affichage.
12. **template_include** (filtre) : change le fichier de template qui sera chargé pour afficher la page.
13. **wp_enqueue_scripts** : hook dédié à l'enregistrement des scripts et styles côté front (`wp_enqueue_script`, `wp_enqueue_style`).
14. **wp_head** : dans la balise `<head>` du thème ; pour injecter des balises meta, du CSS inline ou des scripts de suivi.
15. **the_content** (filtre) : transforme le contenu d'un article avant affichage ; très utilisé, mais à manier avec prudence pour des raisons de performance (voir plus bas).
16. **wp_footer** : juste avant la fermeture du `</body>` ; emplacement classique pour les scripts de fin de page.
17. **shutdown** : le tout dernier hook déclenché avant la fin du script PHP, une fois la réponse envoyée.

Côté administration, un autre ensemble de hooks structure le chargement des écrans du tableau de bord : **admin_init** se déclenche à chaque chargement d'une page d'administration (utile avec l'API Settings), **admin_menu** permet d'ajouter des pages et sous-pages de menu avec `add_menu_page` et `add_submenu_page`, et **admin_enqueue_scripts** charge des scripts et styles spécifiques à l'administration, en filtrant sur l'écran courant (`$hook_suffix`) pour éviter de tout charger partout.

## Créer ses propres hooks dans une extension ou un thème

Rien n'empêche votre extension ou votre thème de définir ses propres hooks pour devenir, à son tour, extensible par d'autres. C'est une bonne pratique dès qu'un morceau de code gagnerait à être personnalisable sans modification directe.

Quelques règles à respecter systématiquement :

- **Préfixer les noms de hooks** avec un identifiant propre à votre extension (par exemple `wpm_`), afin d'éviter toute collision avec un hook du cœur ou d'une autre extension.
- **Documenter chaque hook** avec un bloc de commentaire DocBlock, en précisant son rôle, ses paramètres et depuis quelle version il existe — c'est la convention suivie par le cœur de WordPress lui-même.
- **Prévoir des hooks « avant/après »** autour des opérations sensibles, pour permettre à d'autres codes de réagir sans devoir modifier votre logique interne :

```
/**
 * Se déclenche avant l'enregistrement d'une commande.
 *
 * @param array $donnees Données brutes de la commande.
 */
do_action( 'wpm_avant_enregistrement_commande', $donnees );

$commande_id = wpm_enregistrer_commande( $donnees );

/**
 * Se déclenche après l'enregistrement d'une commande.
 *
 * @param int   $commande_id Identifiant de la commande créée.
 * @param array $donnees     Données brutes de la commande.
 */
do_action( 'wpm_apres_enregistrement_commande', $commande_id, $donnees );
```

Et pour les valeurs qui méritent d'être personnalisables, un filtre :

```
$taux_tva = apply_filters( 'wpm_taux_tva', 0.20, $commande_id );
```

## Hooks dynamiques

Certains hooks intègrent une variable dans leur nom, pour cibler précisément un contexte sans avoir à filtrer manuellement dans le callback. Le cœur en propose plusieurs :

- `save_post_{$post_type}` : se déclenche à l'enregistrement d'un contenu, uniquement pour le type de contenu concerné (par exemple `save_post_product`).
- `{$taxonomy}_add_form_fields` : ajoute des champs personnalisés au formulaire de création d'un terme, pour une taxonomie donnée.

Pour s'y accrocher, il suffit de construire dynamiquement la chaîne :

```
add_action( 'save_post_evenement', 'wpm_valider_evenement', 10, 3 );
```

Pour inspecter ce qui se passe au moment de l'exécution, quatre fonctions utilitaires sont précieuses :

- `current_filter()` : retourne le nom du hook actuellement en cours d'exécution.
- `doing_action( $hook_name = null )` : indique si une action donnée (ou n'importe quelle action si l'argument est omis) est en train de s'exécuter.
- `did_action( $hook_name )` : retourne le nombre de fois qu'une action donnée a déjà été déclenchée depuis le début de la requête.
- `has_filter( $hook_name, $callback = false )` : vérifie si un filtre (ou un callback précis sur ce filtre) est enregistré.

## Hooks côté JavaScript

Le système de hooks ne se limite pas à PHP. Depuis l'arrivée de l'éditeur de blocs, WordPress fournit le paquet `@wordpress/hooks`, qui reproduit la même logique côté client avec les fonctions `addFilter` et `addAction` :

```
import { addFilter } from '@wordpress/hooks';

addFilter(
    'blocks.registerBlockType',
    'wpm/personnaliser-paragraphe',
    ( settings, name ) => {
        if ( name !== 'core/paragraph' ) {
            return settings;
        }
        settings.supports = { ...settings.supports, align: false };
        return settings;
    }
);
```

Le filtre `blocks.registerBlockType` illustre bien le principe : il permet de modifier les réglages d'un bloc au moment de son enregistrement, exactement comme un filtre PHP modifie une valeur avant son utilisation. On retrouve la même logique de nommage, de priorité (troisième argument optionnel) et de valeur de retour obligatoire.

## Déboguer les hooks

Quand un comportement inattendu apparaît et que vous soupçonnez un hook, plusieurs outils permettent d'y voir clair.

L'extension **Query Monitor** est la référence : son panneau « Hooks & Actions » liste, pour la page en cours, tous les hooks déclenchés ainsi que les callbacks accrochés à chacun, avec leur priorité et l'extension ou le thème d'origine. C'est souvent le moyen le plus rapide d'identifier qui modifie quoi.

Sans outil disponible sur l'environnement, une **extension-témoin** minimale, écrite pour l'occasion, peut suffire : elle s'accroche au hook suspecté avec une priorité extrême (`1` ou `PHP_INT_MAX`) et journalise le contexte avec `error_log()` pour confirmer si et quand il se déclenche réellement.

Enfin, WordPress expose un hook particulier, `all`, qui se déclenche à chaque exécution de *n'importe quel* hook, action ou filtre. Il peut servir à tracer l'ensemble des hooks déclenchés sur une requête, mais avec beaucoup de prudence : s'exécutant à chaque hook du cycle de vie, un callback un peu lourd accroché à `all` peut ralentir le site de façon spectaculaire. Il est réservé au débogage ponctuel, jamais à un usage en production.

## Bonnes pratiques et pièges à éviter

- Dans un filtre, retournez **toujours** la valeur, même si votre condition ne s'applique pas : un `return` oublié transforme silencieusement la donnée en `null`.
- Évitez les requêtes SQL ou appels HTTP lourds dans `the_content` : ce filtre peut s'appliquer plusieurs fois par page (widgets, flux RSS, recherche).
- Vérifiez le contexte avec `is_admin()` avant d'enregistrer des scripts pour le front, et avec `wp_doing_ajax()` pour adapter un comportement en requête AJAX.
- Ne présumez jamais de l'ordre d'exécution entre extensions : fixez explicitement une priorité si l'ordre compte pour votre logique.
- Préfixez systématiquement vos hooks personnalisés pour éviter toute collision de noms.
- Passez le bon `$accepted_args` dès que votre callback a besoin de plus d'un paramètre : c'est l'erreur la plus fréquente derrière un argument « manquant ».
- Une closure ne peut pas être retirée avec `remove_action`/`remove_filter` : préférez une fonction nommée ou une méthode de classe si vous voulez garder la possibilité de la désaccrocher.
- N'accrochez jamais de traitement coûteux sur des hooks très fréquents (`pre_get_posts`, `the_content`) sans mettre en cache le résultat.
- Pour retirer un hook tiers, utilisez la même priorité que celle de l'extension d'origine ; en cas de doute, inspectez le code source ou utilisez Query Monitor.
- Documentez vos propres hooks comme le fait le cœur : un futur développeur (vous y compris) vous remerciera.

## Conclusion

Le système de hooks est la colonne vertébrale de WordPress : il permet d'étendre, de modifier et de personnaliser le comportement du logiciel sans jamais toucher à son cœur. Actions pour agir, filtres pour transformer et retourner une valeur, priorités pour ordonner l'exécution, et un cycle de vie de requête bien identifié pour savoir où intervenir : ces notions suffisent à comprendre la quasi-totalité des extensions et thèmes que vous rencontrerez. La meilleure façon de les maîtriser reste la pratique : accrochez-vous à un hook, inspectez le résultat avec Query Monitor, et itérez.

> Pour aller plus loin, la documentation officielle reste la référence la plus fiable : le guide [Hooks — Plugin Developer Handbook](https://developer.wordpress.org/plugins/hooks/) pour les fondamentaux, et la [référence complète des hooks](https://developer.wordpress.org/reference/hooks/) pour explorer, hook par hook, tous ceux que propose le cœur de WordPress.
