匿名オブジェクトであるフィルターを削除する方法は?


62

私のfunctions.phpファイルでは、以下のフィルターを削除したいのですが、クラス内にあるため、どうすればよいかわかりません。どうremove_filter()見える?

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

ここの88行目にあります


あなたは削除する必要があり&、あなたから&$thisそれはPHP 4のことだ、
トム・J Nowell

回答:


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これは古い答えです。最近では、コールバックを削除するアクションを提供します。しかし、シングルトンではなく、それは多くの理由でアンチパターンです。
FUXIAの

この回答は、次のようなアクションの削除にもremove_action()
有効です。

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の
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.