vendredi 25 septembre 2026

À propos

Contact

Extensions

remove_action sur une méthode de classe anonyme : débrancher un hook tenace

Un hook accroché par une instance à laquelle vous n'avez pas accès résiste à remove_action. Voici comment le retrouver dans $wp_filter et le neutraliser proprement.

Par Clément Hadrot • 2 janvier 2023 • 5 min de lecture • Aucun commentaire
remove_action sur une méthode de classe anonyme : débrancher un hook tenace

Symptôme : un client signale qu’un message d’avertissement s’affiche en haut de toutes les pages de son site, ajouté par une extension tierce qu’il ne veut plus voir. Le code source de cette extension montre un add_action( 'admin_notices', array( $this, 'afficher_avertissement' ) ) exécuté depuis une méthode d’initialisation, sur une instance créée à la volée et jamais stockée dans une variable globale accessible. Un remove_action( 'admin_notices', array( $obj, 'afficher_avertissement' ) ) classique ne fonctionne évidemment pas : on n’a pas d’accès à $obj.

C’est un cas fréquent avec les extensions qui instancient leur classe principale directement à l’intérieur d’un fichier, sans exposer de point d’accès global ni de fonction singleton. Le hook est bien là, actif, mais invisible depuis l’extérieur au sens strict de l’API PHP.

Pourquoi remove_action échoue silencieusement

WordPress identifie un callback enregistré avec une méthode d’objet par un hash calculé à partir du spl_object_hash de l’instance (ou, pour PHP récent, de son identifiant d’objet) combiné au nom de la méthode. remove_action() ne supprime un callback que si vous lui fournissez exactement la même paire objet/méthode, ou exactement la même closure. Sans référence à l’instance d’origine, impossible de reconstituer ce hash à la main : la fonction renvoie false, sans avertissement, et le hook reste actif.

Le même problème se pose avec les fonctions anonymes : une closure déclarée inline dans un add_action ne peut être retirée par un remove_action ultérieur que si vous conservez une référence à cette closure précise — recréer une closure identique en apparence ne suffit pas, PHP les considère comme deux objets distincts.

Retrouver le callback dans $wp_filter

La structure globale $wp_filter contient, pour chaque hook, un objet WP_Hook qui expose la liste des callbacks enregistrés, classés par priorité. On peut l’inspecter pour comprendre ce qui est réellement accroché :

L'essentiel à retenir : remove_action exige la même instance ou closure ; Parcourir $wp_filter pour identifier le callback ; Préférer des priorités plus tardives à la chirurgie
add_action( 'admin_notices', function() {
    global $wp_filter;
    if ( empty( $wp_filter['admin_notices'] ) ) {
        return;
    }
    foreach ( $wp_filter['admin_notices']->callbacks as $priorite => $callbacks ) {
        foreach ( $callbacks as $id => $donnees ) {
            $cible = $donnees['function'];
            if ( is_array( $cible ) && is_object( $cible[0] ) ) {
                error_log( sprintf(
                    'Hook admin_notices, priorité %s : classe %s, méthode %s',
                    $priorite,
                    get_class( $cible[0] ),
                    $cible[1]
                ) );
            }
        }
    }
}, 1 );

Ce journal permet d’identifier précisément la classe fautive et sa méthode. Une fois la classe connue, plusieurs stratégies s’offrent à vous, du plus propre au plus radical.

Solution 1 : retirer directement l’entrée de $wp_filter

Puisque l’objet WP_Hook est accessible globalement, on peut manipuler sa structure interne pour retirer un callback identifié par sa classe, sans avoir besoin de l’instance :

add_action( 'wp_loaded', function() {
    global $wp_filter;
    if ( empty( $wp_filter['admin_notices'] ) ) {
        return;
    }
    foreach ( $wp_filter['admin_notices']->callbacks as $priorite => $callbacks ) {
        foreach ( $callbacks as $id => $donnees ) {
            $cible = $donnees['function'];
            if ( is_array( $cible ) && is_object( $cible[0] )
                && get_class( $cible[0] ) === 'Extension_Tierce_Notices'
                && $cible[1] === 'afficher_avertissement' ) {
                unset( $wp_filter['admin_notices']->callbacks[ $priorite ][ $id ] );
            }
        }
    }
}, 999 );

Cette approche fonctionne, mais elle est fragile : elle dépend de la structure interne de WP_Hook, qui reste un détail d’implémentation. Elle doit rester une solution de dernier recours, documentée clairement dans le code pour qu’un futur mainteneur comprenne pourquoi elle existe.

Solution 2 : neutraliser en aval plutôt qu’en amont

Une alternative moins invasive consiste à laisser le hook s’exécuter, mais à masquer son effet après coup, par exemple en interceptant la sortie avec ob_start() et un filtre sur le buffer, ou en ciblant le rendu final via CSS si le contenu est prévisible. Pour un message d’admin, on peut aussi jouer sur l’ordre d’exécution : accrocher sa propre fonction sur admin_notices avec une priorité très basse, avant le hook fautif, et modifier une option globale que la méthode fautive consulte en interne — cette option n’existant en pratique que rarement.

Solution 3 : contacter l’auteur ou forker localement

Quand le hook gêne réellement le fonctionnement du site (pas juste un affichage cosmétique), la solution la plus saine reste de signaler le problème à l’auteur de l’extension, ou d’ajouter un filtre de compatibilité si l’extension en propose un. Beaucoup d’extensions bien conçues exposent justement un filtre pour désactiver leurs notices, une piste à vérifier avant de se lancer dans la chirurgie sur $wp_filter.

  • Chercher dans le code source un filtre du type nom_extension_disable_notices.
  • Vérifier la documentation ou le support de l’extension avant toute intervention manuelle.
  • Documenter systématiquement toute manipulation de $wp_filter avec un commentaire expliquant le contexte et la date.

Prévention côté auteur d’extension

Si vous développez vos propres extensions, évitez ce piège chez vos utilisateurs en exposant toujours un point d’accès à l’instance : une fonction globale mon_extension() qui retourne le singleton, ou une constante contenant l’instance. Cela permet à n’importe qui de faire un remove_action( 'hook', array( mon_extension(), 'methode' ) ) propre, sans avoir à fouiller dans les structures internes de WordPress.

Ce qu’il faut retenir

Un hook accroché depuis une instance inaccessible n’est pas irréversible, mais il exige de sortir de l’API publique pour aller lire directement $wp_filter. Identifiez d’abord précisément la classe et la méthode en cause, cherchez ensuite un filtre de compatibilité officiel, et ne touchez à la structure interne des hooks qu’en dernier recours, avec un commentaire clair sur le pourquoi. C’est un correctif de survie, pas une pratique à généraliser dans du code neuf.

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