Введение
WordPress — мощная платформа для разработки сайтов и веб-приложений. И, наверное, в 99% проектов необходимо модифицировать админку: добавить свои поля ввода, свои разделы, страницы, или скорректировать выводимые данные.
Но редактировать исходники админки нельзя. Поэтому разработчики внедрили в систему экшены (actions), хуки (hooks) и фильтры (filters).
Что такое экшены (actions) и хуки (hooks)
Экшен (action) — это место в коде, в которое можно прикрепить функцию при помощи хука.
Хук (hook) — это функция, которая прикрепляется к экшену.
Экшен обозначается функцией do_action(); В нем указывают следующие аргументы:
- $hook_name — название экшена.
- …$args — переменные, которые будут передаваться в хук (функцию). Их может быть бесконечное множество.
Простой пример
Допустим, у нас есть файл index.php, и в нем есть такой код:
<?php
get_header();
the_post();
do_action( "before_content_editor", $post );
get_footer();
В место, где указано do_action( "before_content_editor", $post );
мы можем прикрепить функцию (хук) PHP, не меняя исходники файла index.php. И эта функция будет исполняться каждый раз, когда будет срабатывать файл index.php.
Чтобы добавить хук, нужно использовать функцию add_action() в файле functions.php. У этой функции есть следующие параметры:
- $tag — название экшена, к которому мы хотим прикрепить функцию
- $callback — название функции, которая будет выполняться с этим экшеном
- $priority — приоритет выполнения функции. Это необязательно, но важный параметр, потому что на экшен можно добавить сколько угодно хуков и, как правило, нужно чтобы они исполнялись в строго определенном порядке
- $accepted_args — количество агрументов, которое принимает функция $callback. Если экшен не передает аргументы или он один — то можно не указывать.
Давайте добавим в экшен «before_content_editor» функцию, которая будет выводить секцию «О компании». Для этого в functions.php добавим такой код:
<?php
add_action(
"before_content_editor", //Название хука, к которому цепляемся
"feodoraxis_show_about", //Название функции, которая будет выполнена
10, //Приоритет. Если этому-же экшену добавлены функции с цифрой меньше - они будут выполнены раньше этого.
1 // Мы передали один дополнительный аргумент - объект $post
);
/**
* В функции указываем аргумент $post,
* потому что он передается в функции do_action() в index.php
**/
function feodoraxis_show_about( $post ) {
echo "<section class='about'>
<div class='row'>
<div class='col-md-6'>
<h2>О компании</h2>
<p>Некоторый текст о компании</p>
</div>
<div class='col-md-6'>
<img src='img/about.jpg'>
</div>
</div>
</section>";
}
Таким образом, не вмешиваясь в исходники файла index.php мы добавили работу простой функции.
Что такое фильтры (filters)
Фильтры (filter) — это место в коде, которое принимает какую-либо информацию (строки, числа, массивы — любые типы данных PHP), «пропускает» её через прикрепленные функции и возвращает её.
Задача фильтра в том, чтобы дать возможность менять и корректировать передаваемые данные, не меняя при этом исходный файл. Принцип такой-же, как у экшенов. Функции, которые прикрепляются к фильтрам, обязательно должны возвращать тот-же тип данных, которые возвращает фильтр, к которому мы прикрепили функцию.
Фильтр создается при помощи функции apply_filter(), которая имеет следующие параметры:
- $hook_name — название фильтра
- $value — значение, которое передается в фильтр и которое, после обработки всеми прикрепленными функциями, будет возвращено
- …$args — дополнительные параметры. Они не фильтруются, но могут использоваться в прикрепленных фильтрах. Можно передавать бесконечное множество.
Обычно фильтры имеют такой вид:
$content = apply_filters( "the_content", $content );
Чтобы прикрепить функцию к фильтру, нужно в functions.php использовать функцию add_filter();
Пример:
<?php
add_filter( "the_content", "feodoraxis_modify_content", 10 );
function feodoraxis_modify_content( $content ) {
$content = nl2br( $content );
$content = str_replace( "фрукты", "овощи", $content );
return $content;
}
Именованные экшены (actions)
Выше мы рассмотрели экшены, у которых есть строго определенное название и оно не меняется. Но в WP есть и хуки, часть названия которых может меняться. Вот некоторые из них:
- save_post_{$post_type} — экшен, который срабатывает когда запись определенного типа создаётся или обновляется. $post_type — это строка, в которой передается название типа записи. Например, если мы сохраняем простую страницу, то экшен будет называться так: save_post_page; Если сохраняем товар WooCommerce — то название будет save_post_product
- edit_post_{$post_type} — экшен, который срабатывает когда запись определенного типа обновляется. Смысл такой-же, как с save_post_{$post_type}.
- wp_ajax_{$action_name} — экшен, который срабатывает, когда авторизованный пользователь делает ajax-запрос. Например, если это отправка сообщения из формы обратной связи, то $action_name может иметь значение по типу recall_form. Тогда полное имя экшена будет wp_ajax_recall_form
- wp_ajax_nopriv_{$action_name} — экшен, который срабатывает, когда неавторизованный пользователь делает ajax-запрос. Отличие от wp_ajax только в том, это этот экшен будет срабатывать только для неавторизованных пользователей. В остальном принцип идентичен.
Подробнее про Ajax в WordPress я писал в этой статье.
Пример: обработка количества товаров на складе у товаров при сохранении записи товара. Если вдруг товаров будет меньше одной штуки — то автоматически поставим количество 10. Код указываем в functions.php
<?php
add_action( "save_post_" . "product", "feodoraxis_check_product_stock", 10, 2 );
/**
* @param int $post_ID
* @param WP_Post $post
**/
function feodoraxis_check_product_stock( $post_ID, $post ) {
$stock = get_post_meta( $post_ID, "feodoraxis_product_stock", true );
if ( $stock < 1 ) {
update_post_meta( $post_ID, "feodoraxis_product_stock", 10 );
}
}
Пример исключительно академический. Вряд-ли вам в реальности потребуется сделать что-то такое. Показал просто для наглядности 🙂
Хуки в админке WordPress
Допустим, у вас блог. И вы хотите вывести статистику просмотров выбранного поста сразу под редактором описания в админке. При этом важно, чтобы данные выводились только у постов. Предположим, что количество просмотров уже записывается в некое мета-поле.
Менять исходники нельзя. Поэтому нужно использовать хуки. Для решения конкретно этой задачи нам потребуется хук edit_form_after_editor. Он находится в файле /wp-admin/edit-form-advanced.php:649.
Чтобы вывести статистику, укажем такой код в functions.php:
<?php
add_action( "edit_form_after_editor", "feodoraxis_show_views", 10, 1 );
function feodoraxis_show_views( $post ) {
if ( get_post_type( $post->ID ) != 'post' ) {
return;
}
echo "Количество просмотров: " . get_post_meta( $post->ID, "feodoraxis_views", true );
}
Отлично! Но выводится не очень красиво — просто посреди серого фона админки. А хочется как-то оформить. Оформим вывод той-же информации по-красивей:
<?php
add_action( "edit_form_after_editor", "feodoraxis_show_views", 10, 1 );
function feodoraxis_show_views( $post ) {
if ( get_post_type( $post->ID ) != 'post' ) {
return;
}
?>
<div style="padding: 10px 15px; margin-top: 30px; color: #FFF; background: linear-gradient(to bottom right, #4358d0, #6443d0);">
<p>Количество просмотров: <?php echo get_post_meta( $post->ID, "feodoraxis_views", true ); ?></p>
</div>
<?php
}
В результате получится такая красивая штука 🙂
Хуки и фильтры в плагине на примере Contact Form 7
Подробно о Contact Form 7 я написал в этой статье
Тут смысл точно такой-же, как и в ядре WordPress. Более того, вы можете сами создавать свои хуки и фильтры в своих решениях. Здесь я лишь покажу на примере, как можно работать с хуками в плагине:
<?php
//Исходники из этой статьи - https://feodoraxis.ru/wordpress/contact-form-7-kak-polzovatsya.html
add_filter( "wpcf7_posted_data", "feodoraxis_change_cf7_data", 10 );
function feodoraxis_change_cf7_data( $posted_data ) {
if ( isset( $posted_data["product-id"] ) && intval( $posted_data["product-id"] ) > 0 ) {
$post = get_post( $posted_data["product-id"] );
if ( isset( $post->post_title ) ) {
$posted_data["product-id"] = $post->post_title . " | " . get_permalink( $post );
} else {
$posted_data["product-id"] = "Товар не найден.";
}
}
return $posted_data;
}
Полезные советы
Не используйте анонимные функции
Это плохая практика, потому что такой хук нельзя удалить через remove_action(), а именованные — можно. Иногда это бывает необходимо.
Кроме того, одну и ту же именованную функцию можно прикрепить к разным хукам. Так нам не придется несколько раз описывать одно и тоже.
Комментирйте экшены
Если создаёте свой экшен, обязательно комментируйте, какие хуки и с каким приоритетом добавили их к нему. Выглядеть это может так:
<?php
get_header();
the_post();
/**
* Hook: feodoraxis_front_page
*
* @hooked front_page_open - 10
* @hooked section_first - 20
* @hooked section_about - 30
* @hooked section_recall - 40
* @hooked front_page_close - 50
**/
do_action( "feodoraxis_front_page", $post );
Также обязательно пишите в комментариях к функциям и методам о том, к каким экшенам вы их прикрепили. Выглядеть это может так:
<?php
/**
* Hook: feodoraxis_front_page - 10
*/
function front_page_open() {
//Some code...
}
Комментировать экшены из ядра не нужно 🙂