Làm cách nào để xóa bộ lọc là đối tượng ẩn danh?


62

Trong functions.phptệp của tôi, tôi muốn xóa bộ lọc bên dưới, nhưng tôi không chắc chắn cách thực hiện vì nó nằm trong một lớp. Nên remove_filter()trông như thế nào?

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

Nó ở dòng 88 ở đây .


Bạn nên xóa cái đó &khỏi cái của bạn &$this, đó là thứ 4 của PHP
Tom J Nowell

Câu trả lời:


79

Đó là một câu hỏi rất hay. Nó đi vào trái tim đen tối của API plugin và thực tiễn lập trình tốt nhất.

Đối với câu trả lời sau đây, tôi đã tạo một plugin đơn giản để minh họa vấn đề với mã dễ đọc.

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

Bây giờ chúng ta thấy điều này:

nhập mô tả hình ảnh ở đây

WordPress cần một tên cho bộ lọc. Chúng tôi đã không cung cấp một cái, vì vậy WordPress gọi _wp_filter_build_unique_id()và tạo một cái. Tên này không thể dự đoán được vì nó sử dụng spl_object_hash().

Nếu chúng ta chạy var_export()trên $GLOBALS['wp_filter'][ 'wp_footer' ]chúng ta sẽ nhận được một cái gì đó như thế này ngay bây giờ:

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

Để tìm và loại bỏ hành động xấu xa của chúng ta, chúng ta phải đi qua các bộ lọc liên quan cho hook (một hành động chỉ là một bộ lọc rất đơn giản), kiểm tra xem đó có phải là một mảng không và liệu đối tượng có phải là một thể hiện của lớp không. Sau đó, chúng tôi ưu tiên và loại bỏ bộ lọc, mà không bao giờ nhìn thấy định danh thực sự .

Được rồi, hãy đặt nó vào một chức năng:

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

Khi nào chúng ta gọi chức năng này? Không có cách nào để biết chắc chắn khi đối tượng ban đầu được tạo. Có lẽ đôi khi trước đây 'plugins_loaded'? Có lẽ sau này?

Chúng tôi chỉ sử dụng cùng một móc mà đối tượng được liên kết và nhảy vào rất sớm với mức độ ưu tiên 0. Đó là cách duy nhất để thực sự chắc chắn. Đây là cách chúng tôi sẽ loại bỏ phương thức 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'
    );
}

Kết quả:

nhập mô tả hình ảnh ở đây

Và điều đó sẽ loại bỏ hành động khỏi câu hỏi của bạn (không được kiểm tra):

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

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

Phần kết luận

  • Luôn luôn viết mã dự đoán. Đặt tên dễ đọc cho các bộ lọc và hành động của bạn. Làm cho nó dễ dàng để loại bỏ bất kỳ móc.
  • Tạo đối tượng của bạn trên một hành động dự đoán, ví dụ như trên 'plugins_loaded'. Không chỉ khi plugin của bạn được gọi bởi WordPress.


@MikeSchinkel Ý tưởng liên quan , cho đến nay vẫn chưa thử nó.
fuxia

Hấp dẫn. Tôi thấy câu trả lời của bạn rất tốt, nhưng kết luận cuối cùng của bạn khá kém. Theo tôi, nói chung, các thể hiện của lớp nên được khởi tạo ngay khi WordPress tải plugin. Sau đó, hàm tạo của cá thể lớp không nên thực hiện bất kỳ hành động thực tế nào, chỉ cần thêm các hành động và bộ lọc. Theo cách này, các plugin muốn loại bỏ các hành động và bộ lọc khỏi thể hiện lớp của bạn có thể chắc chắn rằng chúng thực sự được thêm vào khi plugins_loadedđược gọi, đó chính xác plugins_loadedlà những gì dành cho. Tất nhiên, thể hiện của lớp vẫn cần có thể truy cập được, có thể thông qua mẫu singleton.
engelen

@engelen Đây là một câu trả lời cũ. Ngày nay tôi sẽ đưa ra một hành động để loại bỏ các cuộc gọi lại. Nhưng không phải là Singleton, đó là một mô hình chống vì nhiều lý do.
fuxia

Câu trả lời này cũng hoạt động để loại bỏ các hành động, chẳng hạn nhưremove_action()
Nick Pyett

0

Tôi không chắc nhưng bạn có thể thử sử dụng một singleton.
Bạn phải lưu trữ tham chiếu đối tượng trong một thuộc tính tĩnh của lớp và sau đó trả về biến tĩnh đó từ một phương thức tĩnh. Một cái gì đó như thế này:

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

0

Miễn là bạn biết đối tượng (và bạn sử dụng PHP 5.2 trở lên - phiên bản PHP ổn định hiện tại là 5.5, 5.4 vẫn được hỗ trợ, 5.3 là hết hạn sử dụng), bạn có thể xóa nó bằng remove_filter()phương thức. Tất cả những gì bạn cần nhớ là đối tượng, tên phương thức và mức độ ưu tiên (nếu được sử dụng):

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

Tuy nhiên bạn làm một chút sai lầm trong mã của bạn. Không có tiền tố $thisvới dấu và &, điều cần thiết trong PHP 4 (!) Và nó đã quá hạn trong thời gian dài. Điều này có thể khiến việc xử lý các hook của bạn gặp vấn đề, vì vậy hãy bỏ qua nó:

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

Và đó là nó.


1
Bạn không có quyền truy cập $thistừ bên ngoài (một plugin / chủ đề khác).
fuxia
Licensed under cc by-sa 3.0 with attribution required.