Terrain 12 — Premier code : hooks & functions.php
⏱️ TL;DR — 30 mini-jeux
⭐⭐⭐sur le mécanisme fondamental de WordPress : les hooks (actions & filtres). Tu vas brancher ton code sur le cœur sans le modifier. Travaille dans ton plugin labo (terrain 11) ou un thème enfant (terrain 13), avecWP_DEBUG_LOGactif.
💡 Pour un dev React — Une action = un event listener (« quand X arrive, fais Y ») ; un filtre = une transformation de valeur (« la donnée passe par ta fonction, tu la renvoies modifiée »). Tout WordPress est extensible par ces deux primitives.
🕹️ Mini-jeu 1 — Ton premier add_action ⭐⭐⭐ 💻
But : exécuter du code sur un événement. Mission : affiche un message en pied de chaque page. Étapes :
add_action( 'wp_footer', function () {
echo '<p style="text-align:center">Fait avec ❤️ en apprenant WordPress</p>';
} );✅ Solution
add_action( $hook, $callback ) attache ta fonction à un point d’action du cœur. wp_footer se déclenche juste avant </body> : ton echo s’affiche partout. Tu viens d’étendre WordPress sans toucher son code.
🕹️ Mini-jeu 2 — Action vs filtre ⭐⭐⭐ 💻
But : la distinction clé. Mission : énonce la différence, puis prédis lequel retourne une valeur.
✅ Solution
Action (do_action) : « il s’est passé quelque chose » → ton callback agit (echo, enregistre…) et ne retourne rien d’utile. Filtre (apply_filters) : « voici une valeur » → ton callback la modifie et la retourne. Oublier de return dans un filtre = bug classique (jeu 4).
🕹️ Mini-jeu 3 — Callback nommé plutôt qu’anonyme ⭐⭐⭐ 💻
But : du code réutilisable. Mission : réécris le jeu 1 avec une fonction nommée préfixée. Étapes :
function labo_footer_credit() {
echo '<p class="labo-credit">Appris sur le terrain 12</p>';
}
add_action( 'wp_footer', 'labo_footer_credit' );✅ Solution
Une fonction nommée et préfixée (labo_) est réutilisable et détachable (remove_action, jeu 7) — impossible avec une closure anonyme. Préfixe toujours pour éviter les collisions.
🕹️ Mini-jeu 4 — Ton premier filtre ⭐⭐⭐ 💻
But : modifier une valeur. Mission : ajoute une signature à la fin de chaque article. Étapes :
add_filter( 'the_content', function ( $content ) {
if ( is_single() ) {
$content .= '<p><em>— Merci d\'avoir lu.</em></p>';
}
return $content;
} );✅ Solution
the_content passe le contenu de l’article dans ta fonction : tu l’augmentes et le retournes. Sans le return $content, l’article disparaîtrait. is_single() limite aux articles seuls (jeu 17).
🕹️ Mini-jeu 5 — La priorité d’un hook ⭐⭐⭐ 💻
But : contrôler l’ordre. Mission : attache deux callbacks au même hook avec des priorités différentes. Étapes :
add_action( 'wp_footer', 'labo_un', 5 );
add_action( 'wp_footer', 'labo_deux', 20 );✅ Solution
Le 3e argument = priorité (défaut 10). Plus petit = plus tôt. Ici labo_un (5) s’exécute avant labo_deux (20). Levier pour se placer avant/après un autre callback (le tien ou celui d’une extension).
🕹️ Mini-jeu 6 — Le nombre d’arguments ⭐⭐⭐⭐ 💻
But : recevoir plus d’un paramètre.
Mission : utilise le 4e argument d’add_filter pour recevoir 2 arguments.
Étapes :
add_filter( 'the_title', 'labo_titre', 10, 2 );
function labo_titre( $title, $post_id ) {
return $title;
}✅ Solution
Le 4e argument (accepted_args) déclare combien de paramètres ton callback reçoit. the_title en fournit 2 (titre, ID) mais n’en passe qu’un si tu ne le demandes pas. Oublier ce chiffre = « argument manquant ».
🕹️ Mini-jeu 7 — Détacher un hook ⭐⭐⭐⭐ 💻
But : annuler un comportement. Mission : retire un callback attaché par défaut ou par une extension. Étapes :
remove_action( 'wp_head', 'wp_generator' );✅ Solution
remove_action/remove_filter détachent un callback (il faut le même nom et la même priorité). Ici on cache la version de WordPress du <head> (durcissement, terrain 16). Ne marche que sur des callbacks nommés.
🕹️ Mini-jeu 8 — Le hook init ⭐⭐⭐ 💻
But : le point d’entrée « tout est prêt ».
Mission : logue un message sur init.
Étapes :
add_action( 'init', function () {
error_log( 'Terrain 12 : init a tiré' );
} );✅ Solution
init se déclenche après le chargement de WordPress, avant tout rendu : c’est là qu’on enregistre CPT, taxonomies, shortcodes (terrain 15). error_log() écrit dans debug.log (terrain 11) — ton console.log serveur.
🕹️ Mini-jeu 9 — Charger un CSS proprement ⭐⭐⭐⭐ 💻
But : la BONNE façon d’ajouter des assets. Mission : enfile une feuille de style sur le front. Étapes :
add_action( 'wp_enqueue_scripts', function () {
wp_enqueue_style( 'labo', plugins_url( 'labo.css', __FILE__ ) );
} );✅ Solution
Jamais de <link>/<script> en dur dans wp_head : on enfile via wp_enqueue_style/script sur le hook wp_enqueue_scripts. WordPress gère alors dépendances, versions, ordre, dédoublonnage. Règle absolue (Partie 6).
🕹️ Mini-jeu 10 — Charger un JS avec dépendance ⭐⭐⭐⭐ 💻
But : dépendances gérées.
Mission : enfile un JS qui dépend de wp-element (React).
Étapes :
wp_enqueue_script(
'labo-js',
plugins_url( 'labo.js', __FILE__ ),
array( 'wp-element' ),
'1.0',
true
);✅ Solution
Le 3e argument = dépendances (chargées avant), le 5e true = dans le footer. Déclarer wp-element te donne React côté WordPress (Partie 8). WordPress résout l’ordre pour toi.
🕹️ Mini-jeu 11 — Une page d’admin ⭐⭐⭐⭐ 💻
But : ajouter une entrée de menu. Mission : crée une page sous « Réglages ». Étapes :
add_action( 'admin_menu', function () {
add_options_page( 'Labo', 'Labo', 'manage_options', 'labo', 'labo_page' );
} );
function labo_page() {
echo '<div class="wrap"><h1>Mon Labo</h1></div>';
}✅ Solution
admin_menu + add_options_page créent une page d’admin protégée par la capability manage_options (admin only). La fonction de rendu affiche le HTML. Base d’une page de réglages (Settings API, terrain 15).
🕹️ Mini-jeu 12 — Réagir à l’enregistrement ⭐⭐⭐⭐ 💻
But : agir quand un article est sauvé.
Mission : logue l’ID à chaque save_post.
Étapes :
add_action( 'save_post', function ( $post_id ) {
if ( wp_is_post_revision( $post_id ) ) return;
error_log( "Article sauvé : $post_id" );
} );✅ Solution
save_post tire à chaque sauvegarde. On ignore les révisions (sinon double déclenchement). C’est le hook où l’on sauve des métadonnées custom (terrain 15) — avec vérif nonce + capability (terrain 16).
🕹️ Mini-jeu 13 — Un filtre sur la classe du body ⭐⭐⭐ 💻
But : styler selon le contexte.
Mission : ajoute une classe CSS au <body>.
Étapes :
add_filter( 'body_class', function ( $classes ) {
$classes[] = 'theme-labo';
return $classes;
} );✅ Solution
body_class reçoit un tableau de classes : tu en ajoutes une et retournes le tableau. Tu peux alors cibler .theme-labo en CSS. Modèle typique du filtre qui manipule une liste.
🕹️ Mini-jeu 14 — Raccourcir les extraits ⭐⭐⭐ 💻
But : un filtre numérique. Mission : limite les extraits à 20 mots. Étapes :
add_filter( 'excerpt_length', function ( $len ) {
return 20;
} );✅ Solution
excerpt_length filtre un nombre. Beaucoup de comportements sont des valeurs filtrables (longueur, texte « […] » via excerpt_more). Cherche « filter » + ton besoin : il existe souvent un hook.
🕹️ Mini-jeu 15 — Ajouter un item au menu ⭐⭐⭐⭐ 💻
But : injecter dans la navigation. Mission : ajoute un lien en fin de menu principal. Étapes :
add_filter( 'wp_nav_menu_items', function ( $items, $args ) {
return $items . '<li><a href="/contact">Contact</a></li>';
}, 10, 2 );✅ Solution
wp_nav_menu_items filtre le HTML des items : on concatène. Combiné à $args->theme_location, on cible un menu précis. Répond au « menu conditionnel » évoqué au terrain 06, jeu 19.
🕹️ Mini-jeu 16 — Déclencher son propre hook ⭐⭐⭐⭐ 💻
But : rendre TON code extensible. Mission : expose une action et un filtre personnels. Étapes :
do_action( 'labo_apres_traitement', $data );
$prix = apply_filters( 'labo_prix', $prix );✅ Solution
do_action/apply_filters créent tes points d’extension : d’autres (ou toi) pourront s’y brancher sans modifier ton plugin. C’est ainsi qu’on écrit du code extensible (Partie 7) — la philosophie WordPress appliquée à ton propre code.
🕹️ Mini-jeu 17 — Les conditional tags ⭐⭐⭐ 💻
But : agir selon le contexte. Mission : n’ajoute la signature (jeu 4) que sur la home ET pas en admin. Étapes :
if ( is_front_page() && ! is_admin() ) {
// ...
}✅ Solution
is_front_page(), is_single(), is_page(), is_admin(), is_user_logged_in()… répondent « où/qui sommes-nous ». ⚠️ La plupart ne sont fiables qu’après le hook wp (pas trop tôt). Base de tout code contextuel.
🕹️ Mini-jeu 18 — Rediriger conditionnellement ⭐⭐⭐⭐ 💻
But : contrôler l’accès à une page. Mission : redirige les non-connectés d’une page vers l’accueil. Étapes :
add_action( 'template_redirect', function () {
if ( is_page( 'membres' ) && ! is_user_logged_in() ) {
wp_safe_redirect( home_url() );
exit;
}
} );✅ Solution
template_redirect tire avant le rendu du gabarit : moment idéal pour rediriger. Toujours wp_safe_redirect() puis exit. Brique d’un contenu réservé aux membres.
🕹️ Mini-jeu 19 — Un shortcode ⭐⭐⭐ 💻
But : insérer du dynamique dans le contenu.
Mission : crée [labo_annee] qui affiche l’année.
Étapes :
add_shortcode( 'labo_annee', function () {
return esc_html( date( 'Y' ) );
} );✅ Solution
add_shortcode mappe [labo_annee] à une fonction qui retourne (jamais echo) le HTML. Insérable via le bloc Shortcode (terrain 05, jeu 27). Un shortcode retourne, comme un filtre.
🕹️ Mini-jeu 20 — Shortcode avec attributs ⭐⭐⭐⭐ 💻
But : paramétrer.
Mission : [labo_salut nom="Alex"] → « Salut Alex ».
Étapes :
add_shortcode( 'labo_salut', function ( $atts ) {
$a = shortcode_atts( array( 'nom' => 'toi' ), $atts );
return 'Salut ' . esc_html( $a['nom'] );
} );✅ Solution
shortcode_atts fusionne les attributs passés avec des défauts — comme des props avec valeurs par défaut. Toujours échapper la sortie (esc_html). Pattern réutilisé partout.
🕹️ Mini-jeu 21 — Personnaliser le titre de la page login ⭐⭐⭐ 💻
But : toucher l’écran de connexion.
Mission : change le lien du logo de /wp-login.php.
Étapes :
add_filter( 'login_headerurl', function () {
return home_url();
} );✅ Solution
L’écran de login a ses propres hooks (login_headerurl, login_enqueue_scripts pour un CSS, login_headertext). White-labelliser le login est une demande client fréquente. Chaque « zone » de WordPress a ses hooks dédiés.
🕹️ Mini-jeu 22 — Autoriser un type de fichier ⭐⭐⭐⭐ 💻
But : étendre les uploads.
Mission : autorise l’upload de fichiers .svg (avec prudence).
Étapes :
add_filter( 'upload_mimes', function ( $mimes ) {
$mimes['svg'] = 'image/svg+xml';
return $mimes;
} );✅ Solution
upload_mimes filtre les types autorisés. ⚠️ Le SVG peut contenir du script (XSS) : à n’autoriser que pour des rôles de confiance + assainissement. Bon exemple d’un hook puissant mais sensible (terrain 16).
🕹️ Mini-jeu 23 — Un avis dans l’admin ⭐⭐⭐ 💻
But : communiquer avec l’admin. Mission : affiche une bannière d’information en admin. Étapes :
add_action( 'admin_notices', function () {
echo '<div class="notice notice-info"><p>Labo actif.</p></div>';
} );✅ Solution
admin_notices affiche les bandeaux du haut de l’admin (classes notice-info/success/warning/error). Utile pour prévenir/alerter. À conditionner (capability, page) pour ne pas spammer tous les écrans.
🕹️ Mini-jeu 24 — Vérifier la capability dans un hook ⭐⭐⭐⭐ 💻
But : sécuriser une action.
Mission : n’exécute une action sensible que si l’utilisateur peut manage_options.
Étapes :
if ( current_user_can( 'manage_options' ) ) {
// action réservée aux admins
}✅ Solution
current_user_can() est le garde de sécurité : on vérifie la capability, jamais le rôle en dur. Réflexe à graver avant toute action sensible (terrain 16). Cacher un bouton ne suffit pas — il faut vérifier au traitement.
🕹️ Mini-jeu 25 — Injecter dans le <head> ⭐⭐⭐ 💻
But : meta/vérification. Mission : ajoute une balise meta dans le head. Étapes :
add_action( 'wp_head', function () {
echo '<meta name="labo" content="terrain-12">' . "\n";
} );✅ Solution
wp_head (déclenché par wp_head() du thème) est le point d’injection du <head> : meta, balises de vérification, préconnexions. Les plugins SEO l’utilisent massivement. Ne jamais y mettre un asset (→ enqueue, jeu 9).
🕹️ Mini-jeu 26 — Trouver le bon hook ⭐⭐⭐⭐ 💻
But : la compétence méta. Mission : tu veux agir « juste après la publication d’un article » — trouve le hook.
✅ Solution
Cherche dans le Hooks Reference (developer.wordpress.org) ou active « Query Monitor » pour voir les hooks tirés. Ici : transition_post_status ou publish_post. La vraie compétence WordPress = savoir quel hook existe pour ton besoin.
🕹️ Mini-jeu 27 — Déboguer un hook qui ne tire pas ⭐⭐⭐⭐ 💻
But : méthode de diagnostic. Mission : ton callback ne s’exécute pas — liste 3 causes probables.
✅ Solution
Causes fréquentes : (1) hook attaché trop tard (après son déclenchement) ; (2) priorité/accepted_args incorrects ; (3) condition (is_*) évaluée trop tôt ; (4) faute de frappe dans le nom du hook. Ajoute un error_log() en tête de callback pour vérifier qu’il entre.
🕹️ Mini-jeu 28 — Filtre avec valeur conditionnelle ⭐⭐⭐⭐ 💻
But : modifier seulement parfois. Mission : double le nombre d’articles par page, mais seulement sur les archives. Étapes :
add_action( 'pre_get_posts', function ( $q ) {
if ( ! is_admin() && $q->is_main_query() && $q->is_archive() ) {
$q->set( 'posts_per_page', 20 );
}
} );✅ Solution
pre_get_posts modifie la requête principale avant exécution (puissant !). Toujours vérifier ! is_admin() et is_main_query() pour ne pas tout casser. Un des hooks les plus utiles (Partie 5).
🕹️ Mini-jeu 29 — Organiser son code ⭐⭐⭐ 💻
But : hygiène.
Mission : regroupe tes hooks du terrain dans des fichiers includes/ chargés depuis le plugin principal.
✅ Solution
Un functions.php ou plugin qui grossit devient illisible : découpe en includes/hooks-front.php, includes/admin.php, chargés par require_once. Préfixe tout. C’est le passage du snippet au plugin structuré (terrain 15).
🕹️ Mini-jeu 30 — Récap hooks ⭐⭐⭐ 💻
But : capstone du terrain.
Mission : un mini-plugin qui : enfile un CSS, ajoute une signature aux articles (filtre), une page d’admin (action), un shortcode, et retire wp_generator — le tout préfixé et sécurisé.
✅ Solution
Si tu combines actions, filtres, priorités, enqueue, capability et shortcode sans copier-coller aveugle, tu tiens le cœur du dev WordPress. Tous les terrains suivants (thèmes, plugins, sécurité, REST) ne sont que des applications des hooks. Bravo.
Terrain suivant : 13 — Thème enfant.