Chapitre 7.2 — Les hooks en pratique
⏱️ TL;DR — Un plugin s’organise autour des hooks (Partie 5). En pratique : on encapsule le plugin dans une classe (ou un namespace) pour éviter les collisions et regrouper la logique, on branche les hooks dans une méthode dédiée, on charge les dépendances proprement, et on choisit le bon hook au bon moment (
initpour enregistrer,wp_enqueue_scriptspour les assets,save_postpour réagir à un enregistrement…). Objectif : un plugin lisible, testable et sans effets de bord.
🎯 Objectifs
- Structurer un plugin autour d’une classe principale.
- Brancher les hooks proprement (et pouvoir les retirer).
- Choisir le bon hook selon l’objectif.
- Éviter les pièges (closures non retirables, hook trop tôt/tard, instanciation multiple).
1. Encapsuler dans une classe
Plutôt que des dizaines de fonctions globales préfixées, on regroupe la logique dans une classe (les méthodes évitent les collisions et clarifient l’organisation) :
<?php
// includes/class-wpr-books.php
namespace WPR\Books;
if ( ! defined( 'ABSPATH' ) ) { exit; }
final class Plugin {
private static ?Plugin $instance = null;
public static function instance(): Plugin {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->register_hooks();
}
private function register_hooks(): void {
add_action( 'init', array( $this, 'register_content' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_front' ) );
add_action( 'save_post_livre', array( $this, 'on_save_livre' ), 10, 3 );
}
public function register_content(): void { /* CPT, taxonomies (ch. 7.3) */ }
public function enqueue_front(): void { /* assets (ch. 6.4) */ }
public function on_save_livre( $post_id, $post, $update ): void { /* réagir */ }
}Dans le fichier principal, on amorce une seule instance :
// wpr-books.php (après l'en-tête)
require_once __DIR__ . '/includes/class-wpr-books.php';
add_action( 'plugins_loaded', function () {
\WPR\Books\Plugin::instance();
} );⚠️ Piège n°1 — l’instanciation multiple. Sans singleton (ou garde), un plugin instancié plusieurs fois branche ses hooks en double → callbacks exécutés deux fois. Le patron
instance()garantit une seule initialisation.
2. Brancher (et pouvoir retirer) les hooks
Utiliser des méthodes (array( $this, 'methode' )) plutôt que des closures permet de retirer un hook si besoin (remove_action avec la même référence) et facilite les tests. Rappel (ch. 5.2) :
add_action( 'save_post_livre', array( $this, 'on_save_livre' ), 10, 3 );
// retrait possible :
remove_action( 'save_post_livre', array( $this, 'on_save_livre' ), 10 );⚠️ Piège n°2 — la closure non retirable.
add_action('init', function(){…})marche mais est impossible à retirer et dur à tester (pas de référence stable). Préférez des méthodes nommées pour tout hook qu’on pourrait devoir désactiver ou tester.
3. Choisir le bon hook (table de survie)
| Objectif | Hook | Note |
|---|---|---|
| Enregistrer CPT/taxonomie/shortcode | init | Jamais avant (ch. 5.1). |
| Charger CSS/JS front | wp_enqueue_scripts | Admin : admin_enqueue_scripts. |
| Charger les assets de l’éditeur de blocs | enqueue_block_editor_assets | Partie 8. |
| Ajouter une page/menu d’admin | admin_menu | Partie 7.5. |
| Réagir à l’enregistrement d’un contenu | save_post / save_post_{type} | 3 args (ch. 5.2). |
| Modifier la requête d’une page | pre_get_posts | Cibler is_main_query() (ch. 5.5). |
| Enregistrer des réglages | admin_init | Settings API (ch. 7.5). |
| Ajouter une route REST | rest_api_init | Partie 9. |
| Traductions | init (load_plugin_textdomain) | Ch. 7.8. |
💡 Pour un dev React — La classe
Pluginavec sonregister_hooks()est votre point de montage : un endroit unique qui déclare tous les abonnements (comme un fichier qui enregistre les listeners/effets au démarrage). Chaque méthode publique = un handler. Cette structure rend le plugin lisible d’un coup d’œil (« que fait ce plugin ? → regardezregister_hooks()») et testable (on appelle les méthodes directement).
4. Charger le code proprement
- Inclusions explicites (
require_once) pour un petit plugin, ou un autoloader (Composer PSR-4) pour un gros. - Charger au bon moment : les classes au chargement du fichier ; l’instanciation sur
plugins_loaded(tous les plugins présents) ; l’enregistrement du contenu surinit. - Éviter le travail lourd au chargement : ne faites pas de requêtes/API à chaque requête HTTP dès l’
include. Branchez sur un hook, et mettez en cache (ch. 5.7).
5. Un squelette réutilisable
Le trio fichier principal (amorçage) → classe Plugin (hooks) → classes spécialisées (CPT, meta, admin…) est un squelette réutilisable pour tous vos plugins. On l’étoffe dans les chapitres suivants (CPT, meta, admin, sécurité) et on l’assemble en entier au ch. 7.9 (wpr-books).
📚 Aller plus loin — WP-CLI peut générer ce squelette :
wp scaffold plugin wpr-books(ch. 4.3) crée une base avec structure, tests et build. Beaucoup d’équipes partent d’un boilerplate (ex. « WordPress Plugin Boilerplate ») pour standardiser l’organisation.
✏️ Exercices
- Pourquoi encapsuler un plugin dans une classe (ou un namespace) plutôt qu’en fonctions globales ?
- Sur quel hook enregistrez-vous un CPT, et sur lequel chargez-vous les CSS du front ?
- Pourquoi préférer une méthode nommée à une closure pour un callback de hook ?
✅ Solution
- Pour éviter les collisions de noms (espace global partagé), regrouper la logique de façon lisible, et faciliter les tests et le retrait de hooks. Un singleton évite en plus l’instanciation multiple.
- CPT →
init; CSS du front →wp_enqueue_scripts(admin :admin_enqueue_scripts). - Parce qu’une méthode nommée a une référence stable : on peut la
remove_action()et la tester directement. Une closure anonyme n’est ni retirable ni facilement testable.
🧠 Quiz de révision
1. Comment éviter qu’un plugin branche ses hooks en double ?
En l’amorçant une seule fois (patron singleton instance() ou garde d’initialisation).
2. Où déclare-t-on tous les abonnements aux hooks ?
Dans une méthode dédiée (ex. register_hooks()) de la classe principale, appelée à l’amorçage.
3. Sur quel hook instancie-t-on typiquement le plugin ?
plugins_loaded (tous les plugins chargés), l’enregistrement du contenu se faisant ensuite sur init.
4. Pourquoi ne pas faire de travail lourd au chargement du fichier ?
Parce qu’il s’exécute à chaque requête ; on branche sur un hook et on met en cache pour ne pas ralentir le site.
5. Quel outil génère un squelette de plugin ?
WP-CLI : wp scaffold plugin nom (ou un boilerplate existant).
Chapitre suivant : Custom Post Types & taxonomies.