WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru

Как загружается 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. Этим мы и займемся.

Порядок загрузки (теория)

Понять логику загрузки не так сложно, как может показаться на первый взгляд. Для этого давайте взглянем на эту, столь любимую мной, картинку:

Порядок загрузки ядра 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 (подключение файла темы) смотрите в: Иерархия шаблона.

Смотрите также картинку о том, как работает запрос WordPress:

Как работают функции запросов в WordPress

меню

Загрузка REST запроса

REST запрос на 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
		// ЯДРО (описано выше)
		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(), он в свою очередь работает так:

  1. Вызывает WP::init() - устанавливает текущего пользователя. См. wp_get_current_user(). Позднее, на хуке аутентификации rest_authentication_errors авторизованный юзер обнуляется при неправильном nonce коде см. rest_cookie_check_errors().
  2. Вызывает WP::parse_request() - устанавливает параметры запроса $wp->query_vars.
  3. В конце WP::parse_request() срабатывает хук parse_request, на котором вызывается функция rest_api_loaded().
  4. Устанавливается константа define( 'REST_REQUEST', true ).
  5. Инициализируется REST сервер rest_get_server() > WP_REST_Server, тут срабатывает хук rest_api_init. На нем регистрируются свои маршруты.
  6. Обрабатывается REST запрос, через метод: WP_REST_Server::serve_request( $route )
  7. Работа PHP обрывается через die() в конце функции rest_api_loaded().

Таким образом

За обработку 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() и две функции производные от нее:

меню

Занимательная картинка о том, как загружается WordPress

13 комментов
Полезные 1 Все
  • Приветствую. Вопрос такого рода. При AJAX запросах подключаются только файлы WordPress или же файлы плагинов тоже?

    Ответить1.5 года назад #
  • uptimizt github.com/uptimizt

    Все круто, но статья не полная без озвучки про WP JSON/REST API.
    До сих пор многие для AJAX используют admin ajax. Что тащит за собой кучу нагрузки, проблем и тормозов.

    Потому надо как можно больше сложных вычислений выносить в AJAX, но только не через админку, а через нативный REST API/WP JSON. и так можно решить 99% проблем с тормозами с заделом на будущее.

    Ошибка в том что вы AJAX ассоциируете с admin ajax. А это не так. Это не просто не точность, а дикая ошибка. Которая влечет то что джуны и мидлы в РФ в корне не верно понимают AJAX. Особенно вордпрессеры. Потому надо четко донести в статье о том что AJAX через admin только в админке. А на сайте это следует делать только через WP JSON/REST API. Вы ж библиотека русского ВордПресс. От вас зависит адекватность мышления людей. Давайте исправляться )

    Ответитьгод назад #
    • Kama7602

      Аякс это аякс, рест это рест, не надо путать это не одно и тоже, хоть и похоже и не редко взаимозаменяемо. В ВП AJAX - это admin ajax, только он может правильно найти конфиг файл, и именно для аякс запросов он и был создан (вместо него, при желании можно создать свой аналогичный файл без админ части.)! И он не тащит кучу нагрузки, без реальных тестов не поверю в это, чуть больше да, но никак не кучу!

      Кое где добавлю в статью пояснения насчет аякса, спс за коммент.

      REST API/WP JSON - это фронт и грузятся они также как и фронт, с минимальной разницей, там и правила ЧПУ срабатывают и все прочее, но детальнее это расписать пожалуй все же нужно, спс.

      От вас зависит адекватность мышления людей. Давайте исправляться )

      Не надо на меня обязанности и ответственности вешать, на ровном месте, я и так расписал так как никто не расписал. Адекватность людей зависит от людей, я тут не при чем...

      2
      Ответитьгод назад #
      • uptimizt github.com/uptimizt

        Аякс это аякс, рест это рест, не надо путать это не одно и тоже, хоть и похоже и не редко взаимозаменяемо.

        я нигде не утверждал что AJAX это REST. AJAX - это асинхронные запросы. REST - это протокол исполнения запросов.

        речь лишь о том что на фронте в 99% случаев правильно AJAX делать через REST API. но никак не черед admin ajax.

        Ответитьгод назад #
      • uptimizt github.com/uptimizt

        Извини если оскорбил и нагрузил ответственностью )

        Я хотел добавить что очень важно отразить 4ю группу нагрузки. У тебя их 3 - есть фронт, админка и AJAX (подразумевается AdminAjax). Но на самом деле важна именно 4я группа нагрузки - REST API.

        Потому в ядре есть 4 основных пути запросов и структуры хуков:

        • WP Admin
        • Admin Ajax - AJAX для админки
        • базовый init
        • REST API - AJAX для фронта

        При этом все современные практики веб разработки опираются именно на 4й механизм. будь то реактивный фронт, SPA или Mobile разработка. Там же скрывается огромный пласт оптимизации для больших проектов.

        Учитывая важность этого механизма, я предлагаю добавить его как отдельный пункт и добавить туда эти описания. Чтобы дать более полное представление о том как работают современные механики WP.

        У него кстати и своя структура и последовательность хуков.

        Ответитьгод назад #
        • Kama7602

          Я хотел добавить что очень важно отразить 4ю группу нагрузки.

          Да согласен полностью, добавил REST загрузку в статью.

          Ответить8 мес назад #
    • @ mihdan415 www.kobzarev.com

      Анатолий, тут надо сразу давать ссылку на твой последний пост "Шизокод и шизокодеры". От Тимура не зависит адекватность людей, если они шизокодеры или говнокодеры, уж простите, но из статьи слов не выкинешь mosking

      Рест появился значительно недавно, есть куча учебником и мануалов, где про него ни слова.

      И некоторые задачи все же надо решать при помощи админ аякс, а не реста.

      Вещи похожие, но применение разное.

      2
      Ответитьгод назад #
      • uptimizt github.com/uptimizt

        И некоторые задачи все же надо решать при помощи админ аякс, а не реста.

        например? что за задачи на фронте надо решать через админ?

        admin ajax - был придуман для работы аякс в админке. там и должен использоваться.

        то что его по не знанию начали тащить на фронт - издержки шаблонного мышления.

        ajax на фронте грамотные разработчики всегда делали через свои эндпоинты. например см как работает с AJAX тот же WooCommerce. он никогда не лезет в админ аякс.
        https://github.com/woocommerce/woocommerce/blob/master/includes/class-wc-ajax.php

        просто с приходом REST API - оно стало более понятно для джунов.

        Ответитьгод назад #
        • @ mihdan415 www.kobzarev.com

          REST API появился прям совсем недавно, жили же мы без него, используя admin-ajax.php как написано в Кодексе, и горя не знали.

          Мы реализуем через сердцебиение WP получение в реальном времени уведомлений для юзера в одном крупном проекте, он на старой версии ядра и обновлять мы его не собираемся по ряду причин, так что REST API нам там и не светитmosking

          Ответитьгод назад #
          • uptimizt github.com/uptimizt

            Да, ты прав. Я не разбирался как работает Hearbeat API и не использовал его на фронте. Только в админке.

            В этой части интересно узнать как у BuddyPress реализован похожий механизм сообщений в чате и уведомлений в панели.

            Если там тоже Hearbeat API то тут 4 варианта:

            • либо там разработчики поленились все написать на свой REST API
            • либо ждали тот что выйдет в WP из коробки
            • либо не знали что есть альтернатива админскому аяксу
            • либо в BP считается нормально тащить AJAX из админки на фронт (альтернатива подхода команды WooCommerce)
            1
            Ответитьгод назад #
            • @ mihdan415 www.kobzarev.com

              Раньше в бадипрессе так и было, сейчас не могу сказать - уже пару лет ничего не приходилось на нем делать

              Ответитьгод назад #
Здравствуйте, !     Войти . Зарегистрироваться