Comment supprimer un filtre qui est un objet anonyme?

60

Dans mon fichier functions.php , je voudrais supprimer le filtre ci-dessous, mais je ne sais pas comment le faire car il est dans une classe. À quoi remove_filter() devrait-il ressembler?

add_filter('comments_array',array( &$this, 'FbComments' ));

C'est la ligne 88 ici .

    
posée Jonas 01.07.2012 - 10:02

3 réponses

77

C’est une très bonne question. Cela va au cœur sombre de l’API du plugin et aux meilleures pratiques de programmation.

Pour la réponse suivante, j'ai créé un plugin simple pour illustrer le problème avec du code facile à lire.

<?php # -*- coding: utf-8 -*-
/* Plugin Name: Anonymous OOP Action */

if ( ! class_exists( 'Anonymous_Object' ) )
{
    /**
     * Add some actions with randomized global identifiers.
     */
    class Anonymous_Object
    {
        public function __construct()
        {
            add_action( 'wp_footer', array ( $this, 'print_message_1' ), 5 );
            add_action( 'wp_footer', array ( $this, 'print_message_2' ), 5 );
            add_action( 'wp_footer', array ( $this, 'print_message_3' ), 12 );
        }

        public function print_message_1()
        {
            print '<p>Kill me!</p>';
        }

        public function print_message_2()
        {
            print '<p>Me too!</p>';
        }

        public function print_message_3()
        {
            print '<p>Aaaand me!</p>';
        }
    }

    // Good luck finding me!
    new Anonymous_Object;
}

Nous voyons maintenant ceci:

WordPressabesoind'unnompourlefiltre.Nousn’enavonspasfourni,doncWordPressappelle _wp_filter_build_unique_id() et en crée un. Ce nom n’est pas prévisible car il utilise spl_object_hash() .

Si nous lançons un var_export() sur $GLOBALS['wp_filter'][ 'wp_footer' ] , nous obtenons quelque chose comme ceci maintenant:

array (
  5 => 
  array (
    '000000002296220e0000000013735e2bprint_message_1' => 
    array (
      'function' => 
      array (
        0 => 
        Anonymous_Object::__set_state(array(
        )),
        1 => 'print_message_1',
      ),
      'accepted_args' => 1,
    ),
    '000000002296220e0000000013735e2bprint_message_2' => 
    array (
      'function' => 
      array (
        0 => 
        Anonymous_Object::__set_state(array(
        )),
        1 => 'print_message_2',
      ),
      'accepted_args' => 1,
    ),
  ),
  12 => 
  array (
    '000000002296220e0000000013735e2bprint_message_3' => 
    array (
      'function' => 
      array (
        0 => 
        Anonymous_Object::__set_state(array(
        )),
        1 => 'print_message_3',
      ),
      'accepted_args' => 1,
    ),
  ),
  20 => 
  array (
    'wp_print_footer_scripts' => 
    array (
      'function' => 'wp_print_footer_scripts',
      'accepted_args' => 1,
    ),
  ),
  1000 => 
  array (
    'wp_admin_bar_render' => 
    array (
      'function' => 'wp_admin_bar_render',
      'accepted_args' => 1,
    ),
  ),
)

Pour trouver et supprimer notre action maléfique, nous devons passer en revue les filtres associés au hook (une action est simplement un filtre très simple), vérifiez si c'est un tableau et si l'objet est une instance de la classe. Ensuite, nous prenons la priorité et retirons le filtre sans jamais voir l'identifiant réel .

OK, mettons cela dans une fonction:

if ( ! function_exists( 'remove_anonymous_object_filter' ) )
{
    /**
     * Remove an anonymous object filter.
     *
     * @param  string $tag    Hook name.
     * @param  string $class  Class name
     * @param  string $method Method name
     * @return void
     */
    function remove_anonymous_object_filter( $tag, $class, $method )
    {
        $filters = $GLOBALS['wp_filter'][ $tag ];

        if ( empty ( $filters ) )
        {
            return;
        }

        foreach ( $filters as $priority => $filter )
        {
            foreach ( $filter as $identifier => $function )
            {
                if ( is_array( $function)
                    and is_a( $function['function'][0], $class )
                    and $method === $function['function'][1]
                )
                {
                    remove_filter(
                        $tag,
                        array ( $function['function'][0], $method ),
                        $priority
                    );
                }
            }
        }
    }
}

Quand appelons-nous cette fonction? Il n'y a aucun moyen de savoir avec certitude quand l'objet original est créé. Peut-être parfois avant 'plugins_loaded' ? Peut-être plus tard?

Nous utilisons exactement le même point auquel l'objet est associé et sautons très tôt avec la priorité 0 . C’est la seule façon d’être vraiment sûr. Voici comment nous supprimerions la méthode print_message_3() :

add_action( 'wp_footer', 'kill_anonymous_example', 0 );

function kill_anonymous_example()
{
    remove_anonymous_object_filter(
        'wp_footer',
        'Anonymous_Object',
        'print_message_3'
    );
}

Résultat:

Et cela devrait supprimer l'action de votre question (non testée):

add_action( 'comments_array', 'kill_FbComments', 0 );

function kill_FbComments()
{
    remove_anonymous_object_filter(
        'comments_array',
        'SEOFacebookComments',
        'FbComments'
    );
}

Conclusion

  • Écrivez toujours du code prévisible. Définissez des noms lisibles pour vos filtres et vos actions. Rendez-le facile à enlever n'importe quel crochet.
  • Créez votre objet sur une action prévisible, par exemple sur 'plugins_loaded' . Pas seulement lorsque votre plugin est appelé par WordPress.
réponse donnée fuxia 01.07.2012 - 13:25
0

Je ne suis pas sûr, mais vous pouvez utiliser un singleton.
Vous devez stocker la référence de l'objet dans une propriété statique de votre classe, puis renvoyer cette variable statique à partir d'une méthode statique. Quelque chose comme ça:

class MyClass{
    private static $ref;
    function MyClass(){
        $ref = &$this;
    }
    public static function getReference(){
        return self::$ref;
    }
}
    
réponse donnée Hamed Momeni 01.07.2012 - 12:41
0

Tant que vous connaissez l'objet (et que vous utilisez PHP 5.2 ou une version ultérieure - la version stable actuelle est la version 5.5, la version 5.4 est toujours prise en charge, la version 5.3 est en fin de vie), vous pouvez simplement le supprimer avec la méthode remove_filter() . Tout ce dont vous devez vous souvenir est l’objet, le nom de la méthode et la priorité (si utilisée):

remove_filter('comment_array', [$this, 'FbComments']);

Cependant, vous faites une petite erreur dans votre code. Ne préfixez pas $this avec l'esperluette & , cela était nécessaire en PHP 4 (!) Et il est en retard depuis longtemps. Cela peut rendre la gestion de vos hooks problématique, alors laissez-le de côté:

add_filter('comments_array', [$this, 'FbComments]));

Et c'est tout.

    
réponse donnée hakre 28.07.2013 - 13:59

Lire d'autres questions sur les étiquettes