remove_action или remove_filter с внешними классами?

В ситуации, когда плагин инкапсулировал свои методы в класс, а затем зарегистрировал фильтр или действие против одного из этих методов, как удалить действие или фильтр, если у вас больше нет доступа к экземпляру этого класса?

Например, предположим, что у вас есть плагин, который делает это:

класс MyClass {
    функция __construct () {
       add_action ("plugins_loaded", array ($ this, 'my_action'));
    }

    function my_action () {
       //Делаем вещи ...
    }
}

новый MyClass ();

Отметив, что у меня нет доступа к экземпляру, как мне отменить регистрацию класса? Это: remove_action ("plugins_loaded", array (MyClass, 'my_action'));, похоже, не правильный подход - по крайней мере, похоже, не работает в моем случае.

54 голоса | спросил Tom Auger 9 FriEurope/Moscow2011-12-09T21:54:35+04:00Europe/Moscow12bEurope/MoscowFri, 09 Dec 2011 21:54:35 +0400 2011, 21:54:35

5 ответов


14

Лучше всего здесь использовать статический класс. Следующий код должен быть инструктивным:

класс MyClass {
    функция __construct () {
        add_action ('wp_footer', array ($ this, 'my_action'));
    }
    function my_action () {
        print '<h1>' , __класс__ . '-'. __function__. '& Л; /h1 >';
    }
}
новый MyClass ();


класс MyStaticClass {
    public static function init () {
        add_action ('wp_footer', array (__class__, 'my_action'));
    }
    public static function my_action () {
        print '<h1>' , __класс__ . '-'. __function__. '& Л; /h1 >';
    }
}
MyStaticClass :: Init ();

function my_wp_footer () {
    print '<h1> my_wp_footer () </h1>';
}
add_action ('wp_footer', 'my_wp_footer');

функция mfields_test_remove_actions () {
    remove_action ('wp_footer', 'my_wp_footer');
    remove_action ('wp_footer', array ('MyClass', 'my_action'), 10);
    remove_action ('wp_footer', array ('MyStaticClass', 'my_action'), 10);
}
add_action ('wp_head', 'mfields_test_remove_actions');

Если вы запустите этот код из плагина, вы заметите, что метод StaticClass, а также функция будет удален из wp_footer.

ответил mfields 11 SunEurope/Moscow2011-12-11T01:25:25+04:00Europe/Moscow12bEurope/MoscowSun, 11 Dec 2011 01:25:25 +0400 2011, 01:25:25
73

Всякий раз, когда плагин создает новый MyClass ();, он должен назначать ему уникальную именованную переменную. Таким образом, доступен экземпляр класса.

Итак, если он выполнял $ myclass = new MyClass ();, то вы могли бы сделать это:

global $ myclass;
remove_action ('wp_footer', array ($ myclass, 'my_action'));

Это работает, потому что плагины включены в глобальное пространство имен, поэтому неявные объявления переменных в основной части плагина являются глобальными переменными.

Если плагин не сохраняет идентификатор нового класса где-то , то технически это ошибка. Одним из общих принципов объектно-ориентированного программирования является то, что объекты, на которые не ссылается какая-либо переменная где-либо, подлежат очистке или устранению.

В настоящее время PHP, в частности, не делает этого, как Java, потому что PHP представляет собой полуресурсную реализацию ООП. Переменные экземпляра - это просто строки с уникальными именами объектов в них, что-то вроде. Они работают только из-за того, как взаимодействие переменных с именем функции работает с оператором ->. Поэтому просто выполнение new class () действительно может работать отлично, просто глупо. :)

Итак, в нижней строке, никогда не делайте new class ();. Сделайте $ var = new class (); и сделайте этот $ var доступным каким-либо образом, чтобы другие биты ссылались на него.

Изменить: годы спустя

Одна вещь, которую я видел, что многие плагины делают, - это использовать что-то похожее на шаблон «Singleton». Они создают метод getInstance () для получения единственного экземпляра класса. Это, наверное, лучшее решение, которое я видел. Пример плагина:

class ExamplePlugin
{
    protected static $ instance = NULL;

    public static function getInstance () {
        NULL === self :: $ instance и self :: $ instance = new self;
        return self :: $ instance;
    }
}

При первом вызове getInstance () он запускает класс и сохраняет его указатель. Вы можете использовать это для совершения действий.

Одна из проблем заключается в том, что вы не можете использовать getInstance () внутри конструктора, если используете такую ​​вещь. Это связано с тем, что новый вызывает конструктор перед установкой экземпляра $, поэтому вызов getInstance () из конструктора приводит к бесконечному циклу и разбивает все.

Одним из способов решения этой проблемы является не использование конструктора (или, по крайней мере, не использование getInstance () внутри него), а явно наличие функции «init» в классе для настройки ваших действий и т. д. Вот так:

public static function init () {
    add_action ('wp_footer', array (ExamplePlugin :: getInstance (), 'my_action'));
}

Что-то вроде этого, в конце файла, после того, как класс был все определен и такой, создание экземпляра плагина становится таким простым, как это:

ExamplePlugin :: Init ();

Init начинает добавлять ваши действия, и при этом он вызывает getInstance (), который создает экземпляр класса и гарантирует, что только один из них существует. Если у вас нет функции init, вы должны сделать это, чтобы вместо этого создать экземпляр класса:

ExamplePlugin :: деЫпзЬапс ();

Чтобы устранить исходный вопрос, удаление этого действия с внешней стороны (ака, в другом плагине) может быть выполнено следующим образом:

remove_action ('wp_footer', array (ExamplePlugin :: getInstance (), 'my_action'));

Положите это на что-нибудь, связанное с крюком действия plugins_loaded, и оно отменит действие, подключенное исходным плагином.

ответил Otto 11 SunEurope/Moscow2011-12-11T03:39:30+04:00Europe/Moscow12bEurope/MoscowSun, 11 Dec 2011 03:39:30 +0400 2011, 03:39:30
10

2 небольших PHP-функций, позволяющих удалить фильтр /действие с помощью «анонимного» класса: https://github.com/herewithme/wp-filters-extras/

ответил herewithme 6 22012vEurope/Moscow11bEurope/MoscowTue, 06 Nov 2012 11:55:57 +0400 2012, 11:55:57
9

Вот подробная документация, которую я создал для удаления фильтров, когда у вас нет доступа к объекту класса (работает с WordPress 1.2+, включая 4.7 +):

https://gist.github.com/tripflex/c6518efc1753cf2392559866b4bd1a53

/**
 * Удалить фильтр класса без доступа к объекту класса
 *
 * Чтобы использовать основной WordPress remove_filter () в фильтре, добавленном с обратным вызовом
 * к классу вы либо должны иметь доступ к этому объекту класса, либо он должен быть вызовом
 * к статическому методу. Этот метод позволяет удалять фильтры с обратным вызовом класса
 * у вас нет доступа.
 *
 * Работает с WordPress 1.2+ (добавлена ​​поддержка 4.7+ 9-19-2016)
 * Обновлено 2-27-2017 для использования внутреннего удаления WordPress для 4.7+ (чтобы предотвратить вывод предупреждений PHP)
 *
 * @param string $ tag Фильтр для удаления
 * @param string $ class_name Имя класса для обратного вызова фильтра
 * @param string $ method_name Имя метода для обратного вызова фильтра
 * @param int $ priority Приоритет фильтра (по умолчанию 10)
 *
 * @return bool Удаляется ли функция.
 * /
function remove_class_filter ($ tag, $ class_name = '', $ method_name = '', $ priority = 10) {
    глобальный $ wp_filter;

    //Проверяем, действительно ли фильтр существует
    if (! isset ($ wp_filter [$ tag])) return FALSE;

    /**
     * Если filter config является объектом, значит, мы используем WordPress 4.7+, а config больше не
     * простой массив, скорее это объект, реализующий интерфейс ArrayAccess.
     *
     * Чтобы быть обратно совместимым, мы устанавливаем $ callbacks равным правильному массиву в качестве ссылки (так обновляется $ wp_filter)
     *
     * @see https://make.wordpress.org/core/2016/09/08/wp_hook-next-generation-actions-and-filters/
     * /
    if (is_object ($ wp_filter [$ tag]) & & isset ($ wp_filter [$ tag] -> обратные вызовы)) {
        //Создаем объект $ fob из тега фильтра, чтобы использовать ниже
        $ fob = $ wp_filter [$ tag];
        $ callbacks = & $ wp_filter [$ tag] -> обратные вызовы;
    } else {
        $ callbacks = & $ wp_filter [$ tag];
    }

    //Выход, если нет никаких обратных вызовов для указанного приоритета
    if (! isset ($ callbacks [$ priority]) || empty ($ callbacks [$ priority])) return FALSE;

    //Перебираем каждый фильтр для указанного приоритета, ищем наш класс & метод
    foreach ((array) $ callbacks [$ priority] как $ filter_id => $ filter) {

        //Фильтр всегда должен быть массивом - массивом ($ this, 'method'), если не перейти к следующему
        if (! isset ($ filter ['function']) ||! is_array ($ filter ['function'])) continue;

        //Если первое значение в массиве не является объектом, оно не может быть классом
        if (! is_object ($ filter ['function'] [0])) continue;

        //Метод не совпадает с тем, который мы ищем,
        if ($ filter ['function'] [1]! == $ method_name) continue;

        //Метод сопоставлен, теперь давайте проверим класс
        if (get_class ($ filter ['function'] [0]) === $ class_name) {

            //WordPress 4.7+ использует core remove_filter (), так как мы нашли объект класса
            if (isset ($ fob)) {
                //Обрабатывает фильтр удаления, переименование приоритетных ключей обратного вызова в середине итерации и т. Д.
                $ fob-> remove_filter ($ tag, $ filter ['function'], $ priority);

            } else {
                //Использование устаревшего процесса удаления (до 4.7)
                unset ($ callbacks [$ priority] [$ filter_id]);
                //и если он был единственным фильтром в этом приоритете, отключите этот приоритет
                if (empty ($ callbacks [$ priority])) {
                    unset ($ callbacks [$ priority]);
                }
                //и если это единственный фильтр для этого тега, установите тег в пустой массив
                if (empty ($ callbacks)) {
                    $ callbacks = array ();
                }
                //Удалить этот фильтр из merged_filters, который указывает, были ли отсортированы фильтры
                unset ($ GLOBALS ['merged_filters'] [$ tag]);
            }

            return TRUE;
        }
    }

    return FALSE;
}

/**
 * Удалить действие класса без доступа к объекту класса
 *
 * Чтобы использовать основной WordPress remove_action () для действия, добавленного с обратным вызовом
 * к классу вы либо должны иметь доступ к этому объекту класса, либо он должен быть вызовом
 * к статическому методу. Этот метод позволяет удалять действия с обратным вызовом класса
 * у вас нет доступа.
 *
 * Работает с WordPress 1.2+ (добавлена ​​поддержка 4.7+ 9-19-2016)
 *
 * @param string $ tag Действие для удаления
 * @param string $ class_name Имя класса для обратного вызова действия
 * @param string $ method_name Имя метода для обратного вызова действия
 * @param int $ priority Приоритет действия (по умолчанию 10)
 *
 * @return bool Удаляется ли функция.
 * /
function remove_class_action ($ tag, $ class_name = '', $ method_name ='', $ priority = 10) {
    remove_class_filter ($ tag, $ class_name, $ method_name, $ priority);
}
ответил sMyles 15 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 15 Sep 2016 22:58:52 +0300 2016, 22:58:52
2

Выше решения выглядят устаревшими, приходилось писать свои собственные ...

function remove_class_action ($ action, $ class, $ method) {
    глобальный $ wp_filter;
    if (isset ($ wp_filter [$ action])) {
        $ len = strlen (метод $);
        foreach ($ wp_filter [$ action] как $ pri => $ actions) {
            foreach ($ actions as $ name => $ def) {
                if (substr ($ name, - $ len) == $ method) {
                    if (is_array ($ def ['function'])) {
                        if (get_class ($ def ['function'] [0]) == $ class) {
                            if (is_object ($ wp_filter [$ action]) & & isset ($ wp_filter [$ action] -> обратные вызовы)) {
                                unset ($ wp_filter [$ action] -> обратные вызовы [$ pri] [$ name]);
                            } else {
                                unset ($ wp_filter [$ action] [$ pri] [$ name]);
                            }
                        }
                    }
                }
            }
        }
    }
}
ответил Digerkam 25 MonEurope/Moscow2017-12-25T12:53:20+03:00Europe/Moscow12bEurope/MoscowMon, 25 Dec 2017 12:53:20 +0300 2017, 12:53:20

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132