¿Cómo eliminar un filtro que es un objeto anónimo?

60

En mi archivo functions.php me gustaría eliminar el filtro a continuación, pero no estoy seguro de cómo hacerlo ya que está en una clase. ¿Cómo debería ser remove_filter() ?

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

Está en la línea 88 aquí .

    
pregunta Jonas 01.07.2012 - 10:02

3 respuestas

77

Esa es una muy buena pregunta. Se reduce al corazón oscuro de la API del complemento y las mejores prácticas de programación.

Para la siguiente respuesta, creé un complemento simple para ilustrar el problema con un código fácil de leer.

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

Ahora vemos esto:

WordPressnecesitaunnombreparaelfiltro.Noproporcionamosuno,porloqueWordPressllamaa _wp_filter_build_unique_id() y crea uno. Este nombre no es predecible porque utiliza spl_object_hash() .

Si ejecutamos un var_export() en $GLOBALS['wp_filter'][ 'wp_footer' ] obtenemos algo como esto ahora:

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 y eliminar nuestra acción malvada, debemos pasar por los filtros asociados para el enlace (una acción es solo un filtro muy simple), verifique si es una matriz y si el objeto es una instancia de la clase. Luego tomamos la prioridad y eliminamos el filtro, sin ver nunca el identificador real .

Está bien, pongamos eso en una función:

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

¿Cuándo llamamos a esta función? No hay forma de saber con seguridad cuándo se crea el objeto original. Tal vez a veces antes de 'plugins_loaded' ? ¿Quizas mas tarde?

Usamos el mismo gancho al que está asociado el objeto y saltamos muy pronto con la prioridad 0 . Esa es la única manera de estar realmente seguro. Aquí es cómo eliminaríamos el 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:

Y eso debería eliminar la acción de su pregunta (no verificada):

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

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

Conclusión

  • Siempre escribe código predecible. Establezca nombres legibles para sus filtros y acciones. Facilita la eliminación de cualquier gancho.
  • Cree su objeto en una acción predecible, por ejemplo, en 'plugins_loaded' . No solo cuando WordPress llama a tu complemento
respondido por el fuxia 01.07.2012 - 13:25
0

No estoy seguro, pero puedes intentar usar un singleton.
Debe almacenar la referencia del objeto en una propiedad estática de su clase y luego devolver esa variable estática desde un método estático. Algo como esto:

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

Mientras se conozca el objeto (y use PHP 5.2 o superior: la versión actual de PHP estable es 5.5, 5.4 aún es compatible, 5.3 es el final de la vida útil), puede eliminarlo con el método remove_filter() . Todo lo que necesita recordar es el objeto, el nombre del método y la prioridad (si se usa):

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

Sin embargo, usted comete un pequeño error en su código. No coloque el prefijo $this con el signo y & , que se necesitó en PHP 4 (!) Y hace mucho tiempo que está atrasado. Esto puede hacer que el manejo de tus ganchos sea problemático, así que simplemente déjalo fuera del camino:

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

Y eso es todo.

    
respondido por el hakre 28.07.2013 - 13:59

Lea otras preguntas en las etiquetas