Bagaimana cara menghapus filter yang merupakan objek anonim?

62

Dalam functions.phpfile saya, saya ingin menghapus filter di bawah ini, tetapi saya tidak yakin bagaimana melakukannya karena ini ada di kelas. Seperti apa seharusnya remove_filter()?

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

Ada di saluran 88 di sini .

Jonas
sumber
Anda harus menghapus &dari Anda &$this, itu adalah hal PHP 4
Tom J Nowell

Jawaban:

79

Itu pertanyaan yang sangat bagus. Itu turun ke jantung gelap dari plugin API dan praktik pemrograman terbaik.

Untuk jawaban berikut ini saya membuat plugin sederhana untuk menggambarkan masalah dengan kode yang mudah dibaca.

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

Sekarang kita melihat ini:

masukkan deskripsi gambar di sini

WordPress membutuhkan nama untuk filter. Kami tidak menyediakan satu, jadi WordPress memanggil _wp_filter_build_unique_id()dan membuat satu. Nama ini tidak dapat diprediksi karena digunakan spl_object_hash().

Jika kita menjalankan var_export()pada, $GLOBALS['wp_filter'][ 'wp_footer' ]kita mendapatkan sesuatu seperti ini sekarang:

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

Untuk menemukan dan menghapus tindakan jahat kita, kita harus melalui filter terkait untuk kait (tindakan hanyalah filter yang sangat sederhana), periksa apakah itu adalah array dan jika objek adalah turunan dari kelas. Lalu kami mengambil prioritas dan menghapus filter, tanpa pernah melihat pengenal yang sebenarnya .

Oke, mari kita fungsi itu:

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

Kapan kita memanggil fungsi ini? Tidak ada cara untuk mengetahui dengan pasti kapan objek asli dibuat. Mungkin terkadang sebelumnya 'plugins_loaded'? Mungkin nanti?

Kami menggunakan kait yang sama dengan objek yang dikaitkan dan melompat di awal dengan prioritas 0. Itulah satu-satunya cara untuk benar-benar yakin. Inilah cara kami menghapus metode 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'
    );
}

Hasil:

masukkan deskripsi gambar di sini

Dan itu akan menghapus tindakan dari pertanyaan Anda (tidak diuji):

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

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

Kesimpulan

  • Selalu tulis kode yang dapat diprediksi. Tetapkan nama yang dapat dibaca untuk filter dan tindakan Anda. Buat mudah untuk menghapus kait apa pun.
  • Buat objek Anda pada tindakan yang dapat diprediksi, misalnya pada 'plugins_loaded'. Bukan hanya ketika plugin Anda dipanggil oleh WordPress.
fuxia
sumber
FYI
MikeSchinkel
@MikeSchinkel Ide terkait , belum mencobanya dalam praktik sejauh ini.
fuxia
Menarik. Saya menemukan jawaban Anda sangat bagus, tetapi kesimpulan terakhir Anda cukup buruk. Menurut pendapat saya, instance kelas harus, secara umum, akan dipakai segera setelah WordPress memuat plugin. Kemudian, konstruktor instance kelas tidak boleh melakukan tindakan nyata, cukup tambahkan tindakan dan filter. Dengan cara ini, plugin yang ingin menghapus tindakan dan filter dari instance kelas Anda dapat memastikan bahwa mereka benar-benar ditambahkan ketika plugins_loadeddipanggil, yang memang untuk apa plugins_loaded. Tentu saja, instance kelas masih perlu diakses, mungkin melalui pola singleton.
engelen
@engelen Ini adalah jawaban lama. Saat ini saya akan menawarkan tindakan untuk menghapus callback. Tapi bukan Singleton, itu anti-pola karena berbagai alasan.
fuxia
Jawaban ini juga berfungsi untuk menghilangkan tindakan, sepertiremove_action()
Nick Pyett
0

Saya tidak yakin tetapi Anda dapat mencoba menggunakan singleton.
Anda harus menyimpan referensi objek di properti statis kelas Anda dan kemudian mengembalikan variabel statis dari metode statis. Sesuatu seperti ini:

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

Selama Anda tahu objek (dan Anda menggunakan PHP 5.2 atau lebih tinggi - versi PHP stabil saat ini adalah 5.5, 5.4 masih didukung, 5.3 adalah akhir hidup), Anda bisa menghapusnya dengan remove_filter()metode ini. Yang perlu Anda ingat adalah objek, nama-metode, dan prioritas (jika digunakan):

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

Namun Anda melakukan sedikit kesalahan dalam kode Anda. Jangan awali $thisdengan ampersand &, yang diperlukan di PHP 4 (!) Dan sudah lama ditunggu. Ini bisa membuat berurusan dengan kait Anda bermasalah, jadi biarkan saja:

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

Dan itu saja.

hakre
sumber
1
Anda tidak memiliki akses $thisdari luar (plugin / tema lain).
fuxia