Como remover um filtro que é um objeto anônimo?

60

No meu arquivo functions.php , gostaria de remover o filtro abaixo, mas não sei como fazê-lo, pois ele está em uma classe. Como deve ser o remove_filter() ?

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

Está on-line 88 aqui .

    
por Jonas 01.07.2012 / 10:02
fonte

3 respostas

77

Essa é uma boa pergunta. Isso vai ao coração sombrio da API de plugins e às melhores práticas de programação.

Para a resposta a seguir, criei um plug-in simples para ilustrar o problema com código de fácil leitura.

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

Agora vemos isso:

OWordPressprecisadeumnomeparaofiltro.Nãofornecemosum,porissooWordPresschama _wp_filter_build_unique_id() e cria um. Esse nome não é previsível porque usa spl_object_hash() .

Se executarmos um var_export() em $GLOBALS['wp_filter'][ 'wp_footer' ] , obteremos algo assim agora:

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,
    ),
  ),
)

Para encontrar e remover nossa ação maligna, precisamos passar pelos filtros associados ao gancho (uma ação é apenas um filtro muito simples), verifique se é uma matriz e se o objeto é uma instância da classe. Então pegamos a prioridade e removemos o filtro, sem nunca ver o identificador real .

Ok, vamos colocar isso em uma função:

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

Quando chamamos essa função? Não há como saber com certeza quando o objeto original é criado. Talvez às vezes antes de 'plugins_loaded' ? Talvez mais tarde?

Nós usamos apenas o mesmo gancho ao qual o objeto está associado e pularemos muito cedo com a prioridade 0 . Essa é a única maneira de ter certeza. Veja como removeríamos o método 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'
    );
}

Resultado:

E isso deve remover a ação da sua pergunta (não testada):

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

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

Conclusão

  • Sempre escreva um código previsível. Defina nomes legíveis para seus filtros e ações. Facilite a remoção de qualquer gancho.
  • Crie seu objeto em uma ação previsível, por exemplo, em 'plugins_loaded' . Não apenas quando seu plugin é chamado pelo WordPress.
por fuxia 01.07.2012 / 13:25
fonte
0

Não tenho certeza, mas você pode tentar usar um singleton.
Você deve armazenar a referência do objeto em uma propriedade estática da sua classe e, em seguida, retornar essa variável estática de um método estático. Algo parecido com isto:

class MyClass{
    private static $ref;
    function MyClass(){
        $ref = &$this;
    }
    public static function getReference(){
        return self::$ref;
    }
}
    
por Hamed Momeni 01.07.2012 / 12:41
fonte
0

Contanto que você conheça o objeto (e você usa o PHP 5.2 ou superior - a versão estável do PHP atual é 5.5, 5.4 ainda é suportado, 5.3 é final de vida), basta removê-lo com o método remove_filter() . Tudo que você precisa lembrar é o objeto, o nome do método e a prioridade (se usada):

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

No entanto, você comete um pequeno erro no seu código. Não prefixar $this com o e comercial & , que era necessário no PHP 4 (!) E está atrasado há muito tempo. Isso pode tornar problemático lidar com seus ganchos, então deixe-o fora do caminho:

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

E é isso.

    
por hakre 28.07.2013 / 13:59
fonte