Хуки WordPress на практике: actions и filters без сюрпризов
Фильтр на the_content без проверки контекста вылезает в архивах и RSS, а забытый return обнуляет текст записи. Разбираем, где держать код хуков, как расставлять приоритеты и снимать чужие колбэки.

Вы добавили фильтр на the_content, чтобы под записью появлялся блок «Читайте также». Блок появился, но не только там: он полез в RSS-ленту и в архивы. Либо случилось обратное, и текст записи исчез, потому что колбэк забыл вернуть значение. Хуки WordPress выглядят просто: add_action и add_filter. Трудности в деталях: где держать код, в каком порядке он выполнится, как снять чужой колбэк и почему сохранение записи уходит в бесконечный цикл. Примеры написаны для WordPress 6.x. Вставляйте их в файл плагина или дочерней темы после открывающего <?php.
Action и filter: разница в контракте
Регистрируются они одинаково: имя хука, колбэк, приоритет, число аргументов. Различается ожидание. Action значит «сделай что-то в этой точке»: подключить скрипт, вывести разметку, записать лог. Возвращаемое значение игнорируется. Filter значит «вот значение, верни его обратно, изменённым или нет».
Фильтр обязан вернуть значение, даже если ничего в нём не изменил. Забытый return не вызывает ошибку, он молча обнуляет данные.
Сноска. Колбэк (callback) это функция, которую WordPress вызывает, когда выполнение доходит до хука. Хук это именованная точка в коде ядра, темы или плагина, к которой можно подключить колбэки.
Где держать код: три варианта
Прежде чем писать колбэк, решите, где он будет жить. От этого зависит, что случится при смене темы и как вы отключите код, если что-то пошло не так.
| Место | Что выигрываете | Чем платите |
|---|---|---|
| functions.php дочерней темы | Ничего не нужно устанавливать, правка занимает минуту | Код пропадает при смене темы, отключить его можно только правкой файла |
| Обычный плагин | Переживает смену темы, включается и выключается в админке | Нужен файл с заголовком плагина, его можно случайно отключить |
| mu-plugin (папка wp-content/mu-plugins) | Загружается всегда и раньше обычных плагинов, не отключается из списка | Файлы в подпапках сами не подхватываются, обновлять придётся вручную |
Фильтр на the_content: ограничиваем область
the_content применяется везде, где выводится содержимое записи: на одиночной странице, в архивах, в RSS-ленте. Фильтр без проверок затронет всё это. Три условия сужают область до нужной.
add_filter( 'the_content', 'blog_append_related_block', 20 );
function blog_append_related_block( $content ) {
// Только основной цикл на странице одной записи
if ( ! is_singular( 'post' ) || ! in_the_loop() || ! is_main_query() ) {
return $content; // возвращаем без изменений, а не пустоту
}
// Приоритет 20: после автоформатирования и шорткодов
return $content . '<aside><p>Читайте также в блоге</p></aside>';
}
is_singular('post') отсекает архивы и страницы. in_the_loop() и is_main_query() защищают от вложенных запросов и от вызовов вне основного цикла. Тот же принцип работает для pre_get_posts: проверяйте is_admin() и метод is_main_query() у объекта запроса, иначе правка заденет админку и чужие выборки.
Приоритет и число аргументов
- Приоритет по умолчанию равен 10. Чем меньше число, тем раньше выполнится колбэк. При равных приоритетах порядок определяет очередь регистрации.
- Чтобы сработать после чужого фильтра, берите число больше. Чтобы успеть до него, меньше.
- Четвёртый параметр, число принимаемых аргументов, по умолчанию равен 1. Даже если хук передаёт три значения, колбэк получит только первое.
Как снять чужой хук
remove_action и remove_filter работают, только если совпадают три вещи: имя хука, колбэк и приоритет. Снимать нужно после того, как колбэк добавлен, иначе вызов ничего не найдёт. Простейший случай это функция ядра: remove_action( 'wp_head', 'wp_generator' ); убирает мета-тег generator из шапки. Если колбэк это метод класса, нужен тот же объект, который передали в add_action. Замыкание без сохранённой ссылки снять нельзя вообще, поэтому в коде, который кто-то захочет переопределить, используйте именованные функции. Если доступа к объекту нет, поищите у плагина собственную настройку или фильтр для отключения нужной функции.
Свои хуки для своего кода
Если вы пишете плагин или тему для других, оставляйте точки расширения вместо необходимости править ваш код. Префикс обязателен: имена хуков общие на весь сайт, и без префикса вы рано или поздно столкнётесь с чужими.
// Схематично: точки расширения в вашем плагине
$price = apply_filters( 'shop_final_price', $price, $product_id );
do_action( 'shop_order_created', $order_id );
// Подписчик: другой плагин или дочерняя тема
add_filter( 'shop_final_price', 'shop_round_price', 10, 2 );
function shop_round_price( $price, $product_id ) {
return round( $price );
}
Что ломается
- Нет return на одной из веток фильтра. Чаще всего забывают ранний выход по условию.
- Не указано число аргументов. Колбэк на save_post без третьего параметра не увидит $post и $update.
- remove_action не срабатывает из-за другого приоритета или потому что вызван раньше add_action.
- Рекурсия: колбэк на save_post вызывает wp_update_post, который снова запускает save_post. Вдобавок хук срабатывает на ревизиях и автосохранениях.
Защита от рекурсии: на время обновления колбэк снимает сам себя.
add_action( 'save_post_post', 'blog_normalize_title', 10, 3 );
function blog_normalize_title( $post_id, $post, $update ) {
// Ревизии и автосохранения пропускаем
if ( wp_is_post_revision( $post_id ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
return;
}
// Иначе wp_update_post снова вызовет этот же колбэк
remove_action( 'save_post_post', 'blog_normalize_title', 10 );
wp_update_post( array(
'ID' => $post_id,
'post_title' => trim( $post->post_title ),
) );
add_action( 'save_post_post', 'blog_normalize_title', 10, 3 );
}
Когда что выбирать
- Нужно изменить данные, которые WordPress вот-вот выведет или сохранит (текст, заголовок, цену): filter.
- Нужно выполнить побочное действие (подключить скрипт, отправить письмо, сбросить кеш): action.
- Нужно изменить выборку записей до запроса: action pre_get_posts, объект запроса меняется на месте.
- Код про оформление и нужен только этой теме: дочерняя тема. Должен пережить смену темы: плагин. Нельзя отключать случайно: mu-plugin.
Что делать дальше
Поставьте Query Monitor на тестовую копию сайта и откройте панель хуков: там видно, какие колбэки висят на хуке и в каком порядке. Для быстрой проверки в коде есть has_filter, has_action и did_action. Затем замерьте, сколько раз ваш колбэк вызывается на странице архива: на the_content он срабатывает по разу на каждую запись, и тяжёлый запрос внутри умножится. Инвалидация кеша и хуки блочного редактора здесь не разбираются, это отдельные темы.
Проверка рекламы на риски: Что в макете вызовет вопросы ФАС: превосходные степени, сравнения, обещания результата, маркировка.
Спросить за 40 ₽Источники: WordPress Plugin Handbook: Hooks, WordPress Developer Resources: add_filter()
Частые вопросы
Чем add_action отличается от add_filter, если сигнатуры одинаковы?
Фильтр подключён, а страница пустая или текст записи пропал. Что проверить?
Колбэк получает только первый аргумент, хотя хук передаёт несколько. Почему?
Что, если у меня другая версия WordPress?
Хук не срабатывает. Как понять, вызывается ли он вообще?
Не замедлят ли сайт десятки хуков?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.