# Un register_deactivation_hook qui oublie de nettoyer un cron actif

> Pour les développeurs qui déboguent une tâche planifiée fantôme après désactivation d'une extension : symptôme, diagnostic, correctif et prévention.

- Auteur : WordPress Développement
- Publié le : 2022-02-13
- Mis à jour le : 2022-02-13
- Catégorie : Extensions
- URL : https://www.wpmoderne.fr/extensions/deactivation-hook-oublie-cron-actif/

## L’essentiel

- Une tâche planifiée survit à la désactivation si elle n'est pas retirée explicitement
- wp_clear_scheduled_hook() doit être appelé dans le hook de désactivation
- Une tâche fantôme continue d'exécuter du code qui n'existe plus

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.

> L'essentiel à retenir : Une tâche planifiée survit à la désactivation si elle n'est pas retirée explicitement ; wp_clear_scheduled_hook() doit être appelé dans le hook de désactivation ; Une tâche fantôme continue d'exécuter du code qui n'existe plus

## 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.php` distinct 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.
