Une extension désactivée depuis plusieurs semaines continue pourtant d’exécuter, chaque nuit, une fonction de purge que personne n’a demandée. C’est un cas classique rencontré lors de l’audit d’un site aux performances dégradées, et il illustre une asymétrie fréquente entre activation et désactivation d’une extension.
Symptôme
Le tableau de bord d’état du site (ou une extension de gestion des tâches planifiées) affiche une tâche encore programmée, associée à un nom de hook qui correspond à une extension pourtant désactivée depuis un moment. Selon les cas, cette tâche fantôme s’exécute silencieusement sans effet visible, ou provoque une erreur si elle appelle une fonction qui n’est plus définie, faute d’extension active pour la fournir.

Diagnostic
Une commande WP-CLI permet de lister l’ensemble des tâches planifiées actives, avec leur prochaine date d’exécution :
wp cron event list --fields=hook,next_run_relative
Si cette liste montre un hook correspondant à une extension désactivée, la cause est presque toujours la même : cette extension a planifié la tâche avec wp_schedule_event() dans son register_activation_hook(), mais n’a jamais implémenté de register_deactivation_hook() symétrique pour la retirer. WordPress ne supprime jamais automatiquement une tâche planifiée à la désactivation d’une extension : ce nettoyage reste entièrement à la charge du code de l’extension elle-même.
Correctif
La fonction wp_clear_scheduled_hook() retire toutes les occurrences planifiées d’un hook donné. Elle doit être appelée depuis le hook de désactivation, de façon strictement symétrique à la planification faite à l’activation :
function mon_extension_activation() {
if ( ! wp_next_scheduled( 'mon_extension_purge_nocturne' ) ) {
wp_schedule_event( time(), 'daily', 'mon_extension_purge_nocturne' );
}
}
register_activation_hook( __FILE__, 'mon_extension_activation' );
function mon_extension_desactivation() {
wp_clear_scheduled_hook( 'mon_extension_purge_nocturne' );
}
register_deactivation_hook( __FILE__, 'mon_extension_desactivation' );
Pour une extension déjà en production, où une tâche fantôme s’est accumulée sur des sites clients, une correction ponctuelle en ligne de commande permet de nettoyer l’existant sans attendre une mise à jour :
wp cron event delete mon_extension_purge_nocturne
Cette commande retire la tâche du site concerné, mais elle ne corrige pas la cause : sans le hook de désactivation ajouté au code de l’extension, une future réactivation puis désactivation reproduira exactement le même problème.
Un cas plus délicat : la désinstallation complète
Le hook de désactivation ne suffit pas toujours à couvrir tout le cycle de vie d’une extension. Quand un utilisateur choisit de supprimer complètement l’extension depuis l’écran « Extensions », WordPress déclenche un mécanisme distinct, généralement via un fichier uninstall.php placé à la racine de l’extension. Ce fichier reste indépendant du hook de désactivation : une extension peut très bien nettoyer correctement ses tâches planifiées à la désactivation, tout en oubliant de supprimer ses options ou ses tables lors d’une désinstallation définitive. Les deux mécanismes répondent à des moments différents du cycle de vie et méritent chacun leur propre vérification, sans supposer que l’un couvre automatiquement l’autre.
// Dans uninstall.php, exécuté uniquement lors d'une suppression définitive
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
wp_clear_scheduled_hook( 'mon_extension_purge_nocturne' );
delete_option( 'mon_extension_reglages' );
Prévention
- Considérer chaque appel à
wp_schedule_event()dans un hook d’activation comme incomplet tant qu’il n’a pas son pendant dans un hook de désactivation. - Vérifier systématiquement, lors d’une revue de code, que chaque planification a bien sa désinscription correspondante.
- Documenter, dans un commentaire au-dessus de chaque
wp_schedule_event(), l’emplacement exact du nettoyage correspondant, pour faciliter la relecture future. - Tester explicitement le cycle activation puis désactivation avant chaque publication, pas seulement l’activation seule.
- Prévoir également un fichier
uninstall.phpdistinct pour couvrir le cas d’une suppression définitive, plutôt qu’une simple désactivation temporaire.
Une tâche planifiée oubliée ne fait aucun bruit : elle continue simplement de s’exécuter, discrètement, bien après que tout le monde a considéré l’extension comme retirée.
En résumé
Le hook de désactivation existe précisément pour défaire ce que l’activation a mis en place. Oublier wp_clear_scheduled_hook() laisse une tâche fantôme tourner indéfiniment, parfois pendant des mois, sans qu’aucun utilisateur ne s’en aperçoive avant qu’un audit ne la remonte. Vérifier cette symétrie devrait faire partie de toute checklist de publication d’extension.