Життєвий цикл виконання WordPress є синхронним конвеєром ініціалізації середовища, динамічної диспетчеризації хуків та контекстної маршрутизації. На відміну від сучасних фреймворків з єдиним роутером та сервіс-контейнером (Laravel, Symfony), WordPress базується на детермінованому ланцюжку процедурного підключення файлів ядра (Bootstrapping Sequence). Поверх цієї послідовності розгорнута подійно-орієнтована архітектура (Event-Driven Architecture) на базі глобальних реєстрів $wp_filter та $wp_actions.
У сучасному стеку WordPress 7.0/7.1+ («Armstrong») ядро пройшло глибоку модернізацію: впроваджено безпечне хешування паролів на базі алгоритму bcrypt (яке замінило застарілий phpass), додано нативні протоколи спільного редагування в реальному часі (Gutenberg Phase 3) та уніфіковано конвеєри реактивного фронтенду блоків (Interactivity API) і Block Bindings.
При надходженні вхідного HTTP-запиту через точку входу index.php керування негайно передається завантажувачу wp-blog-header.php. Цей скрипт виконує трифазний цикл:
wp-load.php → wp-config.php → wp-settings.php): зчитування конфігурації, ініціалізація середовища PHP 8.4+, завантаження ранніх дроп-інів кешування, підключення драйвера БД $wpdb, активних плагінів та функцій активної теми.wp() → WP::main()): синтаксичний аналіз запитаного URI через WP_Rewrite, компіляція SQL-запиту та вилучення даних з MySQL через WP_Query, заповнення глобального контексту $post та $wp_the_query.template-loader.php): вибір найбільш специфічного файлу шаблону згідно зі специфікацією Template Hierarchy (для класичних тем) або компіляція дерева блоків через Block Engine (для блокових тем FSE) з подальшою віддачею HTML-потоку в буфер виводу клієнта.Архітектуру WordPress критично важливо сприймати не як монолітний блок коду, а як пошаровий конвеєр (Pipeline) із суворо регламентованими контрольними точками.
Bootstrapping Sequence Drop-ins $wp_filter Template Hierarchy Execution Paths[Вхідний HTTP-запит]
│
▼
[1. index.php & wp-blog-header.php] ── Запуск таймера та буфера виводу
│
▼
[2. wp-config.php & wp-settings.php]
├── Pre-DB Drop-ins (advanced-cache.php, sunrise.php)
├── Ініціалізація $wpdb (db.php) & Object Cache (object-cache.php)
├── WPINC (Класи ядра, функції, l10n, bcrypt-автентифікація)
├── Must-Use Плагіни (wp-content/mu-plugins/)
├── Мережеві / Звичайні Плагіни (active_plugins) ──> do_action('plugins_loaded')
└── Ініціалізація Теми (functions.php) ───────────> do_action('after_setup_theme')
│
▼
[3. Ініціалізація Середовища: init & wp_loaded]
├── Реєстрація CPT, таксономій, ендпоінтів ─────> do_action('init')
└── Готовність ядра до обробки запиту ──────────> do_action('wp_loaded')
│
▼
[4. Диспетчеризація Запиту: WP::main()]
├── WP::parse_request() (WP_Rewrite розбирає URL у query_vars)
├── WP::send_headers() (HTTP-заголовки, Content-Type, Cache-Control)
└── WP_Query::query() (Виконання SQL, заповнення $wp_query)
│
▼
[5. Рендеринг та Завершення: template-loader.php]
├── do_action('template_redirect') (Шлюз редиректів та fast-exit)
├── Classic Template (single.php) АБО Block Template (FSE block engine)
└── do_action('shutdown') ── Закриття з'єднань з БД, скидання FastCGI буфераЗвичайний HTTP GET-запит сторінки. Виконуються всі 5 фаз ядра, парсинг URL, WP_Query та рендеринг теми.
Визначення констант пам'яті, завантаження db.php, підключення $wpdb та object-cache.php.
Не можна викликати функції теми чи плагінів — вони ще не завантажені.
Виноска: Якщо advanced-cache.php віддає сторінку, ядро зупиняється без звернення до БД.
Завантаження wp-config.php, констант безпеки, ініціалізація з'єднання $wpdb та підключення drop-ins об'єктного кешування.
Якщо advanced-cache.php віддає закешовану відповідь, ядро завершує роботу тут без підключення до MySQL.
Суворо послідовне завантаження: MU-плагіни, мережеві плагіни, активні плагіни з бази даних та functions.php активної теми.
На хуку plugins_loaded сесія користувача ще не розібрана — wp_get_current_user() поверне гостьового користувача.
Автентифікація користувача (bcrypt), реєстрація CPT, таксономій, блоків Gutenberg, REST-маршрутів та Block Bindings.
Точка повної готовності бізнес-логіки. Доступні права користувача, але URL сторінки ще не розпарсено.
WP_Rewrite парсить URL у query_vars, WP_Query виконує SQL у базі даних, заповнюючи глобальні $wp_query та $post.
У REST API та AJAX даний крок пригнічується, заощаджуючи час виконання та пам'ять PHP.
Визначення шаблону за Template Hierarchy або FSE block engine, компіляція HTML та фінальний хук shutdown.
Хук shutdown виконується після закриття FastCGI-з'єднання — ідеальний для фонового логування та очищення.
Визначаються ліміти пам'яті (WP_MEMORY_LIMIT), ключі безпеки, підключаються drop-ins advanced-cache.php та object-cache.php. Створюється екземпляр $wpdb.
База даних доступна, але функції бізнес-логіки та хуки плагінів ще не зареєстровані.
Спочатку підключаються Must-Use плагіни (mu-plugins), потім звичайні активні плагіни з бази даних, після чого спрацьовує хук plugins_loaded.
Точка для ініціалізації сервісів та DI-контейнерів плагінів без доступу до авторизації користувача.
Підключається functions.php активної батьківської та дочірньої теми. Реєструється підтримка можливостей теми (add_theme_support).
Функції теми стають доступними лише після завершення цього кроку.
Поточний користувач авторизований ($current_user). Реєструються Custom Post Types, кастомні таксономії, REST-маршрути та Block Bindings.
Ядро готове приймати будь-які команди додатку. Завершується глобальний етап wp_loaded.
Парсинг query-змінних, виконання основного циклу WP_Query, формування HTTP-заголовків. Спрацьовує хук template_redirect.
Фінальний рубіж перед виведенням: остання можливість змінити файл шаблону або завершити виконання через wp_send_json().
Визначення шаблону за ієрархією, генерація HTML/JSON розмітки. По завершенні віддачі контенту спрацьовує хук shutdown.
Хук shutdown викликається після відправлення відповіді клієнту; використовується для очищення ресурсів та асинхронних завдань.
Кожна підсистема WordPress суворо прив'язана до точки своєї появи в конвеєрі. Спроба запросити контекст до його ініціалізації призводить до прихованих помилок:
wp_get_current_user() поверне ID 0 на хуку plugins_loaded, оскільки плагіни автентифікації та кукі сесій розшифровуються лише перед хуком init.register_post_type() на after_setup_theme не створить правила WP_Rewrite, призводячи до фатальних помилок 404.wp-settings.php викличе збій скрипта через відсутність екземпляра $wpdb.Drop-in файли (advanced-cache.php, db.php, object-cache.php) виконуються в глобальній області видимості ДО підключення механізму хуків (add_action() / apply_filters()). Якщо advanced-cache.php знаходить валідний закешований HTML у Redis або FastCGI microcache, він віддає відповідь клієнту та викликає exit;. У цьому випадку PHP завершує виконання менш ніж за 5 мс без жодного SQL-запиту до MySQL.
На якому етапі життєвого циклу ядра WordPress гарантовано доступний поточний авторизований користувач (wp_get_current_user()), але головний SQL-запит сторінки (WP_Query) ще НЕ був відправлений до бази даних?
advanced-cache.php або template_redirect дозволяє віддати закешований JSON/HTML за 5–15 мс, оминаючи важкі SQL-запити WP_Query та рендеринг теми.parse_request або rest_api_init, запобігаючи завантаженню невикористовуваних ассетів теми та знижуючи споживання RAM PHP у 4–6 разів.sunrise.php на основі доменного імені запиту.Плагіни з каталогу wp-content/mu-plugins/ підключаються в алфавітному порядку ДО звичайних плагінів бази даних. Пам'ятайте: для MU-плагінів не викликаються хуки активації та деактивації (register_activation_hook), а ядро не сканує вкладені директорії без кастомного bootstrap-завантажувача.
Життєвий цикл ядра має фундаментальну особливість: він розділяється на різні шляхи виконання (Execution Paths) залежно від характеру запиту.
Нижче наведено production-ready інструмент профайлінгу на базі сучасного PHP 8.4+, оформлений як автономний Must-Use плагін. Він фіксує споживання оперативної пам'яті (RAM) та затримку в мікросекундах (microtime) між ключовими етапами ядра, впроваджуючи діагностичний заголовок Server-Timing за стандартами консорціуму W3C.
<?php
/**
* Plugin Name: Fullstack Agentic Lab — Core Lifecycle APM Profiler
* Description: Заміряє часові відсічки та споживання RAM на ключових етапах ядра WordPress.
* Version: 2.1.0
* Author: Artur Yusupov (Fullstack Agentic Lab)
*/
declare(strict_types=1);
namespace AgenticLab\Core\Profiler;
if (!defined('ABSPATH')) {
exit;
}
final class LifecycleProfiler
{
/** @var array<string, array{time: float, memory: int, memory_peak: int}> */
private static array $checkpoints = [];
private static float $startTime;
public static function init(): void
{
self::$startTime = defined('WP_START_TIMESTAMP')
? (float) WP_START_TIMESTAMP
: microtime(true);
self::checkpoint('bootstrap_start');
add_action('plugins_loaded', self::onPluginsLoaded(...), PHP_INT_MIN);
add_action('after_setup_theme', self::onThemeLoaded(...), PHP_INT_MIN);
add_action('init', self::onInit(...), PHP_INT_MIN);
add_action('wp_loaded', self::onWpLoaded(...), PHP_INT_MIN);
add_action('template_redirect', self::onTemplateRedirect(...), PHP_INT_MIN);
add_action('shutdown', self::onShutdown(...), PHP_INT_MAX);
}
public static function checkpoint(string $name): void
{
self::$checkpoints[$name] = [
'time' => microtime(true),
'memory' => memory_get_usage(false),
'memory_peak' => memory_get_peak_usage(true),
];
}
public static function onPluginsLoaded(): void { self::checkpoint('plugins_loaded'); }
public static function onThemeLoaded(): void { self::checkpoint('theme_loaded'); }
public static function onInit(): void { self::checkpoint('init'); }
public static function onWpLoaded(): void { self::checkpoint('wp_loaded'); }
public static function onTemplateRedirect(): void { self::checkpoint('template_redirect'); }
public static function onShutdown(): void
{
self::checkpoint('shutdown');
if (headers_sent() || is_admin() || wp_doing_cron() || (defined('REST_REQUEST') && REST_REQUEST)) {
return;
}
$timingMetrics = [];
$previousTime = self::$startTime;
foreach (self::$checkpoints as $phase => $data) {
$durationMs = ($data['time'] - $previousTime) * 1000.0;
$timingMetrics[] = sprintf('%s;dur=%.2f', $phase, $durationMs);
$previousTime = $data['time'];
}
$totalMs = (microtime(true) - self::$startTime) * 1000.0;
$timingMetrics[] = sprintf('total_execution;dur=%.2f', $totalMs);
// Відправка стандартного заголовка Server-Timing W3C
header('Server-Timing: ' . implode(', ', $timingMetrics));
header(sprintf('X-Peak-Memory: %.2f MB', memory_get_peak_usage(true) / 1048576));
}
}
LifecycleProfiler::init();Заголовок Server-Timing передає метрики фаз ядра безпосередньо у вкладку Network / Timing браузера DevTools. Це дозволяє Senior-інженеру в реальному часі моніторити затримки plugins_loaded та template_redirect на production-трафіку без встановлення важких сторонніх APM-агентів.
Яким чином архітектурно правильно реалізувати віддачу даних з Redis в обхід рендерингу теми та важкого SQL-запиту WP_Query?
У чому суть помилки: Виклик функцій авторизації (wp_get_current_user(), is_user_logged_in(), current_user_can()) безпосередньо в тілі плагіна або всередині хука plugins_loaded.
Чому розробники так роблять: Прагнення заблокувати доступ до плагіна або закритого розділу якомога раніше задля економії серверних ресурсів.
Що відбувається в продакшені: На етапі plugins_loaded плагіни автентифікації та кукі сесій користувача ще не верифіковані. Функція wp_get_current_user() повертає нульовий об'єкт WP_User(0), ідентифікуючи авторизованого адміністратора як неавторизованого гостя.
Як правильно: Виконувати перевірки авторизації суворо на хуку init (коли ядро завершило ініціалізацію користувача) або на template_redirect (перед віддачею представлення):
add_action('template_redirect', function(): void {
if (is_page('dashboard') && !is_user_logged_in()) {
wp_safe_redirect(wp_login_url(get_permalink()));
exit;
}
});У чому суть помилки: Виклик функції register_post_type() на хуку plugins_loaded або after_setup_theme.
Чому розробники так роблять: Бажання оголосити структури даних якомога ближче до файлу конфігурації плагіна.
Що відбувається в продакшені: Перезапис правил маршрутизації (Rewrite Rules), таксономії та метабокси на даному етапі ще не зареєстровані. CPT реєструється частково, видаючи помилку 404 при переході за прямими посиланнями або збої в редакторі Gutenberg.
Як правильно: Реєструвати CPT суворо на хуку init:
add_action('init', function(): void {
register_post_type('project', [
'public' => true,
'has_archive' => true,
'show_in_rest' => true, // Обов'язково для Gutenberg та REST API
'supports' => ['title', 'editor', 'custom-fields'],
]);
});У чому суть помилки: Виклик query_posts($args) всередині шаблону (index.php, archive.php) для кастомної фільтрації або пагінації.
Чому розробники так роблять: Здається найпростішим способом змінити виведення записів без занурення в хуки ядра.
Що відбувається в продакшені: Функція query_posts() знищує глобальний екземпляр $wp_query та виконує повторний важкий SQL-запит до MySQL, подвоюючи затримку TTFB і безповоротно ламаючи канонічну пагінацію (/page/2/ видає 404 Not Found).
Як правильно: Модифікувати параметри головного запиту до його відправлення в базу даних через фільтр-дію pre_get_posts:
add_action('pre_get_posts', function(\WP_Query $query): void {
if (!is_admin() && $query->is_main_query() && $query->is_post_type_archive('project')) {
$query->set('posts_per_page', 12);
$query->set('orderby', 'date');
$query->set('order', 'DESC');
}
});У чому суть помилки: Розміщення звернень до бази даних (get_posts(), wp_remote_get(), читання важких опцій) безпосередньо в глобальній області видимості functions.php.
Чому розробники так роблять: Нерозуміння того, що файл functions.php компілюється при кожному запиті до сайту, включаючи cron, REST API, CLI та запити до статичних ассетів.
Що відбувається в продакшені: Будь-який запит (навіть до wp-json/) ініціює неоптимізовані синхронні блокування зовнішніми API, викликаючи просідання RPS сайту в десятки разів.
Як правильно: Інкапсулювати логіку в методи класів та прив'язувати їх виконання до суворо відповідних хуків:
add_action('wp_enqueue_scripts', function(): void {
wp_enqueue_style('theme-styles', get_stylesheet_uri(), [], '1.0.0');
});Боти та автоматичні сканери вразливостей створюють до 80% запитів до неіснуючих адрес (.env, wp-login.php, xmlrpc.php). Обробка цих запитів стандартним конвеєром з рендерингом теми 404.php здатна перевантажити MySQL та вичерпати воркери PHP-FPM. Відсікання таких запитів через Fast 404 на етапі plugins_loaded або через Nginx/Cloudflare звільняє воркер PHP-FPM за 5–8 мс.
У WordPress 7.0/7.1 ядро автоматично перевіряє префікс збереженого пароля у wp_users. Якщо хеш починається із застарілого $P$ (phpass MD5), при першому вході функція wp_authenticate_username_password() перераховує його в $2y$ (bcrypt, cost 12) та оновлює запис у базі даних без необхідності ручного скидання пароля користувачем.