Плагины и темы WordPress меняют то, что отображается на экране, и то, как обрабатываются данные, без необходимости изменять файлы самого ядра WordPress (код в каталогах  wp-includes  /  wp-admin ). Сделать это позволяет механизм под названием  хуки (hooks), которые делятся на два типа:  экшены (actions) и  фильтры (filters). Тема этого блога,  wpmm-blog , активно использует оба типа в своем файле  functions.php , поэтому в этой статье мы разберем реальные примеры кода из нее.

Примечание: хук — это точка, которую разработчики ядра WordPress намеренно заложили для подключения стороннего кода. В определенных местах исходного кода ядра уже встроены вызовы вроде  do_action()  и  apply_filters() . Плагины и темы «цепляют» (hook) свои функции к этим точкам, чтобы перехватывать выполнение в нужный момент, не изменяя файлы ядра.

Чем экшен отличается от фильтра

Разница между этими двумя типами хуков предельно проста: возвращают ли они значение.

  •  Экшен (Action): выполняет определенный код в заданный момент. Ему ничего не нужно возвращать — он предназначен для побочных эффектов (запись лога, сохранение данных, отправка внешнего запроса).
  •  Фильтр (Filter): получает значение, которое уже есть у ядра, изменяет его и возвращает обратно. Он нужен для модификации данных прямо перед тем, как ядро их использует или выводит.

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

Пример фильтра: длина анонса и маркер «читать далее»

По умолчанию ядро WordPress обрезает анонсы (excerpt) на страницах архивов до 55 слов, добавляя в конце  […] . Чтобы изменить это поведение, не нужно лезть в исходный код ядра — достаточно зарегистрировать функции на два хука-фильтра:  excerpt_length  и  excerpt_more .

 if (!function_exists('wpmm_blog_excerpt_length')) {
    function wpmm_blog_excerpt_length($length) {
        // Japanese content is measured in "words" here too, but 60
        // reads as a reasonable length even with full-width characters
        return 60;
    }
}
add_filter('excerpt_length', 'wpmm_blog_excerpt_length');

if (!function_exists('wpmm_blog_excerpt_more')) {
    function wpmm_blog_excerpt_more($more) {
        return ' …';
    }
}
add_filter('excerpt_more', 'wpmm_blog_excerpt_more');
 

Ключевая деталь: каждая функция  получает стандартное значение от ядра в качестве аргумента и возвращает замену. Функция  wpmm_blog_excerpt_length($length)  принимает дефолтное значение ядра (55) как  $length , но эта реализация игнорирует его и всегда возвращает фиксированное число 60. Внутренняя логика ядра берет то, что вернула функция, и использует это значение в качестве новой длины анонса.

Пример экшена: предварительная генерация OGP-изображения при сохранении

В качестве примера экшена: этот блог предварительно генерирует и кэширует OGP-изображение (Open Graph) записи в момент ее сохранения или публикации, используя хук  save_post .

 add_action('save_post', '_wpmm_blog_pregenerate_ogp_on_publish', 10, 3);
 

Функция  add_action()  принимает по порядку: имя хука, функцию обратного вызова (callback), приоритет и количество аргументов, которые ожидает callback. В данном вызове используется приоритет 10 (по умолчанию) и запрашивается 3 аргумента.

Событие  save_post  способно передавать своим callback-функциям три аргумента —  $post_ID ,  $post  (объект записи) и  $update  (булево значение, указывающее, обновление это или новая запись), — но эта возможность доступна только в том случае, если callback явно запрашивает такое количество аргументов; по умолчанию передается только первый. Параметр  3  в конце этого вызова существует специально для того, чтобы callback мог использовать все три аргумента.

Зачем нужен приоритет

У такого популярного хука, как  save_post , часто бывает сразу несколько плагинов или тем, регистрирующих свои callback-функции одновременно, не говоря уже о собственном встроенном использовании этого хука ядром. Когда у всех callback-функций установлен одинаковый приоритет по умолчанию (10), они выполняются в порядке регистрации. Однако на практике иногда требуется «выполнить после вот этого callback» или «выполнить раньше всего остального», и приоритет служит как раз явным числовым регулятором для этого. Меньшие числа выполняются раньше, большие — позже.

Почему благодаря этому ядро никогда не приходится редактировать напрямую

В исходном код ядра WordPress в ключевых точках по всему проекту встроены вызовы  do_action('hook-name', ...)  (вызов экшена) и  apply_filters('hook-name', $value, ...)  (применение фильтра). Плагины и темы регистрируют свои callback-функции для этих заранее подготовленных точек входа с помощью  add_action()  /  add_filter()  — им вовсе не нужно модифицировать файлы ядра.

Это дает вполне осязаемую практическую пользу. Когда ядро WordPress обновляется (автоматически или вручную), его файлы целиком заменяются на новую версию. Любые изменения, внесенные напрямую в файлы ядра, были бы молча стерты при следующем обновлении. Кастомизация же через хуки живет в отдельном файле (в каталогах  wp-content/themes/  или  wp-content/plugins/ ), полностью изолированном от ядра — поэтому она переживает любые обновления ядра в неприкосновенности.

Итог

Тип Назначение Возвращаемое значение Пример из этого блога
Экшен Выполнить код в определенный момент Отсутствует (только побочные эффекты)  save_post  → предварительная генерация OGP-изображения
Фильтр Изменить значение, переданное ядром Измененное значение  excerpt_length  → изменение количества слов в анонсе на 60

Экшены и фильтры в совокупности представляют собой тот самый механизм, который позволяет ядру WordPress, плагинам и темам взаимодействовать друг с другом, не изменяя чужой исходный код. Когда вы читаете незнакомый код на WordPress, обычно достаточно проверить всего две вещи: является ли это  add_action  или  add_filter  и на каком хуке это зарегистрировано — чтобы точно понять, в какой момент потока выполнения ядра происходит перехват и как именно.