web-dev-qa-db-fra.com

Comment supprimer un filtre qui est un objet anonyme?

Dans mon fichier functions.php, j'aimerais supprimer le filtre ci-dessous, mais je ne sais pas comment le faire car il fait partie d'une classe. À quoi devrait ressembler remove_filter()?

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

C'est sur la ligne 88 ici .

61
Jonas

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;
}

Maintenant nous voyons ceci:

enter image description here

WordPress a besoin d'un nom pour le filtre. Nous n’en avons pas fourni, donc WordPress appelle _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 courons 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 par les filtres associés pour le hook (une action est simplement un filtre très simple), vérifiez s'il s'agit d'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 saute 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:

enter image description here

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.
79
fuxia

Tant que vous connaissez l'objet (et que vous utilisez PHP 5.2 ou une version ultérieure - la version stable PHP actuelle est 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 à l'aide de la commande remove_filter() méthode. 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 &, qui était nécessaire dans PHP 4 (!) Et qui 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.

0
hakre

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;
    }
}
0
Hamed Momeni