Удаление хуков в WordPress (событий или фильтров)
Чтобы удалить хук, нужно изменить исходный код и удалить вызов хука, обычно это сделать невозможно, потому что хуки находятся в ядре или в плагине. Здесь мы поговорим о том, как можно удалить прикрепленные к хуку коллбэк-функции.
Для удаления функции привязанной к фильтру/событию, используется одна из функций:
Для справки. Это полностью одинаковые функции и обе они удаляют хук, а фильтр это или событие, в данном случае, значения не имеет. Так например, событии и фильтр мы можем удалить используя только функцию remove_filter() или только remove_action(). Но для понятности кода, все же лучше фильтры удалять с помощью remove_filter(), а события с помощью remove_action().
Для удаления хука нужно знать:
- Название хука, например
wp_footer
; - Название прикрепленной функции, например
my_action_function
из примера выше; -
Приоритет выполнения хука, если он был установлен при создании хука. Если при создании приоритет не был установлен, то он равен 10 и при удалении его указывать не обязательно.
Если нужно удалить хук с приоритетом отличным от 10 и вы его не указали, то хук НЕ будет удален!
Пример удаления хука
Допустим где-то в плагине, к событию wp_footer
прикреплена функция my_action_function
, которая выводит текст в подвале темы:
add_action( 'wp_footer', 'my_action_function' ); function my_action_function( $text ){ echo 'Это текст в подвале!'; }
В теме нам нужно удалить это событие, чтобы текст в подвале не выводился. Для этого в файле темы functions.php можно прописать такой код:
remove_action( 'wp_footer', 'my_action_function' );
Важный момент: удалять хук нужно после того как он добавлен, но еще не отработал.
Еще пример удаления хука
Демонстрация добавления и удаления хука:
// добавляем функцию к событию my_action add_action( 'my_action', 'my_action_function' ); function my_action_function( $text ){ echo 'Привет!'; } // создаем событие do_action( 'my_action' ); //> Привет! // удаляем добавленное ранее событие remove_action( 'my_action', 'my_action_function' ); // создаем событие еще раз do_action( 'my_action' ); // ничего не выведет...
Удаление с учетом приоритета:
Приоритет должен совпадать с тем который был задан при добавлении хука.
// если так добавлен add_filter( 'my_filter', 'function_name', 99 ); // то так нужно удалять remove_filter( 'my_filter', 'function_name', 99 );
Удаление хука статического метода класса:
// так добавлен add_filter( 'my_filter', [ 'My_Class', 'static_method_name' ], 15 ); // так нужно удалять remove_filter( 'my_filter', [ 'My_Class', 'static_method_name' ], 15 );
Удаление хука не статического метода класса (если ЕСТЬ доступ к $this - экземпляру класса):
class A { function __construct(){ add_action( 'my_action', [ $this, 'method_name' ], 15 ); } function method_name(){ echo 'Привет!'; } } $class = new A(); // экземпляр // для удаления нужно найти переменную в которую был сохранен // экземпляр класса при создании, сейчас это $class remove_action( 'my_action', [ $class, 'method_name' ], 15 );
Удаление хука не статического метода класса (если НЕТ доступа к $this - экземпляру класса):
Удалить хук для объекта класса к которому у нет доступа, стандартными функциями WP невозможно. Но сделать это можно, используя следующие кастомные функции:
/** * Remove filter without access to class object (instance). * * In order to use the core WordPress remove_filter() on a filter added with the callback * to a class, you either have to have access to that class object, or it has to be a call * to a static method. This function allows you to remove filters with a callback to a class * you don't have access to. * * @param string $hook_name Filter to remove. * @param string|array $class_method_name Class and Method for the filter's callback. * Eg: [ '\Space\My_Class', 'my_method' ] OR '\Space\My_Class::my_method'. * @param int $priority Priority of the filter (default 10). * * @return bool Whether the hook is removed. * * @requires WP 4.7+ * @author Kama (wp-kama.com) * @version 1.0 */ function remove_object_filter( string $hook_name, $class_method_name, $priority = 10 ): bool { global $wp_filter; if( empty( $wp_filter[ $hook_name ]->callbacks[ $priority ] ) ){ return false; } $wp_hooks = & $wp_filter[ $hook_name ]; $hooks = $wp_hooks->callbacks[ $priority ]; [ $class_name, $method_name ] = is_string( $class_method_name ) ? explode( '::', $class_method_name ) + [ '', '' ] // '\Space\My_Class::my_method' : $class_method_name; $class_name = ltrim( $class_name, '\\' ); //> \Space\My_Class >>> Space\My_Class foreach( $hooks as $hook ){ if( ! isset( $hook['function'] ) || ! is_array( $hook['function'] ) ){ continue; } [ $object, $current_method ] = $hook['function']; if( $current_method !== $method_name ){ continue; } $is_our_object = is_object( $object ) && get_class( $object ) === $class_name; if( ! $is_our_object ){ continue; } return $wp_hooks->remove_filter( $hook_name, $hook['function'], $priority ); } return false; } /** * Remove Class Action Without Access to Class Object * * @param string $hook_name Action to remove. * @param string|array $class_method_name Class and Method for the filter's callback. * Eg: [ '\Space\My_Class', 'my_method' ] OR '\Space\My_Class::my_method'. * @param int $priority Priority of the action (default 10) * * @return bool Whether the hook is removed. */ function remove_object_action( $hook_name, $class_method_name, $priority = 10 ): bool { return remove_object_filter( $hook_name, $class_method_name, $priority ); }
Пример использования.
Допустим у нас есть такой класс:
namespace My\Space; class MyClass { public function __construct() { add_action( 'some_action_hook', [ $this, 'my_method' ], 11 ); add_filter( 'some_filter_hook', [ $this, 'my_method' ], 11 ); } public function my_method() { die( 'my_method trigered: '. current_filter() ); } }
Мы инициализировали класс, в результате чего добавилось 2 хука:
new \My\Space\MyClass();
Теперь, удалить хуки этого класса, через стандартные функции WP будет невозможно, потому что для удаления хука, нам нужно иметь доступ к конкретному экземпляру класса, а его у нас нет. Но благодаря функциям выше, мы можем это сделать, просто указав название класса, метода и приоритет.
Рассмотрим все варианты как можно вызвать функцию, чтобы удалить хук:
remove_object_action( 'some_action_hook', [ 'My\Space\MyClass', 'my_method' ], 11 ); remove_object_action( 'some_action_hook', [ '\My\Space\MyClass', 'my_method' ], 11 ); remove_object_action( 'some_action_hook', [ '\\My\\Space\\MyClass', 'my_method' ], 11 ); remove_object_action( 'some_action_hook', [ \My\Space\MyClass::class, 'my_method' ], 11 ); remove_object_action( 'some_action_hook', '\My\Space\MyClass::my_method', 11 ); remove_object_action( 'some_action_hook', 'My\Space\MyClass::my_method', 11 ); // или аналогично для фильтров remove_object_filter( 'some_filter_hook', [ 'My\Space\MyClass', 'my_method' ], 11 ); // etc...
Удаление хука добавленного анонимной функцией (closure)
Удалить хук с замыканием не так просто, например такой код не сработает:
add_action( 'my_action', function(){ echo 'Привет!'; } ); remove_action( 'my_action', function(){ echo 'Привет!'; } ); // не работает!
Надежно удалить хук с использованием замыкания, можно только если замыкание создавалось в переменную и у нас есть доступ к этой переменной:
$my_func = function(){ echo 'Привет!'; }; add_action( 'my_action', $my_func ); remove_action( 'my_action', $my_func ); // Работает!
НЕ надежный, но все же способ удалить хук с замыканием, когда у нас нет доступа к замыканию:
/** * Removes the hook when it has been added by a closure. * The accuracy of the function is not guaranteed - the first hook * that matches the priority and the number of hook arguments will be removed. * * @param string $name * @param int $priority * @param int $accepted_args */ function remove_closure_hook( $name, $priority = 10, $accepted_args = 1 ): bool { global $wp_filter; if( empty( $wp_filter[ $name ]->callbacks[ $priority ] ) ){ return false; } $callbacks = & $wp_filter[ $name ]->callbacks[ $priority ]; // Find our hook. // It is not always possible to identify it unambiguously, but // at least we know that it was created with a closure // and we know it's priority and number of parameters. foreach( $callbacks as $key => $hook ){ if( ! ( $hook['function'] instanceof Closure ) ){ continue; } if( $hook['accepted_args'] !== $accepted_args ){ continue; } // remove unset( $callbacks[ $key ] ); // first suitable only return true; } return false; }
Пример использования:
add_action( 'my_action', function(){ echo 'Привет!'; } ); do_action( 'my_action' ); // Привет! remove_closure_hook( 'my_action', 10, 1 ); do_action( 'my_action' ); // (пусто)
—