Главная Новости

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

Опубликовано: 01.09.2018

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

Плагины WordPress PHP (хуки, фильтры, действия)

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



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

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


Как изменять WooCommerce. Эпизод #1. Введение: файлы, хуки, фильтры

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

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


Создание Wordpress Темы (Шаблона) - Урок 4 Фильтры и события (filters and actions) в wordpress

Простой пример хука-события (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

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

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

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

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

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

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 будут все функции прикрепленные к хуку, в порядке приоритета их вызова...

rss