vendredi 25 septembre 2026

À propos

Contact

Extensions

Désinstallation propre : uninstall.php contre register_uninstall_hook

Options, tables personnalisées, tâches cron oubliées : ce qu'une extension doit vraiment nettoyer à la désinstallation, et pourquoi uninstall.php l'emporte en pratique.

Par Clément Hadrot • 20 juillet 2021 • 5 min de lecture • Aucun commentaire
Désinstallation propre : uninstall.php contre register_uninstall_hook

Un site WordPress qui accumule, au fil des années, des dizaines de tables et d’options orphelines appartenant à des extensions désinstallées depuis longtemps : ce scénario est d’une banalité affligeante. La cause est presque toujours la même — l’auteur de l’extension a soigné son activation et négligé sa désinstallation, considérée à tort comme un détail secondaire.

WordPress propose deux mécanismes pour gérer proprement la désinstallation d’une extension : le fichier uninstall.php et la fonction register_uninstall_hook(). Ils ne sont pas équivalents, et le choix entre les deux a des conséquences concrètes sur la fiabilité du nettoyage.

Désactivation, suppression : une distinction essentielle

La confusion la plus fréquente concerne le moment où le nettoyage doit intervenir. La désactivation d’une extension (via register_deactivation_hook()) est un état réversible : l’utilisateur peut réactiver l’extension l’instant d’après, et ses réglages doivent être préservés. La suppression (l’action « Supprimer » dans la liste des extensions, ou wp plugin delete) est en revanche définitive et c’est à ce moment précis, et uniquement à ce moment, que les données doivent être effacées.

HookSe déclenche lors deDoit nettoyer les données ?
register_deactivation_hookDésactivation depuis l’adminNon, jamais
uninstall.php / register_uninstall_hookSuppression définitiveOui, systématiquement

uninstall.php : la méthode recommandée

Un fichier nommé exactement uninstall.php, placé à la racine du dossier de l’extension, est automatiquement exécuté par WordPress au moment de la suppression, sans qu’aucun appel de fonction ne soit nécessaire dans le fichier principal. C’est l’approche recommandée par le Plugin Handbook, pour une raison de performance : ce fichier n’est chargé que lors de la suppression, jamais à chaque requête normale du site.

<?php
// uninstall.php

// Sécurité impérative : ce fichier ne doit jamais s'exécuter directement
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
    exit;
}

global $wpdb;

// Options simples
delete_option( 'acme_api_key' );
delete_option( 'acme_settings' );

// Métadonnées liées aux articles
$wpdb->query(
    "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE 'acme\_%'"
);

// Table personnalisée créée par l'extension
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}acme_journal" );

// Tâches planifiées restantes
wp_clear_scheduled_hook( 'acme_verification_stock' );

// Sites d'un réseau multisite : à traiter séparément (voir plus bas)

La vérification de la constante WP_UNINSTALL_PLUGIN en tout début de fichier n’est pas cosmétique : sans elle, un accès direct à ce fichier via une URL publique déclencherait la suppression des données du site, une faille de sécurité qui a réellement été exploitée sur des extensions négligentes par le passé.

L'essentiel à retenir : uninstall.php se déclenche uniquement à la suppression, pas à la désactivation ; register_uninstall_hook charge tout le plugin, uninstall.php non ; Prévoir un réglage pour conserver les données volontairement

register_uninstall_hook : quand l’éviter

L’alternative, register_uninstall_hook(), appelée depuis le fichier principal de l’extension, fonctionne aussi mais présente un inconvénient concret : elle oblige WordPress à charger l’intégralité du fichier principal de l’extension (et donc potentiellement toutes ses dépendances) uniquement pour exécuter cette fonction de nettoyage, y compris lors de requêtes normales où WordPress vérifie simplement si un hook de désinstallation existe.

// Dans le fichier principal, approche moins recommandée
register_uninstall_hook( __FILE__, 'acme_desinstaller' );

function acme_desinstaller() {
    delete_option( 'acme_api_key' );
}

Un autre piège spécifique à cette fonction : le callback ne peut pas être une méthode d’instance d’objet ni une closure, uniquement une fonction nommée ou une méthode statique, car WordPress sérialise la référence au callback en base de données pour la retrouver plus tard.

// Fonctionne
register_uninstall_hook( __FILE__, [ 'Acme\MonExtension\Uninstaller', 'run' ] );

// Ne fonctionne PAS : une closure ne peut pas être sérialisée
register_uninstall_hook( __FILE__, function () { /* ... */ } );

Ne pas oublier le cas multisite

Sur un réseau multisite, une extension activée sur l’ensemble du réseau doit nettoyer les données de chaque site individuellement, pas seulement du site courant au moment de la désinstallation.

if ( is_multisite() ) {
    $sites = get_sites( [ 'fields' => 'ids' ] );
    foreach ( $sites as $site_id ) {
        switch_to_blog( $site_id );
        delete_option( 'acme_api_key' );
        restore_current_blog();
    }
} else {
    delete_option( 'acme_api_key' );
}

restore_current_blog() après chaque switch_to_blog() est impératif : l’oublier laisse le contexte du site courant corrompu pour le reste du script de désinstallation, un bug difficile à repérer car il ne provoque pas d’erreur visible immédiate.

Laisser le choix à l’utilisateur

Pour certaines extensions, effacer systématiquement toutes les données à la suppression n’est pas souhaitable : un utilisateur qui désinstalle temporairement pour tester une alternative ne veut pas perdre sa configuration. Bonne pratique : un réglage explicite, décoché par défaut, du type « Supprimer toutes les données à la désinstallation ».

// uninstall.php
if ( get_option( 'acme_supprimer_donnees_a_la_desinstallation' ) ) {
    delete_option( 'acme_api_key' );
    // ... nettoyage complet
}

Un conseil qu’on donne systématiquement en revue de code : tester la désinstallation avant chaque publication, pas seulement l’activation. Un simple wp plugin uninstall suivi d’une inspection de la base suffit à repérer les oublis.

Vérifier le résultat avec WP-CLI

wp plugin deactivate mon-extension
wp plugin uninstall mon-extension
wp db query "SELECT option_name FROM wp_options WHERE option_name LIKE 'acme%'"

Si cette dernière requête retourne encore des lignes après la désinstallation, le nettoyage est incomplet.

En résumé

uninstall.php reste la méthode la plus performante et la plus lisible pour gérer la désinstallation d’une extension. Le nettoyage doit couvrir les options, les métadonnées, les tables personnalisées et les tâches planifiées, en tenant compte du multisite, et idéalement laisser à l’utilisateur le choix de conserver ses données. Ce travail, souvent traité en dernière minute, mérite le même soin que l’activation.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi