WordPress Hooks: Пояснення дій, фільтрів та розширених шаблонів використання
Хуки WordPress — це основний архітектурний механізм, який дозволяє розробникам впроваджувати власний код у заздалегідь визначені точки виконання в WordPress — без зміни основних файлів, тем або сторонніх плагінів. Існує рівно два типи: хуки дій, які запускають власні функції при певних подіях, та хуки фільтрів, які перехоплюють і перетворюють дані перед їх відображенням або збереженням. Опанування обох є обов’язковим для будь-якої серйозної розробки на WordPress.
Цей посібник виходить за рамки основ. Ви знайдете точні довідники синтаксису, реальні граничні випадки, механіку пріоритетів та архітектурні патерни, які відрізняють підтримуваний код WordPress від крихких, схильних до конфліктів рішень.
Система хуків WordPress: як вона працює зсередини
WordPress виконується у передбачуваній послідовності — завантаження ядра, завантаження плагінів, завантаження активної теми, а потім відображення запитаної сторінки. Протягом цього життєвого циклу рушій викликає do_action() та apply_filters() у сотнях заздалегідь визначених точок. Ці виклики і є хуками.
Коли ви реєструєте зворотний виклик за допомогою add_action() або add_filter(), WordPress зберігає його у глобальному масиві $wp_filter, індексованому за назвою хука та пріоритетом. Під час виконання, коли хук спрацьовує, WordPress перебирає всі зареєстровані зворотні виклики у порядку пріоритету та виконує їх послідовно.
Ця архітектура означає:
- Ви ніколи не торкаєтесь основних файлів WordPress (
wp-includes/,wp-admin/) - Ваші налаштування зберігаються після оновлень ядра
- Кілька плагінів можуть підключатися до одного хука без конфліктів — за умови правильного управління пріоритетами
Усі реєстрації хуків повинні знаходитися у власному плагіні або у файлі functions.php вашої теми. Для виробничих середовищ, що працюють на плані VPS Hosting, розгортання налаштувань як окремого плагіна є значно кращим варіантом порівняно з functions.php, оскільки зміна теми не видалить вашу функціональність непомітно.
Хуки дій проти хуків фільтрів: основні відмінності
| Атрибут | Хуки дій | Хуки фільтрів |
|---|---|---|
| — | — | — |
| Основне призначення | Виконання побічних ефектів при певній події | Перехоплення та перетворення даних |
| Потрібне повернення значення | Ні — зворотні виклики нічого не повертають | Так — зворотні виклики ПОВИННІ повертати значення |
| Основна функція для запуску | `do_action()` | `apply_filters()` |
| Основна функція для реєстрації | `add_action()` | `add_filter()` |
| Функція видалення | `remove_action()` | `remove_filter()` |
| Типові випадки використання | Підключення скриптів, надсилання електронних листів, журналювання подій | Зміна вмісту, зміна заголовків, перетворення аргументів запиту |
| Дані, що передаються до зворотного виклику | Необов’язкові контекстуальні аргументи | Значення даних, що фільтруються (обов’язково) |
| Поведінка ланцюжка | Зворотні виклики виконуються послідовно, незалежно | Кожен зворотний виклик отримує результат попереднього |
Найпоширеніша помилка розробників — забути return значення всередині зворотного виклику фільтра. Якщо ви пропустите оператор return, відфільтроване значення стане null, що непомітно порушить виведення на фронтенді — це надзвичайно важко відстежуваний баг.
Хуки дій: поглиблений розгляд
Синтаксис і параметри
add_action( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );$hook_name— Точна назва хука, до якого потрібно підключитися.$callback— Будь-який допустимий PHP callable: іменована функція, анонімна функція, статичний метод (['ClassName', 'method']) або метод об’єкта ([$object, 'method']).$priority— Порядок виконання відносно інших зворотних викликів на тому ж хуку. Менші числа виконуються першими. За замовчуванням10. Використовуйте від’ємні цілі числа для виконання перед усіма стандартними зворотними викликами.$accepted_args— Кількість аргументів, які ваш зворотний виклик прийматиме від хука. Має відповідати тому, що передаєdo_action(), інакше ви отримаєте попередження PHP.
Базовий приклад: додавання вмісту після кожного запису
add_action( 'the_content', 'alexhost_append_cta', 20 );
function alexhost_append_cta( $content ) {
if ( is_single() && in_the_loop() && is_main_query() ) {
$content .= '<p class="post-cta">Enjoyed this article? Share it with your network.</p>';
}
return $content;
}Зверніть увагу на захисні перевірки in_the_loop() та is_main_query(). Без них ваш зворотний виклик спрацьовує при кожному виклику the_content() — включаючи області віджетів, конструктори сторінок та відповіді REST API — що призводить до дублювання виведення, яке надзвичайно важко налагодити.
Розширений приклад: надсилання сповіщення Slack при публікації запису
add_action( 'transition_post_status', 'alexhost_notify_on_publish', 10, 3 );
function alexhost_notify_on_publish( $new_status, $old_status, $post ) {
if ( 'publish' === $new_status && 'publish' !== $old_status && 'post' === $post->post_type ) {
$webhook_url = defined( 'SLACK_WEBHOOK_URL' ) ? SLACK_WEBHOOK_URL : '';
if ( empty( $webhook_url ) ) {
return;
}
wp_remote_post( $webhook_url, [
'body' => wp_json_encode( [ 'text' => 'New post published: ' . get_permalink( $post ) ] ),
'headers' => [ 'Content-Type' => 'application/json' ],
'data_format' => 'body',
] );
}
}Цей патерн використовує transition_post_status замість publish_post, оскільки він надає вам як старий, так і новий статус, дозволяючи відрізнити першу публікацію від оновлення вже опублікованого запису.
Видалення дії, зареєстрованої іншим плагіном
remove_action( 'wp_footer', 'some_plugin_footer_function', 10 );Значення пріоритету в remove_action() повинно точно відповідати пріоритету, використаному в оригінальному виклику add_action(). Якщо ви не знаєте пріоритет, перевірте вихідний код плагіна або скористайтеся інструментом налагодження хуків. Невідповідність означає, що видалення непомітно не спрацює — функція все одно виконається.
Хуки фільтрів: поглиблений розгляд
Синтаксис і параметри
add_filter( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );Сигнатура ідентична add_action(). Критична поведінкова відмінність: ваш зворотний виклик отримує поточне значення відфільтрованих даних як перший аргумент і повинен повертати значення.
Базовий приклад: перетворення заголовків записів у Title Case
add_filter( 'the_title', 'alexhost_titlecase_post_title', 10, 2 );
function alexhost_titlecase_post_title( $title, $post_id ) {
if ( is_admin() ) {
return $title;
}
return mb_convert_case( $title, MB_CASE_TITLE, 'UTF-8' );
}Використання mb_convert_case() замість strtoupper() є правильним підходом для багатомовних сайтів. strtoupper() не є безпечним для багатобайтових символів і пошкодить символи в нелатинських шрифтах.
Розширений приклад: зміна аргументів основного запиту
add_filter( 'pre_get_posts', 'alexhost_exclude_category_from_home' );
function alexhost_exclude_category_from_home( $query ) {
if ( ! is_admin() && $query->is_main_query() && $query->is_home() ) {
$query->set( 'category__not_in', [ 5, 12 ] );
}
}pre_get_posts технічно є хуком дії (він не вимагає повернення), але модифікує об’єкт WP_Query за посиланням — змушуючи його поводитися як фільтр. Це поширена точка плутанини. Ви модифікуєте $query безпосередньо; ви не повертаєте його.
Ланцюжок фільтрів: що пропускають розробники
Коли кілька зворотних викликів підключаються до одного фільтра, кожен отримує результат попереднього. Якщо зворотний виклик A з пріоритетом 10 перетворює $content, а зворотний виклик B з пріоритетом 11 також перетворює $content, B працює з результатом A — а не з оригіналом. Це ланцюжок є потужним, але вимагає ретельного планування пріоритетів, коли кілька плагінів торкаються одних і тих самих даних.
Пріоритет і порядок виконання: практичний довідник
| Значення пріоритету | Коли виконується | Типовий випадок використання |
|---|---|---|
| — | — | — |
| `1` – `9` | До стандартних значень WordPress | Раннє перевизначення поведінки ядра |
| `10` | За замовчуванням | Стандартні налаштування плагіна/теми |
| `11` – `19` | Після стандартного, перед пізніми хуками | Постобробка виведення іншого плагіна |
| `20` – `99` | Пізнє виконання | Очищення, фінальне форматування |
| `PHP_INT_MAX` | Абсолютно останній | Гарантоване виконання в крайньому випадку |
| Від’ємний (наприклад, `-1`) | Перед усім | Завдання попередньої ініціалізації |
Довідник основних хуків WordPress
Найважливіші хуки дій
init— Спрацьовує після завантаження WordPress, але до надсилання заголовків. Використовуйте його для реєстрації власних типів записів, таксономій та правил перезапису. Уникайте використанняplugins_loadedдля реєстрації CPT — він спрацьовує надто рано.wp_enqueue_scripts— Єдине правильне місце для підключення CSS та JavaScript фронтенду. Ніколи не використовуйтеwp_headбезпосередньо для впровадження скриптів.admin_enqueue_scripts— Підключайте ресурси виключно в панелі адміністратора. Приймає аргумент$hook_suffixдля орієнтування на конкретні сторінки адміністратора.wp_footer— Спрацьовує безпосередньо перед</body>. Ідеально підходить для аналітичних фрагментів, відкладених скриптів та некритичної розмітки.save_post— Спрацьовує після збереження запису. Використовуйте його для запуску інвалідації кешу, синхронізації даних із зовнішніми API або оновлення власних мета-даних. Завжди перевіряйте nonce та перевіряйтеwp_is_post_revision(), щоб уникнути подвійного спрацювання.template_redirect— Спрацьовує до того, як WordPress визначить, який шаблон завантажити. Використовуйте для власних перенаправлень або контролю доступу.wp_login— Спрацьовує при успішному вході користувача. Корисний для журналювання аудиту або управління сесіями на багатокористувацьких сайтах.
Найважливіші хуки фільтрів
the_content— Фільтрує вміст запису перед відображенням. Зверніть увагу: цей хук спрацьовує при кожному викликуget_the_content(), включаючи відповіді REST API у WordPress 5.5+.the_title— Фільтрує заголовки записів і сторінок. Отримує як$title, так і$post_idяк аргументи, коли$accepted_argsвстановлено в2.excerpt_length— Контролює кількість слів у автоматично згенерованих уривках. Повертає ціле число.upload_mimes— Фільтрує список дозволених MIME-типів для завантаження. Використовуйте це для увімкнення завантаження SVG (з належною санітизацією) або обмеження завантажень до певних типів файлів.wp_nav_menu_items— Фільтрує HTML-виведення навігаційних меню. Корисний для впровадження динамічних елементів, таких як посилання входу/виходу.body_class— Фільтрує масив CSS-класів, застосованих до тегу<body>. Приймає масив, а не рядок — часте джерело помилок.cron_schedules— Додає власні інтервали WP-Cron. Необхідний для фонових завдань обробки на сайтах, розміщених на Dedicated Servers, де ви також можете налаштувати справжній системний cron як заміну.
Створення власних хуків у плагінах і темах
Добре спроектовані плагіни надають власні хуки, щоб інші розробники могли розширювати їх без форкування коду. Це ознака WordPress-розробки професійного рівня.
Визначення власного хука дії
// Inside your plugin's core function
function alexhost_process_order( $order_id ) {
// ... processing logic ...
// Fire a custom action so other code can react
do_action( 'alexhost_order_processed', $order_id );
}Визначення власного хука фільтра
function alexhost_get_product_price( $product_id ) {
$base_price = get_post_meta( $product_id, '_price', true );
// Allow other code to modify the price before returning it
return apply_filters( 'alexhost_product_price', $base_price, $product_id );
}Будь-який плагін або тема тепер може підключитися до alexhost_product_price для застосування знижок, конвертації валюти або розрахунку податків — без зміни вихідного коду вашого плагіна.
Видалення та заміна хуків: розширені патерни
Видалення хука, зареєстрованого всередині класу
Це один із найменш зрозумілих аспектів системи хуків. Якщо плагін реєструє метод за допомогою екземпляра об’єкта, ви не можете видалити його простим рядковим посиланням.
// Plugin registers like this:
$plugin_instance = new SomePlugin();
add_action( 'init', [ $plugin_instance, 'setup' ] );
// To remove it, you need access to the same object instance.
// One approach: hook into plugins_loaded and use the global instance if exposed.
add_action( 'plugins_loaded', function() {
global $some_plugin;
if ( isset( $some_plugin ) && is_a( $some_plugin, 'SomePlugin' ) ) {
remove_action( 'init', [ $some_plugin, 'setup' ] );
}
}, 20 );Якщо плагін не надає свій екземпляр глобально, вам доведеться безпосередньо перебирати $GLOBALS['wp_filter'] — крихкий підхід, який свідчить про погану архітектуру цільового плагіна.
Захисне використання has_action() та has_filter()
if ( has_action( 'wp_footer', 'some_third_party_function' ) ) {
remove_action( 'wp_footer', 'some_third_party_function' );
}has_action() повертає пріоритет зареєстрованого зворотного виклику (ціле число), якщо знайдено, або false, якщо ні. Це повернуте значення часто неправильно використовується — розробники перевіряють if ( has_action(...) ), очікуючи булеве значення, але отримання 0 (дійсний пріоритет) оцінюється як хибне. Завжди використовуйте !== false для надійної перевірки:
if ( false !== has_action( 'wp_footer', 'some_third_party_function' ) ) {
remove_action( 'wp_footer', 'some_third_party_function', 0 );
}Міркування щодо продуктивності для виробничих середовищ
Хуки окремо додають мінімальні накладні витрати, але погано написані зворотні виклики накопичуються у вимірювану затримку. Основні патерни для дотримання:
- Захищайте дорогі операції умовними перевірками. Запити до бази даних, виклики зовнішніх API та файловий ввід/вивід всередині зворотних викликів хуків повинні бути загорнуті в умовні перевірки (
is_single(),is_admin(),is_main_query()), щоб запобігти їх виконанню при кожному завантаженні сторінки. - Використовуйте кешування об’єктів. Якщо зворотний виклик хука отримує дані з бази даних, загорніть результат у транзієнт або використовуйте
wp_cache_get()/wp_cache_set(). На правильно налаштованому VPS з cPanel або сервері з Redis це значно зменшує кількість звернень до бази даних. - Уникайте анонімних функцій, коли вам потрібно видаляти хуки. Ви не можете викликати
remove_action()для анонімної функції, оскільки у вас немає посилання на неї. Завжди використовуйте іменовані функції або збережені посилання для зворотних викликів, які вам може знадобитися скасувати. - Аудит завантаження хуків за допомогою Query Monitor. Плагін Query Monitor надає спеціальну панель «Hooks & Actions», що показує кожен хук, який спрацював під час запиту, підключені зворотні виклики та час їх виконання. Це незамінно для діагностики регресій продуктивності на сайтах з великим трафіком.
Міркування щодо безпеки
Хуки є поширеною поверхнею атаки в погано написаних плагінах. Конкретні ризики, які слід розуміти:
- Невалідований ввід у зворотних викликах
save_post. Завжди перевіряйте nonce (check_admin_referer()), підтверджуйтеcurrent_user_can()та санітизуйте всі дані$_POSTперед обробкою. - Підвищення привілеїв через хуки
init. Код, що змінює ролі або можливості користувачів всерединіinitбез перевірки можливостей, може бути запущений неавтентифікованими запитами. - Впровадження через фільтри. Якщо зворотний виклик фільтра виводить дані безпосередньо на сторінку без екранування, він стає вектором XSS. Фільтри повинні перетворювати дані; екранування повинно відбуватися в точці виведення за допомогою
esc_html(),esc_attr()абоwp_kses_post(). - SSRF через HTTP-запити, запущені хуками. Зворотні виклики, що виконують виклики
wp_remote_get()на основі URL-адрес, наданих користувачем (наприклад, уsave_post), повинні валідувати та санітизувати URL за допомогоюesc_url_raw()і в ідеалі обмежувати дозволені хости.
Для сайтів, що обробляють конфіденційні дані або транзакції електронної комерції, поєднання вашої інсталяції WordPress з правильно налаштованими SSL Certificates є базовою вимогою — хуки, що передають дані на зовнішні кінцеві точки через незашифровані з’єднання, є критичною вразливістю.
Контрольний список найкращих практик
- Використовуйте унікальні, іменовані функції (наприклад,
myplugin_functionname), щоб запобігти колізіям з ядром, темами та іншими плагінами. - Завжди вказуйте
$accepted_args, коли ваш зворотний виклик потребує більше одного аргументу від хука. - Ніколи не використовуйте
echoвсередині зворотного виклику фільтра — лишеreturn. - Розміщуйте реєстрації хуків всередині умовної перевірки або функції ініціалізації, а не в глобальній області файлу, який може бути включений кілька разів.
- Документуйте кожен власний хук, який ви надаєте, за допомогою блоків
@hook, щоб інші розробники могли їх виявити. - Тестуйте видалення хуків з точним збігом пріоритетів — невідповідність є непомітним збоєм.
- Використовуйте
current_filter()всередині зворотного виклику, щоб підтвердити, який хук його запустив, коли одна функція підключена до кількох хуків.
Практична матриця рішень: коли який тип хука використовувати
| Сценарій | Тип хука | Рекомендований хук |
|---|---|---|
| — | — | — |
| Додати піксель відстеження перед `</body>` | Action | `wp_footer` |
| Змінити вміст запису перед відображенням | Filter | `the_content` |
| Зареєструвати власний тип запису | Action | `init` |
| Обмежити типи файлів для завантаження | Filter | `upload_mimes` |
| Надіслати електронний лист після завершення замовлення | Action | Власна дія у функції обробки замовлення |
| Змінити кількість слів у уривку | Filter | `excerpt_length` |
| Перенаправити незареєстрованих користувачів | Action | `template_redirect` |
| Додати CSS-клас до тегу body | Filter | `body_class` |
| Підключити власну таблицю стилів | Action | `wp_enqueue_scripts` |
| Змінити WP_Query перед виконанням | Action (за посиланням) | `pre_get_posts` |
FAQ
У чому різниця між do_action() та apply_filters() у WordPress?
do_action() запускає хук дії — він виконує всі зареєстровані зворотні виклики в цій точці, але не повертає значення до коду, що викликає. apply_filters() запускає хук фільтра — він передає значення через усі зареєстровані зворотні виклики послідовно та повертає остаточно перетворене значення до викликача. Дії виробляють побічні ефекти; фільтри перетворюють дані.
Чи можна використовувати хук фільтра WordPress як хук дії?
Технічно add_action() є обгорткою навколо add_filter() в ядрі WordPress. Однак використання хука фільтра як дії (без повернення значення) призведе до того, що відфільтроване значення стане null, порушуючи будь-які дані, що оброблялися. Завжди використовуйте семантично правильну функцію для передбачуваної мети.
Чому remove_action() іноді не вдається видалити хук?
Найпоширенішою причиною є невідповідність пріоритету — пріоритет, переданий до remove_action(), повинен точно відповідати пріоритету, використаному в оригінальному виклику add_action(). Друга поширена причина — час виклику: remove_action() повинен бути викликаний після реєстрації хука, але до його спрацювання. Якщо оригінальна реєстрація відбувається всередині конструктора класу або пізно спрацьовуючого хука, ваш виклик видалення може виконатися надто рано.
Яке найбезпечніше місце для додавання власних хуків WordPress у виробничому середовищі?
Окремий, спеціально створений плагін є найбезпечнішим місцем. На відміну від functions.php, плагін зберігається при зміні теми та його легше контролювати версіями, тестувати та розгортати незалежно. У керованих середовищах VPS Hosting зберігання власних плагінів у приватному Git-репозиторії та розгортання через CI/CD-конвеєри є виробничим стандартом.
Як налагодити, які хуки спрацьовують на певній сторінці WordPress?
Встановіть плагін Query Monitor та перейдіть на цільову сторінку, увійшовши як адміністратор. Вкладка «Hooks & Actions» перераховує кожен хук, що спрацював, кожен підключений зворотний виклик та час виконання для кожного зворотного виклику. Для налагодження через CLI на сервері wp hook list --format=table через WP-CLI надає статичний перелік усіх зареєстрованих хуків без завантаження браузера.
