Удаление хуков в 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 невозможно. Но сделать это можно, используя следующие кастомные функции:
GitHub<?php
/**
* Remove Class Action Without Access to Class Object
*
* @see remove_object_filter()
*/
function remove_object_action( string $hook_name, $static_callback, $priority = null ): bool {
return remove_object_filter( $hook_name, $static_callback, $priority );
}
/**
* Remove filters without access to class object (instance).
*
* To use remove_filter() function you need to have access to class instance,
* or the filter should be added using static method as hook callback.
* This function allows you to remove filters with callbacks you don't have access to.
*
* @param string $hook_name Filter name to remove.
* @param string|array $static_callback Hook callback where instance represented as class name.
* Eg: [ '\Space\My_Class', 'my_method' ] OR '\Space\My_Class' to remove all methods added to hook.
* @param int|null $priority (optional) Priority of the filter. If not set all hooks with any priority will be removed.
*
* @return bool Whether the hook is removed.
*
* @requires WP 4.7+
* @author Kama (wp-kama.com)
* @version 2.0
*/
function remove_object_filter( string $hook_name, $static_callback, $priority = null ): bool {
if( is_string( $static_callback ) ){
// '\Space\My_Class::my_method' or '\Space\My_Class'
$static_callback = explode( '::', $static_callback ) + [ '', '' ];
}
$found = _find_hook_callback_instances( $hook_name, $static_callback, $priority );
$res = 0;
foreach( $found as $item ){
$callback = [ $item['instance'], $item['method'] ];
$res += (int) remove_filter( $hook_name, $callback, $item['priority'] );
}
return (bool) $res;
}
/**
* Finds the instance of the object whose specified method is added for the specified hook.
*
* To use remove_filter() function you need to have access to class instance,
* or the filter should be added using static method as hook callback.
* This function allows you to find class instance that was used when the hook was added.
*
* @param string $hook_name Filter name.
* @param string|array $static_callback Hook callback where instance represented as class name.
* Eg: [ '\Space\My_Class', 'my_method' ]
* or [ '\Space\My_Class' ] to get all methods added for the hook.
* @param int|null $priority (optional) Priority of the filter.
*
* @return array{ instance: object|string, method:string, priority:int }[]
*
* @author Kama (wp-kama.com)
* @version 1.1
*/
function _find_hook_callback_instances( string $hook_name, array $static_callback, $priority = null ): array {
global $wp_filter;
/** @var \WP_Hook $wp_hook WP hooks. */
$wp_hook = $wp_filter[ $hook_name ] ?? null;
if( empty( $wp_hook->callbacks ) ){
return [];
}
$find_class_name = ltrim( $static_callback[0], '\\' ); //> \Space\My_Class >>> Space\My_Class
$find_method_name = $static_callback[1] ?? '';
$found = [];
foreach( $wp_hook->callbacks as $the_priority => $hooks_data ){
foreach( $hooks_data as $hook_data ){
$real_callback = $hook_data['function'] ?? null;
if( ! is_array( $real_callback ) ){
continue;
}
[ $object, $the_method_name ] = $real_callback;
$class_name = is_object( $object ) ? get_class( $object ) : $object;
if(
$class_name !== $find_class_name
|| ( $find_method_name && $the_method_name !== $find_method_name )
|| ( null !== $priority && $the_priority !== $priority )
){
continue;
}
$found[] = [
'instance' => $object,
'method' => $the_method_name,
'priority' => $the_priority,
];
}
}
return $found;
}
Пример использования.
Допустим у нас есть такой класс:
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...
Или если нужно удалить все хуки, независимо от приоритета, мы можем пропустить параметр $priority:
remove_object_action( 'some_action_hook', [ 'My\Space\MyClass', 'my_method' ] );
Удаление всех хуков, которые добавляет указанных класс
Этот пример показывает как удалить все добавленных хуки от определенного класса. Здесь не имеет значения какой метод класса добавлен к хуку, будут удалены все указанные хуки, указанного класса.
GitHub<?php
/**
* Remove current action or filter from plugin
*
* @param string $class_name Class name enqueueing the action.
* @param null $action_name Action name.
*/
function remove_plugin_action( $class_name, $action_name = null ) {
global $wp_filter;
if ( null === $action_name ) {
$action_name = current_action();
}
/** @var WP_Hook $hooks WP hooks. */
$hooks = $wp_filter[ $action_name ];
$callbacks = $hooks->callbacks;
foreach ( $callbacks as $priority => $actions ) {
foreach ( $actions as $action ) {
$function = $action['function'];
if ( is_array( $function ) ) {
$callback = $function[0];
if (
( is_object( $callback ) && get_class( $callback ) === $class_name ) ||
( is_string( $callback ) && $callback === $class_name )
) {
remove_action( $action_name, $function, $priority );
}
}
}
}
}
// Example of usage.
/**
* Enqueue Google maps scripts only on certain pages.
*/
function action_google_maps_script() {
if ( ! ( is_page_template( 'templ-contact.php' ) ) ) {
remove_plugin_action( 'PageSpeed_Optimization' );
}
}
add_action( 'wp_enqueue_scripts', 'action_google_maps_script', 0 );
Удаление хука добавленного анонимной функцией (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' ); // (пусто)—