Chapitre 7.1 — Anatomie d’un plugin
⏱️ TL;DR — Un plugin, c’est un dossier dans
wp-content/plugins/contenant au minimum un fichier PHP avec un en-tête de commentaire (Plugin Name: …). WordPress le détecte et permet de l’activer. Un plugin bien fait gère trois moments : l’activation (register_activation_hook— créer tables, options, flush rewrite), la désactivation (nettoyage réversible), et la désinstallation (uninstall.php— suppression définitive des données). On préfixe tout, on protège contre l’accès direct, et on branche sa logique sur des hooks.
🎯 Objectifs
- Créer un plugin détecté et activable par WordPress.
- Comprendre activation / désactivation / désinstallation.
- Structurer les fichiers d’un plugin.
- Appliquer les protections de base (accès direct, préfixes).
1. Le minimum : un fichier avec en-tête
Créez wp-content/plugins/wpr-books/wpr-books.php :
<?php
/**
* Plugin Name: WPR Books
* Plugin URI: https://exemple.com/wpr-books
* Description: Gère un catalogue de livres (CPT, taxonomie, REST).
* Version: 1.0.0
* Requires at least: 6.4
* Requires PHP: 8.1
* Author: Vous
* License: GPL-2.0-or-later
* Text Domain: wpr-books
* Domain Path: /languages
*/
// Sécurité : empêcher l'accès direct au fichier
if ( ! defined( 'ABSPATH' ) ) {
exit;
}Dès que ce fichier existe avec Plugin Name, le plugin apparaît dans Extensions et peut être activé.
⚠️ Piège n°1 — l’accès direct. Le
if ( ! defined( 'ABSPATH' ) ) exit;en tête de chaque fichier PHP du plugin empêche qu’on l’exécute directement via son URL (hors du contexte WordPress) — une protection de base contre certaines attaques et fuites d’infos. À mettre systématiquement.
2. Structure d’un plugin qui grandit
Un plugin d’une fonction tient dans un fichier ; dès qu’il grossit, on organise :
wpr-books/
├── wpr-books.php ← fichier principal (en-tête + amorçage)
├── uninstall.php ← nettoyage à la désinstallation
├── includes/ ← le code (classes, fonctions)
│ ├── class-wpr-books.php
│ ├── class-cpt.php
│ └── class-meta.php
├── admin/ ← code spécifique à l'admin
├── assets/ ← CSS/JS (enqueue)
├── languages/ ← fichiers de traduction (.pot/.po/.mo)
└── readme.txt ← pour wp.orgLe fichier principal amorce le plugin (charge les classes, instancie, branche les hooks). On détaille l’organisation en classe au chapitre suivant.
3. Les trois moments : activation, désactivation, désinstallation
Activation — préparer le terrain (une fois)
register_activation_hook( __FILE__, 'wpr_books_activate' );
function wpr_books_activate() {
wpr_books_register_cpt(); // enregistrer le CPT (même fonction qu'au runtime)
flush_rewrite_rules(); // régénérer les URLs (ch. 5.9) — UNE fois
add_option( 'wpr_books_version', '1.0.0' );
// créer une table custom si besoin (dbDelta, ch. 5.6)
}Désactivation — nettoyage réversible
register_deactivation_hook( __FILE__, 'wpr_books_deactivate' );
function wpr_books_deactivate() {
wp_clear_scheduled_hook( 'wpr_books_tache' ); // retirer les tâches cron (ch. 5.8)
flush_rewrite_rules();
// NE PAS supprimer les données ici : la désactivation doit être réversible
}Désinstallation — suppression définitive (uninstall.php)
<?php
// uninstall.php — exécuté à la SUPPRESSION du plugin
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
delete_option( 'wpr_books_version' );
// supprimer options, meta, tables custom… (si l'utilisateur le souhaite)⚠️ Piège n°2 — confondre désactivation et désinstallation. La désactivation doit être réversible (on peut réactiver sans perdre de données) : n’y supprimez rien de définitif. La désinstallation (
uninstall.phpouregister_uninstall_hook) est le seul endroit pour supprimer les données. Supprimer les contenus d’un utilisateur à la simple désactivation est une faute grave.
💡 Pour un dev React — Ces hooks sont les lifecycle de votre plugin (comme un
install/uninstallde dépendance, ouonMount/onUnmountà l’échelle du plugin) : activation = setup (migrations, seed), désactivation = pause propre (sans perte), désinstallation = cleanup définitif. Bien les séparer évite les catastrophes (données effacées par erreur).
4. Bonnes pratiques structurelles
- Préfixer tout (
wpr_books_…, ou un namespace PHPWPR\Books\…) : l’espace global de PHP est partagé par tous les plugins → sans préfixe, collision fatale de noms. - Constantes utiles en tête :
define( 'WPR_BOOKS_PATH', plugin_dir_path( __FILE__ ) );etplugin_dir_url( __FILE__ )pour les assets. - Charger tard : brancher l’enregistrement (CPT, hooks) sur
init(ch. 5.1), pas au chargement brut. - Un plugin = une responsabilité : évitez le plugin « fourre-tout ». Découpez si nécessaire.
- Vérifier les prérequis (version PHP/WP) et échouer proprement plutôt que de provoquer une erreur fatale.
5. Activer et tester
- Déposez le dossier, activez dans Extensions (ou
wp plugin activate wpr-books). WP_DEBUGactivé (ch. 4.5) : un plugin propre ne génère aucune notice.- Testez le cycle complet : activer (les URLs/tables sont-elles créées ?), désactiver (réversible ?), désinstaller (le nettoyage se fait-il ?).
✏️ Exercices
- Quel est le strict minimum pour que WordPress détecte un plugin ?
- Où crée-t-on une table custom : à l’activation, à la désactivation ou à la désinstallation ? Et où la supprime-t-on ?
- Pourquoi préfixer (ou namespacer) systématiquement ses fonctions ?
✅ Solution
- Un fichier PHP dans
wp-content/plugins/…avec un en-tête de commentaire contenant au moinsPlugin Name:. WordPress le liste alors dans Extensions. - On crée la table à l’activation (
register_activation_hook, viadbDelta). On la supprime à la désinstallation (uninstall.php). La désactivation ne doit rien supprimer (réversible). - Parce que PHP a un espace de noms global partagé par tous les plugins : sans préfixe/namespace, deux fonctions homonymes provoquent une erreur fatale. Le préfixe garantit l’unicité.
🧠 Quiz de révision
1. Qu’est-ce qui rend un fichier reconnaissable comme plugin ?
L’en-tête de commentaire avec au minimum Plugin Name:.
2. À quoi sert register_activation_hook ?
À exécuter du code une fois à l’activation : créer tables/options, enregistrer le CPT et flush_rewrite_rules().
3. La désactivation doit-elle supprimer les données ?
Non : elle doit être réversible. La suppression des données se fait à la désinstallation (uninstall.php).
4. À quoi sert if ( ! defined( ‘ABSPATH’ ) ) exit; ?
À empêcher l’accès direct au fichier PHP hors du contexte WordPress (protection de base).
5. Pourquoi éviter l’espace de noms global sans préfixe ?
Pour éviter les collisions de noms entre plugins (erreur fatale) : on préfixe (wpr_books_…) ou on utilise un namespace.
Chapitre suivant : Les hooks en pratique.