Как загружается ядро WordPress
При работе с темами, плагинами и вообще с любым кодом WordPress, включая хаки в популярном файле темы functions.php. Хорошо бы знать в какой последовательности подключаются php файлы движка, когда срабатывают важные хуки и какие важные константы определены. В этой статье поговорим о такой последовательности загрузки.
Примеры
Чтобы было понятнее, возьмем простую задачу: нам нужно подключить какой-то код в админ-панели WordPress и только там - код не должен срабатывать во фронте или при AJAX запросах. Кто-то скажет, что достаточно сделать проверку с помощью is_admin(), однако это не так, потому что is_admin() вернет true и при AJAX запросе в файл admin-ajax.php.
Или например, можно подключить какую-то логику на хук admin_init и потом долго недоумевать, а почему моя логика работает и при запросах AJAX. Или почему она мешает плагинам, которые работают с AJAX...
Или вот еще пример из комментария. Когда в файле темы functions.php выполняется какой-то код, который зависит от другого кода подключаемого через хук init. Например, вы создали тип записи во время хука init и в самом файле functions.php выполняете проверку, которая опирается на этот новый тип записи и ожидаете что код будет работать, но это не так, потому что прямой код в functions.php срабатывает раньше чем событие init...
Таких примеров можно привести много. Но чтобы так не ошибаться, нужно разобраться и понять последовательность загрузки ядра WordPress. Этим мы и займемся.
Порядок загрузки (теория)
Понять логику загрузки не так сложно, как может показаться на первый взгляд. Для этого давайте взглянем на эту, столь любимую мной, картинку:

Существует 4 варианта загрузки. На самом деле их чуть больше, но это основные:
- Загрузка Фронтэнда (темы).
- Загрузка REST запроса.
- Загрузка Админки.
- Загрузка AJAX запроса (admin-ajax.php).
Во всех случаях загружается ядро WordPress - файл wp-load.php. Ядро загружается всегда и везде!
Прежде чем переходить к каждому типу загрузки, нужно разобраться как загружается само ядро.
Загрузка ядра WordPress
Ядро WP загружается при любом запросе: фронт, аякс, админка, REST ...
wp-load.php wp-config.php wp-settings.php // Функции загрузки (load): wp_debug_mode(), timer_start(), require_wp_db() ... // Функции констант: wp_initial_constants(), wp_cookie_constants() ... // Функции плагинов (хуки, активация): do_action(), plugin_dir_url(), register_activation_hook(). // Устанавливаются константы: WP_START_TIMESTAMP, WP_MEMORY_LIMIT, WP_MAX_MEMORY_LIMIT, WP_DEBUG, SCRIPT_DEBUG, WP_CONTENT_DIR, WP_CACHE. // Стандартизируются переменные сервера: wp_fix_server_vars(). // Проверяется режим разработки: wp_maintenance(). // Включается таймер (скорость загрузки): timer_start(). // Включается дебаг режим: wp_debug_mode(). // Загружается 'wp-content/advanced-cache.php' если он есть и включено WP_CACHE. // База данных. $wpdb. require_wp_db(). // Загружается 'wp-content/db.php' если есть и создается // соединение с БД и все связанные переменные (префикс БД...). // Объектный кэш: 'wp-content/object-cache.php' если есть, или 'wp-include/cache.php'. // Подключаются базовые хуки (фильтры): default-filters.php. // Включается Multisite (если нужно) // Грузится 'wp-content/sunrise.php' если есть (только для multisite). // register_shutdown_function( 'shutdown_action_hook' ) // SHORTINIT: остановка загрузки, где есть только самое базовое. if( SHORTINIT ){ return false; } // Подключаются функции локализации. // Проверяется установлен ли WP: wp_not_installed(). // Подключается куча файлов с остальными функциями WordPress. // Подключается Must-use плагины и срабатывает событие: do_action( 'muplugins_loaded' ); // Константы cookie, ssl: COOKIEPATH, COOKIE_DOMAIN // Общие глобальные переменные: $pagenow, $is_apache, $is_nginx, $is_lynx // Глоб. переменные клиента: $is_opera, $is_NS4, $is_safari, $is_chrome, $is_iphone, $is_IE, $is_edge // Подключаются Активные плагины. // Подключаются pluggable функции: pluggable.php. // Срабатывает do_action( 'plugins_loaded' ); // Принудительное экранирование значений в $_POST, $_REQUEST ... см. wp_magic_quotes() . // Глобальные переменные: // $wp_the_query — new WP_Query() // $wp_query — $wp_the_query // $wp_rewrite — new WP_Rewrite() — константы, функции и правила ЧПУ. // $wp — new WP() — запрос WP (запускается позже). // $wp_widget_factory — new WP_Widget_Factory() // $wp_roles — new WP_Roles() // Текущая тема do_action( 'setup_theme' ); // functions.php (child) - сначала подключается functions.php дочерней темы // functions.php (parent) - затем подключается functions.php основной темы // Файл перевода WordPress: load_default_textdomain() do_action( 'after_setup_theme' ); // первый хук доступный в теме // Устанавливается текущий юзер (создается объект). // См. wp_get_current_user() // Юзер часто уже определен после события 'plugins_loaded' плагинами. $wp->init(); // init событие. Когда среда WP, плагины и тема активированны, // но на экран еще ничего не выведено: do_action( 'init' ); // Регистрация виджетов: событие 'widget_init' // Проверка статуса сайта для мультисайтовой сборки. // Сайт может быть: удален, неактивен, в архиве. См. ms_site_check() // Если сайт не прошел проверку, то вызывается drop файл и PHP прерывается через die(). // Тоже самое что `init` только после проверки статуса. // До этой строки работа PHP может не дойти. Например при REST запросе. do_action( 'wp_loaded' );
При загрузки ядра всегда подключаются файл темы functions.php, даже в админке. Т.е. не важно, нужна нам тема или нет, functions.php работает и поэтому его можно поставить в один ряд с плагинами... Сделано так для удобства, чтобы любой код можно было «сунуть» в наш, потому и любимый, файл functions.php. Такое поведение не логично с точки зрения программирования, но очень удобно с точки зрения разработки.
Ядро, как мы видим, находится в файле wp-settings.php, но перед ним вызывается wp-load.php. Нужно это для того, чтобы найти файл wp-config.php. Дело в том, что wp-config.php может находится в одной папке со всеми файлами wordPress, а может быть вынесен в родительскую папку. Задача wp-load.php отыскать файл конфигурации и подключить его . Если его нигде нет, то WP предложит его создать. Собственно, ровно это происходит при установке WordPress.
После того, как файл конфигурации найден, он подключается. В нем указываются все важные константы, параметры подключения к базе данных и т.д. И затем подключается ядро - файл wp-settings.php.
Загрузка Фронт-энда (темы)
Любые Фронтэнд запросы отправляются в файл index.php в корневой папке домена. Это означает, что WordPress загружается с темой. Для констатации этого факта в index.php определяется константа WP_USE_THEMES. Так например хук template_redirect из файла template-loader.php будет работать только, если определена эта константа.
Процесс загрузки Фронта выглядит так:
index.php // определяется константа WP_USE_THEMES wp-blog-header.php // ЯДРО (описано выше) require_once dirname(__FILE__) . '/wp-load.php'; // Установка основного запроса WordPress. // Делает запрос и определяет какая страница загружается. // См. https://wp-kama.ru/function/wp wp(); // См. WP::main() // подключение нужного файла шаблона темы 'template-loader.php' require_once ABSPATH . WPINC . '/template-loader.php';
-
Подробнее про установку основного запроса и среду WP, читайте в описании функции wp().
- Логику работы файла template-loader.php (подключение файла темы) смотрите в: Иерархия шаблона.
Загрузка REST запроса
Build In PostREST запрос на 90% обрабатывается также как и запрос для фронтенда. Его также начинает обрабатывать файл index.php. Через правила перезаписи (ЧПУ) устанавливается параметр запроса rest_route=маршрут
.
^wp-json/?$ => index.php?rest_route=/ ^wp-json/(.*)? => index.php?rest_route=/$matches[1]
Процесс загрузки REST запроса выглядит так:
index.php // определяется константа WP_USE_THEMES wp-blog-header.php // ЯДРО (описано тут https://wp-kama.ru/handbook/wordpress/loading#wpcore-load) require_once dirname(__FILE__) . '/wp-load.php'; // в процессе загрузки ядра создается хук `parse_request` // который срабатывает в функции `wp()` add_action( 'parse_request', 'rest_api_loaded' ); // Установка основного запроса WordPress. // Делает запрос и определяет какая страница загружается. // В момент срабатывания хука `parse_request` управление передается // функции rest_api_loaded(), которая прерывает PHP через die. // См. https://wp-kama.ru/function/wp wp(); // См. WP::main()
Пояснения к работе wp() и хуку parse_request
Функция wp() вызывает метод WP::main(), он делает следующие действия по порядку:
-
Вызывает WP::init().
Этот метод устанавливает текущего пользователя для обычных (не REST) запросов. см. wp_get_current_user().
Позднее (если это REST запрос), на хуке аутентификации rest_authentication_errors авторизованный юзер будет обнулен при неправильном nonce коде. Смотрите:
-
Вызывает WP::parse_request().
Этот метод устанавливает параметры текущего запроса $wp->query_vars.
-
Запуск REST — rest_api_loaded().
В конце WP::parse_request() срабатывает хук parse_request на котором всегда висит функция rest_api_loaded():
add_action( 'parse_request', 'rest_api_loaded' );
rest_api_loaded() проверяет параметр запроса
$GLOBALS['wp']->query_vars['rest_route']
. Если это REST запрос, то запускается логика REST (логика обработки запросов для обычных страниц тут заканчивается).-
Устанавливается константа
define( 'REST_REQUEST', true )
. -
Запускается REST сервер rest_get_server().
При запуске срабатывает хук rest_api_init. На нём в имеющийся сервер добавляются свои маршруты.
-
Обрабатывается REST запрос WP_REST_Server::serve_request( $route ).
При обработке запроса проверяется доступность текущего маршрута, маршрут обрабатывается, возвращается ответ REST сервера в виде объекта WP_REST_Response.
При обработке запроса срабатывают следующие полезные хуки (в указанном порядке):
- Работа PHP прерывается через die().
-
Таким образом
За обработку REST запроса отвечает функция rest_api_loaded().
REST запрос выглядит точно также как обычный фронтэнд запрос. До момента установки основного запроса WordPress функцией wp(). В ней как и в обычном фронтэнд запросе устанавливается текущий юзер. Но:
- НЕ устанавливаются заголовки WP::send_headers().
- НЕ происходит основного запроса WP::query_posts().
- НЕ обрабатывается 404 страница WP::handle_404().
- НЕ вызывается файл template-loader.php, который определяет шаблон страницы.
- НЕ работает хук редиректов template_redirect.
Заголовки REST ответа ставит REST сервер, также он делает соответствующий запрос в БД или что-угодно еще.
Загрузка Админки
Админка WordPress никак не связана с «корневым» файлом index.php. Вся её загрузка начинается с файла wp-admin/admin.php.
Первое и главной что там происходит - это определяется константа WP_ADMIN, которая говорит всему коду который выполняется потом, что мы находится в админке.
wp-admin/admin.php // определяется константа WP_ADMIN define( 'WP_ADMIN', true ); // ЯДРО (описано выше) require_once dirname(dirname(__FILE__)) . '/wp-load.php'; // проверяется необходимость Апгрейда WordPress // подключаются файлы (дополнительные функции) админки require_once ABSPATH . 'wp-admin/includes/admin.php'; // Проверяет авторизован ли пользователь, перед тем как допустить его на любую страницу // если прав недостаточно, то юзер «улетит» на страницу авторизации auth_redirect(); // хук админки do_action( 'admin_init' ); // устанавливается данные текущей страницы set_current_screen(); // шапка require_once ABSPATH . 'wp-admin/admin-header.php'; // контент - тут логика разветвляется и контент загружается // либо от плагина // либо никакой (если это запрос импорта данных) // либо родные файлы адмники, например, wp-admin/options-general.php // подвал include ABSPATH . 'wp-admin/admin-footer.php';
Загрузка AJAX запроса
Отправлять AJAX запросы принято в файл wp-admin/admin-ajax.php, с него и начинается загрузка WordPress при AJAX запросах.
Давайте и тут разберем порядок загрузки, а затем немного пояснений.
wp-admin/admin.php // определяется константа WP_ADMIN define( 'WP_ADMIN', true ); // ЯДРО (все что есть в ВП) (описано выше) require_once dirname(dirname(__FILE__)) . '/wp-load.php'; // Проверяется наличие параметра запроса 'action' он должен быть определен для любого AJAX запроса. if( empty( $_REQUEST['action'] ) ) die( '0' ); // подключаются файлы (дополнительные функции) админки require_once ABSPATH . 'wp-admin/includes/admin.php'; // подключаются обработчики Ajax запросов ядра WordPress require_once ABSPATH . 'wp-admin/includes/ajax-actions.php'; // хук админки do_action( 'admin_init' ); // проверка и исправление значений параметра action связанных с ядром WordPress и добавление нужного хука add_action( 'wp_ajax_' . $_GET['action'], 'wp_ajax_' . str_replace( '-', '_', $_GET['action'] ), 1 ); // проверка прав пользователя и запуск нужного хука do_action( 'wp_ajax_' . $_REQUEST['action'] ); // или do_action( 'wp_ajax_nopriv_' . $_REQUEST['action'] ); // Ответ AJAX по умолчанию die( '0' );
При AJAX запросе также устанавливается константа WP_ADMIN. Это значит, что любой AJAX запрос выглядит для ядра, как запрос в админ часть, даже если этот запрос делается с фронта... Это не всегда нужно, но так принято в WordPress по причинам стандартизации и удобства использования.
Далее загружается ядро, в котором срабатывают все хуки. Т.е. подключаются и запускаются все плагины, срабатывает код файла functions.php, срабатывает событие init
. Т.е. полностью загружается и обрабатывается ядро WordPress.
Теперь, когда ядро обработано, дополнительно подключаются все PHP функции админ-панели (хотя часто они не нужны) и запускается один из AJAX хуков (зависит от авторизации): wp_ajax_(action)
или wp_ajax_nopriv_(action)
.
Запуск AJAX хука это последний этап - AJAX операция выполнена и она должна прервать работу PHP и вывести на экран какие-либо данные - обычно это либо просто строка, либо строка в json формате.
Кстати, для удобного json ответа при AJAX запросах в WP есть специальная функция - wp_json_encode() и две функции производные от нее:
- wp_send_json_error() - для ответов-ошибок.
- wp_send_json_success() - для успешных ответов.
Загрузка CRON запроса
WP с любой страницы отправляет запрос на файл /wp-cron.php. Он, в свою очередь:
- "Включает" константу DOING_CRON.
- Подгружает среду WordPress (файл /wp-load.php).
- Выполняет крон задачи.
Подробнее смотрите здесь.