WordPress

Tutoriel : Les hooks WordPress (actions et filtres) expliqués

15 min de lecture

Les hooks : le système nerveux de WordPress

Les hooks sont le mécanisme fondamental qui rend WordPress extensible. Chaque fois que WordPress fait quelque chose — charger une page, enregistrer un article, envoyer un email, afficher un menu — il déclenche des hooks auxquels vous pouvez vous accrocher pour ajouter, modifier ou supprimer des comportements. Comprendre les hooks, c’est comprendre WordPress.

Actions vs Filtres : la différence clé

Actions : « Fais quelque chose à ce moment »

Une action est un point dans le code WordPress où vous pouvez insérer votre propre code. L’action ne retourne rien — elle exécute une tâche.

// "Quand WordPress charge le footer, ajoute mon script analytics"
function itsc_add_analytics() {
    ?>
    <!-- Mon code analytics ici -->
    <?php
}
add_action('wp_footer', 'itsc_add_analytics');

// "Quand un article est publié, envoie une notification"
function itsc_notify_on_publish($post_id, $post) {
    if ($post->post_type === 'post') {
        wp_mail('admin@monsite.com', 'Nouvel article', 'Article publié : ' . $post->post_title);
    }
}
add_action('publish_post', 'itsc_notify_on_publish', 10, 2);

Filtres : « Modifie cette valeur avant qu’elle soit utilisée »

Un filtre reçoit une valeur, la modifie (ou non), et la retourne. Il doit TOUJOURS retourner quelque chose.

// "Modifie le titre de l'article avant affichage"
function itsc_modify_title($title) {
    if (is_single()) {
        return $title . ' | ITSkillsCenter';
    }
    return $title; // TOUJOURS retourner la valeur
}
add_filter('the_title', 'itsc_modify_title');

// "Change la longueur de l'extrait"
function itsc_excerpt_length($length) {
    return 25; // 25 mots au lieu de 55
}
add_filter('excerpt_length', 'itsc_excerpt_length');

Résumé de la différence

Aspect Action Filtre
Fonction add_action() add_filter()
But Exécuter du code Modifier une valeur
Retour Rien (void) Obligatoire (la valeur modifiée)
Exemple Envoyer un email quand un post est créé Modifier le contenu d’un post

Syntaxe complète

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

Priorité

La priorité détermine l’ordre d’exécution quand plusieurs fonctions sont accrochées au même hook :

  • 1 : Exécuté en premier
  • 10 : Priorité par défaut
  • 99 : Exécuté en dernier
// Cette fonction s'exécute APRÈS les autres
add_filter('the_content', 'itsc_add_cta_after_content', 99);

// Cette fonction s'exécute AVANT les autres
add_filter('the_content', 'itsc_modify_content_early', 1);

Nombre d’arguments

Par défaut, votre callback reçoit 1 argument. Si le hook en passe plus, indiquez-le :

// save_post passe 3 arguments : post_id, post object, update boolean
function itsc_on_save($post_id, $post, $update) {
    // $update est true si c'est une mise à jour, false si création
    if (!$update) {
        // Nouveau post créé
    }
}
add_action('save_post', 'itsc_on_save', 10, 3); // 3 arguments

Les hooks les plus utilisés

Actions essentielles

// Chargement des styles et scripts
add_action('wp_enqueue_scripts', function() {
    wp_enqueue_style('mon-style', get_stylesheet_uri());
    wp_enqueue_script('mon-script', get_template_directory_uri() . '/js/main.js', array(), '1.0', true);
});

// Configuration du thème (une seule fois)
add_action('after_setup_theme', function() {
    add_theme_support('post-thumbnails');
    add_theme_support('title-tag');
    register_nav_menus(array('primary' => 'Menu Principal'));
});

// Enregistrer les widgets
add_action('widgets_init', function() {
    register_sidebar(array('name' => 'Sidebar', 'id' => 'sidebar-1'));
});

// Enregistrer les Custom Post Types
add_action('init', function() {
    register_post_type('portfolio', array(/* ... */));
});

// Code dans le <head>
add_action('wp_head', function() {
    echo '<meta name="author" content="ITSkillsCenter">';
});

// Code avant </body>
add_action('wp_footer', function() {
    // Scripts analytics, widgets, etc.
});

// Quand un article est publié
add_action('publish_post', function($post_id) {
    // Notification, mise à jour de cache, etc.
});

// Quand un utilisateur s'inscrit
add_action('user_register', function($user_id) {
    // Email de bienvenue, ajout à une liste, etc.
});

// Scripts dans l'admin seulement
add_action('admin_enqueue_scripts', function($hook) {
    // $hook contient la page admin actuelle
    if ($hook === 'post.php') {
        wp_enqueue_script('mon-admin-script', '...');
    }
});

// Ajouter des colonnes dans la liste des articles
add_action('manage_posts_columns', function($columns) {
    $columns['views'] = 'Vues';
    return $columns;
});

Filtres essentiels

// Modifier le contenu de l'article
add_filter('the_content', function($content) {
    if (is_single() && in_the_loop()) {
        // Ajouter un CTA après le contenu
        $cta = '<div class="post-cta"><a href="/newsletter">Abonnez-vous</a></div>';
        $content .= $cta;
    }
    return $content;
});

// Modifier l'extrait
add_filter('excerpt_length', function() { return 30; });
add_filter('excerpt_more', function() { return '...'; });

// Modifier la requête principale
add_filter('pre_get_posts', function($query) {
    if (!is_admin() && $query->is_main_query()) {
        if ($query->is_home()) {
            $query->set('posts_per_page', 12);
        }
        if ($query->is_category()) {
            $query->set('orderby', 'title');
            $query->set('order', 'ASC');
        }
    }
});

// Ajouter des classes au body
add_filter('body_class', function($classes) {
    if (is_single()) {
        $classes[] = 'single-article';
    }
    return $classes;
});

// Modifier le texte "Lire la suite"
add_filter('the_content_more_link', function() {
    return '<a href="' . get_permalink() . '" class="read-more">Lire la suite →</a>';
});

// Personnaliser le formulaire de connexion
add_filter('login_headerurl', function() {
    return home_url('/');
});

// Modifier les paramètres de l'upload
add_filter('upload_mimes', function($mimes) {
    $mimes['svg'] = 'image/svg+xml';  // Autoriser les SVG
    $mimes['webp'] = 'image/webp';    // Autoriser les WebP
    return $mimes;
});

// Désactiver les emojis WordPress (performance)
add_filter('emoji_svg_url', '__return_false');
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');

Créer vos propres hooks

Vous pouvez créer vos propres hooks dans vos thèmes et plugins pour les rendre extensibles :

Créer une action personnalisée

// Dans votre code, à l'endroit où vous voulez permettre l'extension
function itsc_process_order($order_id) {
    // Votre logique de commande...
    
    // Déclencher votre hook personnalisé
    do_action('itsc_order_completed', $order_id, $order_total);
    
    // D'autres développeurs peuvent s'y accrocher :
    // add_action('itsc_order_completed', 'ma_notification_sms', 10, 2);
}

Créer un filtre personnalisé

// Dans votre code
function itsc_format_price($price) {
    $formatted = number_format($price, 0, ',', ' ') . ' FCFA';
    
    // Permettre la modification du format
    return apply_filters('itsc_price_format', $formatted, $price);
}

// Un autre développeur peut modifier le format :
add_filter('itsc_price_format', function($formatted, $price) {
    return $price . ' XOF'; // Format différent
}, 10, 2);

Supprimer des hooks

Vous pouvez retirer un hook ajouté par WordPress, un thème ou un plugin :

// Supprimer une action
remove_action('wp_head', 'wp_generator'); // Retire la version WordPress du <head>
remove_action('wp_head', 'wlwmanifest_link'); // Retire le lien WLW
remove_action('wp_head', 'rsd_link'); // Retire le lien RSD
remove_action('wp_head', 'wp_shortlink_wp_head'); // Retire le shortlink

// Supprimer un filtre
remove_filter('the_content', 'wpautop'); // Retire les <p> automatiques

// Important : la priorité doit être la même que lors de l'ajout
// Si le hook a été ajouté avec priorité 15 :
remove_action('wp_head', 'ma_fonction', 15);

Déboguer les hooks

Voir tous les hooks actifs

// Ajouter temporairement dans functions.php
function itsc_debug_hooks() {
    global $wp_filter;
    
    // Voir tous les filtres sur 'the_content'
    if (isset($wp_filter['the_content'])) {
        echo '<pre>';
        print_r($wp_filter['the_content']);
        echo '</pre>';
    }
}
add_action('wp_footer', 'itsc_debug_hooks');

Plugin : Query Monitor

Installez Query Monitor (gratuit) — il affiche tous les hooks déclenchés sur chaque page, avec le temps d’exécution de chaque callback. C’est l’outil de debug WordPress le plus utile.

Bonnes pratiques pour les hooks

1. Toujours préfixer vos fonctions

// MAUVAIS : risque de conflit
function send_email() { }
add_action('publish_post', 'send_email');

// BON : préfixe unique
function itsc_send_email_on_publish() { }
add_action('publish_post', 'itsc_send_email_on_publish');

2. Toujours retourner dans les filtres

// MAUVAIS : casse tout le contenu du site
add_filter('the_content', function($content) {
    if (is_single()) {
        $content .= '<p>Merci d'avoir lu !</p>';
    }
    // Oubli de return = le contenu disparaît partout
});

// BON : retour dans tous les cas
add_filter('the_content', function($content) {
    if (is_single()) {
        $content .= '<p>Merci d'avoir lu !</p>';
    }
    return $content; // TOUJOURS retourner
});

3. Vérifier le contexte

// MAUVAIS : modifie le titre PARTOUT (admin, menus, widgets...)
add_filter('the_title', function($title) {
    return $title . ' — MonSite';
});

// BON : seulement dans le contenu principal
add_filter('the_title', function($title, $post_id) {
    if (is_single() && in_the_loop() && is_main_query()) {
        return $title . ' — MonSite';
    }
    return $title;
}, 10, 2);

4. Utiliser les conditional tags

is_single()      // Article individuel
is_page()        // Page
is_home()        // Page d'accueil du blog
is_front_page()  // Page d'accueil (statique ou blog)
is_archive()     // Page d'archive (catégorie, tag, date)
is_category()    // Page de catégorie
is_search()      // Page de résultats de recherche
is_admin()       // Dashboard admin
is_user_logged_in()  // Utilisateur connecté
in_the_loop()    // Dans la boucle WordPress
is_main_query()  // Requête principale (pas une requête secondaire)

Hooks utiles par cas d’usage

SEO et performance

// Retirer les éléments inutiles du <head>
remove_action('wp_head', 'wp_generator');
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wp_shortlink_wp_head');
remove_action('wp_head', 'rest_output_link_wp_head');

// Désactiver les commentaires sur les pages
add_filter('comments_open', function($open, $post_id) {
    if (get_post_type($post_id) === 'page') return false;
    return $open;
}, 10, 2);

WooCommerce

// Ajouter un texte sous le prix
add_action('woocommerce_after_shop_loop_item_title', function() {
    echo '<p class="livraison-info">Livraison Dakar : 2 000 FCFA</p>';
}, 15);

// Modifier le bouton "Ajouter au panier"
add_filter('woocommerce_product_single_add_to_cart_text', function() {
    return 'Commander maintenant';
});

// Rediriger après ajout au panier
add_filter('woocommerce_add_to_cart_redirect', function() {
    return wc_get_checkout_url(); // Direct au checkout
});

Sécurité

// Limiter les tentatives de connexion
add_filter('authenticate', function($user, $username, $password) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $attempts = get_transient('login_attempts_' . $ip);
    
    if ($attempts >= 5) {
        return new WP_Error('too_many_attempts', 'Trop de tentatives. Réessayez dans 15 minutes.');
    }
    
    return $user;
}, 30, 3);

add_action('wp_login_failed', function() {
    $ip = $_SERVER['REMOTE_ADDR'];
    $attempts = (int) get_transient('login_attempts_' . $ip);
    set_transient('login_attempts_' . $ip, $attempts + 1, 15 * MINUTE_IN_SECONDS);
});

Confier la production à des professionnels

Au lieu de tout monter vous-même, déléguez la conception. Vous gardez le contrôle ensuite grâce à la formation incluse.

À partir de 350 000 FCFA

📧 E-mail
💬 WhatsApp

La philosophie des hooks : pourquoi WordPress est extensible

Les hooks ne sont pas une fonctionnalite parmi d autres : ce sont le coeur de l architecture WordPress. Chaque page generee par WordPress passe par des centaines de points d extension nommes, et c est leur ouverture qui explique pourquoi 60 pour cent des sites web mondiaux ont pu construire des plugins, des themes et des integrations sans jamais modifier le code source. La regle d or qui en decoule : on ne modifie jamais le core, ni un theme ou un plugin tiers — on s y branche par des hooks. Un site qui respecte cette discipline survit a toutes les mises a jour ; un site qui edite directement des fichiers du core ou des plugins finit casse au premier upgrade automatique.

Difference fondamentale entre actions et filtres

La distinction est mecanique mais structurante. Une action declenche une execution : WordPress dit que tel evenement se produit (publication d un article, chargement d une page, sauvegarde d un commentaire), et toute fonction abonnee a cet evenement s execute. Le retour de la fonction n est pas utilise. Un filtre transforme une donnee : WordPress passe une variable a la chaine de filtres, chaque fonction peut la modifier, et la version finale est utilisee par le reste du code. Le filtre doit toujours retourner la donnee, sinon le site casse.

Concretement : add_action( publish_post , maFonction) declenche maFonction quand un article est publie. add_filter( the_content , maFonction) permet a maFonction de modifier le contenu d un article avant affichage. La fonction de filtre doit imperativement faire return contenu a la fin, sinon les visiteurs voient une page blanche.

Les 20 hooks les plus utiles a connaitre

Actions de cycle de vie : init (chargement WordPress termine), wp_loaded (toutes les options chargees), template_redirect (avant rendu du template), wp_head (debut HTML head), wp_footer (fin du body), shutdown (fin de requete).

Actions de contenu : save_post (a chaque sauvegarde d article), publish_post (publication), wp_insert_post (creation), delete_post (suppression), transition_post_status (changement de statut, plus generique).

Actions utilisateur : wp_login, wp_logout, user_register, profile_update, password_reset.

Filtres de contenu : the_content (contenu article avant affichage), the_title (titre), the_excerpt (extrait), get_the_date (date formatee), wp_nav_menu_items (items du menu de navigation).

Filtres de requete : pre_get_posts (modifier la requete principale), posts_where (clause WHERE SQL), posts_orderby (ordre de tri), found_posts (nombre total).

Priorite et nombre d arguments

Les fonctions add_action et add_filter acceptent quatre parametres : le nom du hook, la fonction (callback), la priorite (defaut 10), et le nombre d arguments (defaut 1). La priorite controle l ordre d execution lorsque plusieurs callbacks sont attaches au meme hook : plus le nombre est petit, plus c est execute tot. Une priorite 5 passe avant 10, qui passe avant 99. Pour s assurer qu un filtre s execute en dernier (utile pour ecraser des modifications d autres plugins), on utilise une priorite tres elevee comme 9999.

Le nombre d arguments est crucial pour les hooks qui en transmettent plusieurs. Par exemple save_post passe trois arguments : l ID du post, l objet WP_Post complet, et un booleen indiquant si c est une mise a jour. Pour les recevoir tous, il faut declarer add_action( save_post , maFonction, 10, 3), sinon WordPress ne fournit que l ID.

Hooks de WordPress vs hooks de plugins

Outre les hooks du core, chaque plugin majeur expose ses propres hooks. WooCommerce en propose des centaines : woocommerce_thankyou (apres une commande), woocommerce_payment_complete, woocommerce_email_order_meta, woocommerce_before_cart. Yoast SEO et Rank Math exposent des filtres pour modifier le titre meta, la description, le schema. Contact Form 7 et WPForms ont des hooks pour modifier les emails de notification ou ajouter une logique post-soumission.

La discipline consiste a toujours chercher d abord un hook officiel avant d ecrire un workaround. Quand un hook manque, deposer une issue sur le depot du plugin (les mainteneurs ajoutent regulierement des hooks demandes). Si l urgence ne permet pas d attendre, le filtre output_buffer capture le rendu final et permet des modifications de derniere minute, mais c est un patch a documenter et a remplacer des qu un hook devient disponible.

Le piege classique : actions executees deux fois

Un bug recurrent chez les developpeurs WordPress : un hook execute son code deux fois (envoyant un email en double, dupliquant une entree en base). La cause est presque toujours la meme : le hook s attache plusieurs fois parce que le code est dans un mu-plugin charge a chaque requete ET dans un fichier functions.php inclus deux fois, ou parce qu il y a une boucle WP_Query dans le code qui re-declenche un hook. La parade : verifier avec did_action(nomDuHook) et sortir tot si l action a deja ete executee, ou utiliser remove_action apres execution.

Cas particulier de save_post : declenche aussi sur les revisions, les auto-sauvegardes, et les sauvegardes via REST API. Il faut explicitement filtrer : if (defined( DOING_AUTOSAVE ) and DOING_AUTOSAVE) return; et tester if (wp_is_post_revision(postId)) return; en debut de fonction.

Performance : couts caches des hooks

Chaque hook ajoute un appel de fonction PHP. Sur une page complexe, WordPress execute facilement 10 000 a 50 000 hooks. Tant que les callbacks sont rapides, l impact est negligeable. Mais un callback qui fait une requete SQL ou un appel HTTP a chaque the_content peut multiplier le temps de generation de la page par 10. Le plugin Query Monitor affiche pour chaque page la liste des hooks executes et leur temps cumule — outil indispensable pour diagnostiquer un site lent.

Les bonnes pratiques de performance : ne s attacher qu aux hooks vraiment necessaires, mettre en cache les resultats couteux avec get_transient / set_transient, utiliser la priorite pour eviter de s executer avant les conditions necessaires, et eviter les boucles WP_Query a l interieur de filtres frequents comme the_content.

FAQ

Quelle difference entre do_action et apply_filters ?
do_action declenche un point d extension de type action (les callbacks ne retournent rien). apply_filters declenche un filtre (les callbacks recoivent une valeur et doivent la retourner, potentiellement modifiee). Si vous developpez un plugin, exposer des do_action et apply_filters dans votre code permet aux autres developpeurs de l etendre sans le forker.

Ou placer ses hooks : functions.php du theme, plugin, ou mu-plugin ?
Pour du code lie au theme (modifications de mise en page, scripts du theme), functions.php du theme enfant. Pour du code metier reutilisable (integrations API, automatisations), un plugin classique. Pour du code critique d infrastructure (hardening securite, observabilite), un mu-plugin (dans wp-content/mu-plugins/) qui se charge automatiquement et qu un client ne peut pas desactiver par erreur.

Comment lister tous les hooks d une page pour explorer ?
Le plugin Query Monitor liste les hooks executes dans son onglet Hooks. Plus simple : ajouter add_action( all , function(hookName) { error_log(hookName); }); dans un mu-plugin de dev pour journaliser tous les hooks declenches sur une page de test, puis retirer apres exploration.

Peut-on retirer un hook ajoute par un plugin tiers ?
Oui avec remove_action ou remove_filter, a condition de connaitre exactement le nom du hook, le nom de la fonction callback, et la priorite avec laquelle elle a ete attachee. Si le plugin attache via une methode d une classe, il faut passer un array [instance, methodName] et avoir une reference a l instance, ce qui n est pas toujours trivial.

References

Service ITSkillsCenter

Application mobile Android et iOS

Création d'application mobile Android et iOS. À partir de 350 000 FCFA.

Démarrer mon projet
Publicité