如何删除一个匿名对象的过滤器?


62

在我的functions.php文件中,我想删除下面的过滤器,但是由于它在一个类中,所以我不确定该如何做。应该是什么remove_filter()样子?

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

这里是 88号线。


您应该&从中删除&$this,这是PHP 4的东西
Tom J Nowell

Answers:


79

这是一个很好的问题。它归结为插件API和最佳编程实践的核心。

对于以下答案,我创建了一个简单的插件,以易于阅读的代码来说明问题。

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

现在我们看到了:

在此处输入图片说明

WordPress需要过滤器的名称。我们没有提供一个,所以WordPress调用_wp_filter_build_unique_id()并创建了一个。此名称不可预测,因为它使用spl_object_hash()

如果运行一个var_export()$GLOBALS['wp_filter'][ 'wp_footer' ]我们现在将得到如下内容:

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

为了找到并删除我们的邪恶行为,我们必须通过与挂钩相关的过滤器(一个动作只是一个非常简单的过滤器),检查它是否为数组,以及对象是否为类的实例。然后,我们优先处理并删除过滤器,而不会看到真正的标识符

好的,让我们将其放入函数中:

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

什么时候调用此函数?无法确定何时创建原始对象。也许有时以前'plugins_loaded'?也许以后?

我们只使用与对象关联的同一个钩子,并以优先级非常早地进入0。那是唯一可以确定的方法。这是我们删除方法的方法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'
    );
}

结果:

在此处输入图片说明

那应该从您的问题中删除操作(未测试):

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

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

结论

  • 始终编​​写可预测的代码。为过滤器和操作设置可读的名称。轻松卸下任何挂钩。
  • 根据可预测的动作创建对象,例如在上'plugins_loaded'。不只是在您的插件被WordPress调用时。


@MikeSchinkel 相关想法,到目前为止尚未在实践中尝试过。
fuxia

有趣。我发现您的回答很好,但是您的最后结论很差。我认为,一般而言,类实例应在WordPress加载插件后立即实例化。然后,类实例的构造函数不应执行任何实际操作,而应添加操作和过滤器。这样,希望从类实例中删除操作和过滤器的插件可以确保在plugins_loaded调用时实际添加了它们,这正是plugins_loaded目的。当然,仍然可以通过单例模式访问类实例。
engelen 2014年

@engelen这是一个旧答案。现在,我将提供一个删除回调的操作。但不是Singleton,出于多种原因,这是一种反模式
fuxia

此答案也适用于删除操作,例如remove_action()
Nick Pyett

0

我不确定,但是您可以尝试使用单例。
您必须将对象引用存储在类的静态属性中,然后从静态方法返回该静态变量。像这样:

class MyClass{
    private static $ref;
    function MyClass(){
        $ref = &$this;
    }
    public static function getReference(){
        return self::$ref;
    }
}

0

只要您知道对象(并且使用的是PHP 5.2或更高版本-当前稳定的PHP版本是5.5,仍支持5.4,5.3终止),您可以使用remove_filter()方法将其删除。您只需要记住对象,方法名称和优先级(如果使用):

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

但是,您在代码中确实犯了一些错误。不要在PHP 4(!)$this中用&前缀作为前缀,&并且早就应该这样做了。这会使处理您的钩子成为问题,因此请不要理会它:

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

就是这样。


1
您无权$this从外部访问(另一个插件/主题)。
fuxia
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.