WordPress как на ладони
WordPress темы и плагины за 250 рублей wordpress jino

Как работают хуки в WordPress (фильтры и события)

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

Хуками в WordPress называются фильтры (filter) и события (action). В программной части это абсолютно одно и тоже, т.е. обрабатывается и то и другое одинаково, можно например заменять функции add_filter() и add_action() — все будет работать! Разделение нужно, потому что по смыслу это разные вещи...

Фильтры нужны, чтобы отфильтровать передаваемое значение, т.е. фильтр получает значение и обязательно должен его вернуть (изменённое или нет). А события — это события, они просто срабатывают в определенный момент и ничего не возвращают.

К хукам (фильтрам или событиям) прикрепляются PHP функции, затем, в момент срабатывания хука, эти функции будут срабатывать.

В функцию прикрепленную к хуку могут передаваться дополнительные данные (параметры). На основе этих данных в функции можно создавать какую-либо логику - что-то проверять перед тем, как что-то сделать.

Простой пример хука-события (action):

// создадим функцию для хука - события
function echo_1(){ echo 1; }

// привяжем функцию к хуку
add_action( 'my_hook', 'echo_1' );

// выполним хук
do_action('my_hook'); //> выведет 1

С передачей параметра:

// создадим функции для событий
function echo_1($data){ echo $data[0]; }
function echo_2($data){ echo $data[1]; }

// привяжем функции к хуку
add_action( 'my_hook', 'echo_1' );
add_action( 'my_hook', 'echo_2' );

// выполним хук
do_action('my_hook', array('Привет', ' мир!') ); //> выведет "Привет мир!"

Список фильтров смотрите на странице «Все хуки WordPress». Всего в WordPress их около 2000. Некоторые важные хуки я описал и продолжаю описывать, они находятся тут.

Как работают фильтры в WordPress

Для работы фильтра используются две функции:

  1. add_filter() — добавляет/прикрепляет PHP функции к указанному фильтру.

  2. apply_filters() — вызывается там, где применяется фильтр. Запускает прикрепленные к фильтру PHP функции.

apply_filters() должна вызываться после add_filter(), т.е. после того, как к фильтру добавлена функция, что логично...

Используется до того как фильтр будет вызван с помощью apply_filters(), потому что во время срабатывания фильтра, все PHP функции должны уже быть подключены к фильтру, чтобы все эти функции обработали переданное значение (отфильтровали его).

Пример

// Прикрепим функцию к фильтру.
// Это обычно делается в другом файле, но обязательно до вызова фильтра...
add_filter('my_filter', 'my_filter_function');
function my_filter_function( $str ){
	$str = 'Здравствуйте '. $str;
	return $str;
}

// Вызов фильтра
$str = 'Владимир';
$str = apply_filters('my_filter', $str );
echo $str; //> Здравствуйте Владимир

Еще пример

Допустим, у нас есть функция text(), которая возвращает текст. Нам нужно сделать так, чтобы можно было изменять этот текст за пределами этой функции. Для этого придумаем название нашему фильтру, пусть: my_filter_name и применим его так:

// Функция, в которой применяется фильтр 'my_filter_name'
function text( $text ){
	// обрабатываем переданный текст - удалим html теги
	$text = strip_tags( $text );

	// теперь, возвращаем текст через фильтр.
	// Если к фильтру не прикреплена ни одна функция, то текст просто 
	// вернется как есть, т.е. строка ниже будет эквивалентна "return $text;"
	return apply_filters('my_filter_name', $text );
}

// Создадим функцию для фильтра
function my_filter_function( $text ){
	// обрежем текст до 30 знаков и вернем его
	return mb_substr( $text, 0, 30 ) .'...';
}
// Прикрепим функцию к фильтру 'my_filter_name'
add_filter('my_filter_name', 'my_filter_function');

// Внимание, сейчас вылетит птичка :)

// Теперь, при вызове text(), функция удалит из текста html теги — это сделает она сама с помощью strip_tags(). 
// А дальше обрежет текст — это сделает фильтр, с помощью функции my_filter_function()
echo text( 'Lorem <b>Ipsum</b> is simply dummy text of the printing and typesetting industry.' );
// выведет: Lorem Ipsum is simply dummy te...

Фильтров в WordPress много и вы наверняка с ними встречались в темах и плагинах. Одни из самых популярных это: the_content, body_class, sanitize_user, comment_form и т.д.

Как работают события в WordPress

Для работы события используются две функции:

  1. add_action() — добавляет/прикрепляет функции к событию, которое вызывается с помощью do_action(). Функция должна прикрепляться к событию до того, как событие произойдет. Нужно это, чтобы во время срабатывания события PHP функции уже были прикреплены к событию.

  2. do_action() — это и есть событие. Запускает/вызывает добавленные к событию функции. Вызывается там, где должно сработать событие.

do_action() должна вызываться после add_action(), т.е. после того, как к событию добавлена функция, что логично...

Пример

Допустим, мы делаем тему и в каком-то месте темы нам нужно вызвать функцию, но мы заранее не знаем как функция будет называться. Т.е. пользователи сами будут добавлять функцию в это место.

Для этого в этом месте шаблона мы может вызвать не функцию а событие, назовем его my_action и затем прикрепить к этому событию функцию:

// создадим функцию для события
function my_action_function( $text ){
	echo 'Событие "my_action" сработало сейчас.';
}
// Прикрепим функцию к событию 'my_action'. Это можно сделать в другом файле
// главное до того как событие сработает.
add_action('my_action', 'my_action_function');

// Вызов самого события. Вставляем эту строку в то место темы где нужно,
// чтобы сработала прикрепленная к нему функция
do_action('my_action');

С событиями вы тоже встречались их много и одни из самых популярных это: wp_head, wp_footer.

Приоритеты выполнения функций

Если к хуку привязано несколько функций, то они будут выполняться в порядке их прикрепления к хуку:

// привяжем функции к хуку
add_action('my_hook', function(){ echo 1; } );
add_action('my_hook', function(){ echo 2; } );
add_action('my_hook', function(){ echo 3; } );

// выполним хук
do_action('my_hook'); // выведет 123

Но, если нужно изменить порядок, то мы может указать приоритет в третьем параметре add_action() или add_filter():

// привяжем функции к хуку
add_action('my_hook', function(){ echo 1; }, 15 );
add_action('my_hook', function(){ echo 2; }, 10 ); // можно не указывать 10 - по умолчанию
add_action('my_hook', function(){ echo 3; }, 5 );

// выполним хук
do_action('my_hook'); // выведет 321

Как видно, чем больше число приоритета, тем позднее выполниться хук.

Если приоритет не указан, как в первом примере, то он равен 10.

Для фильтров приоритеты работают точно также.

Передача дополнительных параметров

По умолчанию, каждому фильтру указывается параметр и он передается в прикрепляемую функцию. Это видно на примерах выше.

Однако есть возможность передать в функцию хука дополнительные параметры. Для этого в функциях хука, можно указать сколько угодно параметров через запятую. Например:

$str = apply_filters('my_filter', $str, $data1, $data2 );

Теперь, при прикреплении функции. Мы можем использовать дополнительные параметры. Для этого нужно указать количество передаваемых параметров:

add_filter('my_filter', 'my_filter_function', 10, 3 ); // 3 - кол. параметров

Тут, 10 - приоритет, а 3 - количество параметров, которые получит функция my_filter_function(). Т.е. функция фильтра будет такая:

function my_filter_function( $str, $data1, $data2 ){
	// фильтруем $str. 
	// Можем использовать $data1, $data2
	return $str;
}

У событий все работает аналогично:

// прикрепляем функцию
function my_action_function( $data1, $data2, $data3 ){
	// делаем что либо во время события
	// Можем использовать $data1, $data2, $data3 
}
add_filter('my_action', 'my_action_function', 10, 3 );

// вызываем событие
do_action('my_action', $data1, $data2, $data3 );

Как удалить хук: фильтр или событие

Для удаления функции привязанной к фильтру или событию, используется одна из функций: remove_action() и remove_filter().

Для справки. Это полностью одинаковые функции и обе они удаляют хук, а фильтр это или событие, в данном случае, значения не имеет. Так, и события и фильтры мы можем удалять используя только функцию 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');

Важный момент: код плагина подключается до подключения файла темы functions.php. А значит, в момент удаления хука: remove_action(), событие уже добавлено в общий массив событий и мы можем его от туда удалить.

Еще пример удаления хука

Демонстрация добавления и удаления хука в одном коде:

// добавляем функцию к событию 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'); // ничего не выведет...

Примеры удаления хуков с приоритетами и добавленных через PHP класс

// Пример -----------------------------
// Простое удаление
add_filter('my_filter', 'function_name');    // так добавлен
remove_filter('my_filter', 'function_name'); // так нужно удалять

// Пример -----------------------------
// Удаление с приоритетом
add_filter('my_filter', 'function_name', 99);    // так добавлен
remove_filter('my_filter', 'function_name', 99); // так нужно удалять - приоритет должен совпадать...

// Пример -----------------------------
// Удаление статического метода класса
add_filter('my_filter', array('My_Class', 'method_name'), 15 );   // так добавлен
remove_filter('my_filter', array('My_Class', 'method_name'), 15); // так нужно удалять - приоритет должен совпадать...

// Пример -----------------------------
// Удаление не статического метода класса, который добавлен с использованием $this
class A{
	function __construct(){
		add_action('my_action', array( & $this, 'method_name'), 15 );   // так добавлен
	}

	function method_name(){  echo 'Привет!';  }
}

$class = new A; // экземпляр

// для удаления нужно найти переменную в которую был сохранен экземпляр класса при создании, сейчас это $class
remove_action('my_action', array( $class, 'method_name'), 15 );   // так нужно удалять

// Пример -----------------------------
// Удалить хук добавленный лямбда-функцией (замыканием) невозможно!
add_action('my_action', function(){  echo 'Привет!';  } );   // так добавлен
// никак не удалить, даже так:
remove_action('my_action', function(){  echo 'Привет!';  });

Все функции хуков

Функция Описание
add_action Регистрирует хук-событие. При регистрации указывается PHP функция, которая сработает в момент события, которое вызывается с помощью do_action().
did_action Получает число, сколько раз было выполнено данное событие (хук).
do_action Создает событие (зацепку для произвольной функции). Чтобы функция сработала в момент события её нужно подключить к этому событию с помощью функции add_action().
do_action_ref_array Создает хук (действие) для зацепки функции. Аргументы передаются из массива.
has_action Проверяет была ли зарегистрирована функция для хука (действия).
remove_action Удаляет функцию прикрепленную к указанному хуку (событию).
   
add_filter Прикрепляет указанную PHP функцию к указанному хукe-фильтру. Так, во время срабатывания фильтра значение будет обработано указанной PHP функцией.
apply_filters Применяет прикрепленную к указанному фильтру PHP функцию. Прикрепляется функция с помощью add_filter().
apply_filters_ref_array Выполняет функции прикрепленные к указанному хуку (фильтру). Параметры передаются в массиве.
current_filter Получает название текущего действия или фильтра.
doing_filter Проверяет обрабатывается ли указанный хук (фильтр или событие), в текущий момент.
has_filter Проверяет была указана дли для фильтра какая-нибудь функция, т.е. имеется ли указанный хук.
remove_all_filters Удаляет все хуки у указанного фильтра.
remove_filter Удаляет указанную функцию прикрепленную к указанному фильтру.

Вспомогательные функции для фильтров

В WordPress есть специальные функции, которые упрощают работу с фильтрами.

Например, мы может отключить все стандартные виджеты WordPress с помощью фильтра load_default_widgets, так:

function is_load_default_widgets(){
	return false;
}
add_filter( 'load_default_widgets', 'is_load_default_widgets' );

Или можно не создавать отдельную функцию которая вернет false, а использовать уже готовую функцию из ядра WordPress: __return_false():

add_filter( 'load_default_widgets', '__return_false' );

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

function wp_maybe_load_widgets() {
	if ( ! apply_filters( 'load_default_widgets', true ) )
		return;

	require_once( ABSPATH . WPINC . '/default-widgets.php' );

	add_action( '_admin_menu', 'wp_widgets_add_menu' );
}

При вызове функции wp_maybe_load_widgets() срабатывает фильтр. По умолчанию он всегда возвращает true и условие не выполняется — виджет подключаются. А в примерах выше мы возвращаем false и условие выполняется — виджеты не подключаются.

Это демонстративный пример из кода WordPress, чтобы объяснить принцип работы. В практике его лучше не использовать, для отключения виджетов используйте код из описания функции unregister_widget().

Аналогичный пример

Аналогичным образом можно выключить возможность публиковать записи по протоколу xmlrpc. Которой, кстати по умолчанию включен, но им мало кто пользуется.

// отключим публикацию по xmlrpc
add_filter( 'xmlrpc_enabled', '__return_false' );

Еще один пример

Закроем возможность сброса пароля, с помощью фильтра allow_password_reset:

add_filter( 'allow_password_reset', '__return_false' );

Теперь все пользователи не смогут сбрасывать пароли на сайте.

Кроме __return_false(), есть и другие вспомогательные функции, которые просто возвращают готовое значение. Вот их список:

Получение списка всех хуков WordPress

Уж не знаю зачем, но такое может понадобится. В процессе разработки или при поиске ошибок...

Как я упоминал выше все хуки записываются в общий массив, а точнее в глобальную переменную $wp_filters. К слову, если удалить из $wp_filters элемент массива (хук), то хук перестанет работать...

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

## Выводит на экран список всех хуков WordPress и функций к ним прикрепленных. 
## @param строка $hook_name Название хука список фукнций которого нужно вывести.
## @ver 2.0
function hooks_list( $hook_name = '' ){
	global $wp_filter;
	$wp_hooks = $wp_filter;

	// для версии 4.4 - переделаем в массив
	if( is_object( reset($wp_hooks) ) ){
		foreach( $wp_hooks as & $object ) $object = $object->callbacks;
		unset($object);
	}

	if( $hook_name ){
		$hooks[ $hook_name ] = $wp_hooks[ $hook_name ];

		if( ! is_array($hooks[$hook_name]) ){
			trigger_error( "Nothing found for '$hook_name' hook", E_USER_WARNING );
			return;
		}
	}
	else {
		$hooks = $wp_hooks;
		ksort( $wp_hooks );
	}

	$out = '';
	foreach( $hooks as $name => $funcs_data ){
		ksort( $funcs_data );
		$out .= "\nхук\t<b>$name</b>\n";
		foreach( $funcs_data as $priority => $functions ){
			$out .= "$priority";
			foreach( array_keys($functions) as $func_name ) $out .= "\t$func_name\n";
		}
	}

	echo '<'.'pre>'. $out .'</pre'.'>';
}

hooks_list();

/* Получим список такого формата:

хук wp_enqueue_scripts
10  lambda_2
99  theme_scripts_styles

хук wp_footer
0   0000000072b7ec0d00002b862c2211a0enqueue_jquery_if_need
10  0000000072b7edb400002b862c2211a0footer_scripts
	0000000072b7eb7b00002b862c2211a0main_js
20  wp_print_footer_scripts
99  0000000072b7ec0d00002b862c2211a0add_script_to_footer
	0000000072b7eb1600002b862c208180
	question_close_ajax_js
	add_question_rating_ajax_js
	add_comment_rating_ajax_js
999 0000000072b7edb600002b862c2211a0footer_script
1000    wp_admin_bar_render
9999999999  0000000072b7eb0b00002b862c208180

... и т.д. ...
*/

А так выглядит элемент массива в самой переменной $wp_filter:

global $wp_filter;

print_r( $wp_filter );

/*
	[wp_title] => WP_Hook Object
		(
			[callbacks] => Array
				(
					[10] => Array
						(
							[wptexturize] => Array
								(
									[function] => wptexturize
									[accepted_args] => 1
								)

							[convert_chars] => Array
								(
									[function] => convert_chars
									[accepted_args] => 1
								)

							[esc_html] => Array
								(
									[function] => esc_html
									[accepted_args] => 1
								)

						)

					[11] => Array
						(
							[capital_P_dangit] => Array
								(
									[function] => capital_P_dangit
									[accepted_args] => 1
								)

						)

				)

			[iterations:WP_Hook:private] => Array
				(
				)

			[current_priority:WP_Hook:private] => Array
				(
				)

			[nesting_level:WP_Hook:private] => 0
			[doing_action:WP_Hook:private] => 
		)

	[widget_title] => WP_Hook Object
		(
		............... и т.д.
*/

Как получить функции прикрепленные к указанному хуку?

Часто бывает нужно посмотреть, какие функции прикреплены к отдельному фильтру или событию.

Код выше позволит это сделать, для этого нужно указать в нем параметр $hook_name:

hooks_list( 'the_title' );

/* Получим:

хук the_title
10  wptexturize
	convert_chars
	trim
	func_title_add_brackets
11  capital_P_dangit

*/

Другой вариант - это просто получить данные массива

Используем глобальную переменную $wp_filters. В ней хранятся данные всех хуков.

Данные в $wp_filters добавляются по ходу обработки кода, поэтому имеет большое значение когда используется переменная $wp_filters: например, если её использовать в начале файла файла темы functions.php, то в ней будут все функции хуков который были добавлены до подключения этого файла, но не будет добавленных в этом файле или позднее.

/**
 * Выводит список функций прикрепленных к указанному хуку
 * @param (str) $hook Название хука, функции которого нужно вывести.
 */
function print_filters_for( $hook ){
	global $wp_filter;

	$data = isset( $wp_filter[$hook] ) ? $wp_filter[$hook] : "Хук `$hook` не найден...";

	echo '<pre>', print_r( $data, 1 ) .'</pre>';
}

Теперь, чтобы вывести список всех функций/методов прикрепленных к фильтру или событию нужно вызвать эту функцию, и указать ей название хука.

Например, получим все функции добавленные к событию wp_footer:

print_filters_for('wp_footer');

/* Выведет:

WP_Hook Object (

	[callbacks] => Array (

		[0] => Array (
			[echo_ads_tpls] => Array (
				[function] => echo_ads_tpls
				[accepted_args] => 1
			)
		)

		[20] => Array (
			[wp_print_footer_scripts] => Array (
				[function] => wp_print_footer_scripts
				[accepted_args] => 1
			)
		)

		[99] => Array (
			[question_close_ajax_js] => Array (
				[function] => question_close_ajax_js
				[accepted_args] => 1
			)

			[add_question_rating_ajax_js] => Array (
				[function] => add_question_rating_ajax_js
				[accepted_args] => 1
			)

			[add_comment_rating_ajax_js] => Array (
				[function] => add_comment_rating_ajax_js
				[accepted_args] => 1
			)

			[textarea_tab_on_tab_click] => Array (
				[function] => textarea_tab_on_tab_click
				[accepted_args] => 1
			)

			[add_to_bookmark_js] => Array (
				[function] => add_to_bookmark_js
				[accepted_args] => 1
			)
		)

		[1000] => Array (
			[wp_admin_bar_render] => Array (
				[function] => wp_admin_bar_render
				[accepted_args] => 1
			)
		)
	)

	[iterations:WP_Hook:private] => Array()
	[current_priority:WP_Hook:private] => Array()
	[nesting_level:WP_Hook:private] => 0
	[doing_action:WP_Hook:private] =>
)
*/

Где в индексе callbacks будут все функции прикрепленные к хуку, в порядке приоритета их вызова...

31 коммент
Полезные 3 Вопросы 1 Все
  • Пример удаления хука
    Допустим где-то в коде, например, в плагине, к событию wp_footer прикреплена функция my_action_function, которая выводит текст в подвале темы:

    add_action('wp_footer', 'my_action_function');
    function my_action_function( $text ){
    	echo 'Это текст в подвале!';
    }

    Теперь, например, в теме нам нужно удалить это событие, чтобы текст в подвале не выводился. Для этого в functions.php темы можно прописать такой код:

    remove_action('my_action', 'my_action_function');

    Исправьте:

    remove_action('my_action', 'my_action_function');

    на

    remove_action('wp_footer', 'my_action_function');

    Да и вообще было бы не плохо, увидеть у вас на сайте "предложить исправление ошибки". Выделил кусок текста, нажал сочетание клавиш, оставил комментарий, и все - а вы уже по возможности исправите.

    1
    Ответить15 дней назад #
    • @ campusboy2801 cайт: www.youtube.com/c/wpplus

      Исправьте

      Спасибо за замечание, исправлено.

      не плохо, увидеть у вас на сайте "предложить исправление ошибки"

      Предложение хорошее, посмотрим, что ответит Тимур на это.

      1
      Ответить15 дней назад #
    • Kama5395

      Да, надо сделать что-то такое, подумаю об этом, спасибо! thank_you Мне это видится вот также через комментарии как-то сделать...

      Ответить15 дней назад #
Здравствуйте, !     Войти . Зарегистрироваться