Лучший способ завершения WordPress ajax запроса и почему?
Рассматривая стандартные WordPress ajax запросы, такие как:
add_action( 'wp_ajax_merrychristmas_happynewyear', array( $this, 'merrychristmas_happynewyear' ) );
add_action( 'wp_ajax_nopriv_merrychristmas_happynewyear', array( $this, 'merrychristmas_happynewyear' ) );
Как лучше всего завершить функцию merrychristmas_happynewyear
- используя die()
, die(0)
, wp_die()
, или что-то другое, и почему?

Использование wp_die()
является лучшим из предложенных вариантов.
Как уже отмечали другие, есть много причин предпочесть специфичную для WordPress функцию обычным die
или exit
:
- Она позволяет другим плагинам подключаться к действиям, вызываемым
wp_die()
. - Она позволяет использовать специальный обработчик завершения в зависимости от контекста (поведение
wp_die()
адаптируется в зависимости от того, является ли запрос Ajax-запросом или нет). - Это делает возможным тестирование вашего кода.
Последний пункт особенно важен, поэтому я добавил эту заметку в Codex. Если вы хотите создавать модульные/интеграционные тесты для своего кода, вы не сможете протестировать функцию, которая вызывает exit
или die
напрямую. Она завершит выполнение скрипта, как и должна. Способ, которым WordPress обходит это в своих тестах (для Ajax-обработчиков, которые тестируются), заключается в подключении к действиям, запускаемым wp_die()
, и генерации исключения. Это позволяет перехватить исключение в тесте и проанализировать вывод обработчика (если он есть).
Единственный случай, когда стоит использовать die
или exit
, — это если вы хотите обойти специальную обработку wp_die()
и немедленно завершить выполнение. В WordPress есть несколько мест, где это делается (а также места, где может использоваться die
напрямую просто потому, что обработка через wp_die()
не важна или никто не пытался создать тесты для этого кода, поэтому это упустили). Помните, что это также усложняет тестирование вашего кода, поэтому обычно такое используется только в коде, который не находится в теле функции (как это делает WordPress в admin-ajax.php
). Таким образом, если обработка через wp_die()
явно нежелательна или вы завершаете скрипт в определённом месте в качестве меры предосторожности (как делает admin-ajax.php
, предполагая, что Ajax-обработчик уже корректно завершил выполнение), то можно рассмотреть использование die
напрямую.
Что касается выбора между wp_die()
и wp_die( 0 )
, то это зависит от того, как обрабатывается ответ на этот Ajax-запрос на фронтенде. Если ожидается определённое тело ответа, то вам нужно передать это сообщение (или число, в данном случае) в wp_die()
. Если важно только успешное завершение запроса (код ответа 200
или другой), то передавать что-либо в wp_die()
не нужно. Однако стоит отметить, что завершение с wp_die( 0 )
сделает ответ неотличимым от стандартного ответа admin-ajax.php
. Таким образом, завершение с 0
не покажет, был ли ваш обработчик правильно подключён и действительно выполнился. Лучше использовать другое сообщение.
Как уже отмечалось в других ответах, часто полезными оказываются функции wp_send_json()
и подобные, если вы отправляете JSON-ответ, что обычно является хорошей практикой. Это также лучше, чем просто вызывать wp_die()
с кодом, потому что вы можете передать гораздо больше информации в JSON-объекте, если это необходимо. Использование wp_send_json_success()
и wp_send_json_error()
также отправляет сообщение об успехе/ошибке в стандартном формате, который смогут понять JS-вспомогательные функции WordPress для Ajax (например, wp.ajax
).
Коротко: Вероятно, вам всегда следует использовать wp_die()
, независимо от того, является ли это Ajax-обработчиком или нет. Ещё лучше — отправлять информацию с помощью wp_send_json()
и подобных функций.

Вы добавили несколько хороших точек зрения. Я обновил тему своими мыслями. Можете оставить комментарий, если хотите. @J.D

@prosti Спасибо, я добавил абзац о том, когда и почему вы/WordPress можете использовать die
вместо wp_die()
.

Ценю ваши усилия, однако, я не понимаю, почему в ядре WordPress иногда используется die()
, а иногда wp_die()
.

Спасибо @prosti. Что касается того, почему WordPress иногда использует die()
, в некоторых случаях это просто устаревший код, или die()
используется для аварийного завершения скрипта в крайнем случае, когда произошло что-то действительно непредвиденное и wp_die()
не был вызван. В других случаях для части кода просто не были созданы тесты, и специальная обработка из wp_die()
не требуется, поэтому это было упущено.

Из кодекса AJAX в плагинах
add_action( 'wp_ajax_my_action', 'my_action_callback' );
function my_action_callback() {
global $wpdb; // получаем доступ к базе данных
$whatever = intval( $_POST['whatever'] );
$whatever += 10;
echo $whatever;
wp_die(); // обязательно для немедленного завершения и возврата корректного ответа
}
Обратите внимание на использование
wp_die()
вместоdie()
илиexit()
. В большинстве случаев в функции обратного вызова Ajax следует использовать именноwp_die()
. Это обеспечивает лучшую интеграцию с WordPress и упрощает тестирование кода.

Упомянутый вами ccodex отличный, но WordPress core его не придерживается. Как насчет этого?

Все функции wp_send_json_*
используют wp_send_json
, который по-прежнему вызывает wp_die

Но почему, я что-то упускаю. Вы проанализировали эти функции и пришли к таким выводам?

Вы также можете использовать wp_send_json()
, описанную в Codex как отправить JSON-ответ на AJAX-запрос и завершить выполнение (die()).
Таким образом, если вам нужно вернуть массив, просто завершите свою функцию вызовом wp_send_json($array_with_values);
. Нет необходимости использовать echo
или die
.
Также доступны две вспомогательные функции: wp_send_json_success()
и wp_send_json_error()
, которые добавляют ключ success
со значением true
или false
соответственно.
Например:
$array_val = range( 1,10 );
var_dump( wp_send_json_error( $array_val ) ); # Вывод: {"success":false,"data":[1,2,3,4,5,6,7,8,9,10]}
echo 'Привет'; # Не выполнится, так как выполнение уже завершено.

Функция wp_json_encode
в случае исключения может вернуть false. Что делать в таком случае?

@prosti wp_send_json()
делает некоторые вещи за нас. Этот вопрос также касается wp_send_json()
.

Именно поэтому @RRikesh я спрашиваю, использует ли WP core эту функцию. Так зачем это? Так лучше?

Для использования AJAX в WordPress / WooCommerce общий синтаксис следующий:
add_action( 'wp_ajax_my_action', 'my_action_callback' );
add_action( 'wp_ajax_nopriv_my_action', 'my_action_callback' );
function my_action_callback()
{
// ваш код здесь
wp_die();
}
В конце функции следует использовать wp_die(). Потому что WordPress внутренне использует фильтр во время выполнения функции wp_die(). Поэтому любой плагин, который работает с этим фильтром, может не работать, если мы не включим wp_die(). Также функции die() и другие немедленно завершают выполнение PHP без учета каких-либо функций WordPress, которые должны быть учтены при завершении выполнения.
Если вы используете wp_send_json() внутри функции, как здесь:
function my_action_callback()
{
// ваш код здесь
wp_send_json();
//wp_die(); не обязательно использовать wp_die();
}
Необязательно использовать wp_die() в конце, если вы включаете wp_send_json() внутри callback-функции, потому что WordPress сам безопасно использует функцию wp_die() внутри функции wp_send_json().

Это дополнение к тому, что уже сказали другие. Причина, по которой стоит предпочесть wp_die
, заключается в том, что ядро WordPress может запускать действия в этом месте, а плагины могут корректно завершать такие вещи, как трассировка, мониторинг или кеширование.
В целом, если доступен вызов API ядра, всегда стоит предпочесть его, так как он, скорее всего, добавляет какую-то ценность (кеширование, интеграция с плагинами или что-то ещё), чего вы не получите при прямом вызове PHP-функции.

Я не приму этот ответ, это было бы несправедливо. Я просто хотел создать структуру и возможные подсказки по пунктам, которые считаю важными:
Основное определение wp-die()
Файл: wp-includes/functions.php
2607: /**
2608: * Прерывает выполнение WordPress и отображает HTML-сообщение с ошибкой.
2609: *
2610: * Эта функция дополняет PHP-функцию `die()`. Разница в том, что
2611: * пользователю будет отображён HTML. Рекомендуется использовать эту функцию
2612: * только когда выполнение не должно продолжаться дальше. Не рекомендуется
2613: * вызывать эту функцию часто, по возможности обрабатывайте ошибки
2614: * "тихо" или более изящно.
2615: *
2616: * В качестве сокращения, желаемый HTTP-код ответа может быть передан как целое число
2617: * в параметр `$title` (будет применён заголовок по умолчанию) или в параметр `$args`.
2618: *
2619: * @since 2.0.4
2620: * @since 4.1.0 Параметры `$title` и `$args` были изменены для опционального приёма
2621: * целого числа в качестве кода ответа.
2622: *
2623: * @param string|WP_Error $message Опционально. Сообщение об ошибке. Если это объект WP_Error,
2624: * и запрос не Ajax или XML-RPC, используются сообщения ошибки.
2625: * По умолчанию пусто.
2626: * @param string|int $title Опционально. Заголовок ошибки. Если `$message` это `WP_Error`,
2627: * данные ошибки с ключом 'title' могут использоваться для указания заголовка.
2628: * Если `$title` это число, оно трактуется как код
2629: * ответа. По умолчанию пусто.
2630: * @param string|array|int $args {
2631: * Опционально. Аргументы для управления поведением. Если `$args` это число, оно трактуется
2632: * как код ответа. По умолчанию пустой массив.
2633: *
2634: * @type int $response HTTP-код ответа. По умолчанию 200 для Ajax-запросов, иначе 500.
2635: * @type bool $back_link Включать ли ссылку "назад". По умолчанию false.
2636: * @type string $text_direction Направление текста. Полезно только внутренне, когда WordPress
2637: * ещё загружается и локаль сайта не установлена. Принимает 'rtl'.
2638: * По умолчанию значение is_rtl().
2639: * }
2640: */
2641: function wp_die( $message = '', $title = '', $args = array() ) {
2642:
2643: if ( is_int( $args ) ) {
2644: $args = array( 'response' => $args );
2645: } elseif ( is_int( $title ) ) {
2646: $args = array( 'response' => $title );
2647: $title = '';
2648: }
2649:
2650: if ( wp_doing_ajax() ) {
2651: /**
2652: * Фильтрует callback для прерывания выполнения WordPress для Ajax-запросов.
2653: *
2654: * @since 3.4.0
2655: *
2656: * @param callable $function Имя callback-функции.
2657: */
2658: $function = apply_filters( 'wp_die_ajax_handler', '_ajax_wp_die_handler' );
2659: } elseif ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
2660: /**
2661: * Фильтрует callback для прерывания выполнения WordPress для XML-RPC запросов.
2662: *
2663: * @since 3.4.0
2664: *
2665: * @param callable $function Имя callback-функции.
2666: */
2667: $function = apply_filters( 'wp_die_xmlrpc_handler', '_xmlrpc_wp_die_handler' );
2668: } else {
2669: /**
2670: * Фильтрует callback для прерывания выполнения WordPress для всех не-Ajax, не-XML-RPC запросов.
2671: *
2672: * @since 3.0.0
2673: *
2674: * @param callable $function Имя callback-функции.
2675: */
2676: $function = apply_filters( 'wp_die_handler', '_default_wp_die_handler' );
2677: }
2678:
2679: call_user_func( $function, $message, $title, $args );
2680: }
wp_send_json
Файл: wp-includes/functions.php
3144: /**
3145: * Отправляет JSON-ответ на Ajax-запрос.
3146: *
3147: * @since 3.5.0
3148: * @since 4.7.0 Добавлен параметр `$status_code`.
3149: *
3150: * @param mixed $response Переменная (обычно массив или объект) для кодирования в JSON,
3151: * затем вывод и завершение.
3152: * @param int $status_code HTTP-код статуса для вывода.
3153: */
3154: function wp_send_json( $response, $status_code = null ) {
3155: @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
3156: if ( null !== $status_code ) {
3157: status_header( $status_code );
3158: }
3159: echo wp_json_encode( $response );
3160:
3161: if ( wp_doing_ajax() ) {
3162: wp_die( '', '', array(
3163: 'response' => null,
3164: ) );
3165: } else {
3166: die;
3167: }
3168: }
wp_doing_ajax
Файл: wp-includes/load.php
1044: /**
1045: * Определяет, является ли текущий запрос WordPress Ajax-запросом.
1046: *
1047: * @since 4.7.0
1048: *
1049: * @return bool True, если это WordPress Ajax-запрос, иначе false.
1050: */
1051: function wp_doing_ajax() {
1052: /**
1053: * Фильтрует, является ли текущий запрос WordPress Ajax-запросом.
1054: *
1055: * @since 4.7.0
1056: *
1057: * @param bool $wp_doing_ajax Является ли текущий запрос WordPress Ajax-запросом.
1058: */
1059: return apply_filters( 'wp_doing_ajax', defined( 'DOING_AJAX' ) && DOING_AJAX );
1060: }
Обычно мы получаем от ajax-запроса какой-то ответ. Ответ может быть закодирован в json или может быть не закодирован в json.
Если нам нужен вывод в формате json
, то wp_send_json
или два сателлита — отличная идея.
Однако мы можем вернуть x-www-form-urlencoded
, multipart/form-data
, text/xml
или любой другой тип кодировки. В этом случае мы не используем wp_send_json
.
Мы можем вернуть целый HTML, и в этом случае имеет смысл использовать wp_die()
с первым и вторым параметром, иначе эти параметры должны быть пустыми.
wp_die( '', '', array(
'response' => null,
) );
Но в чём преимущество вызова wp_die()
без параметров?
Наконец, если вы проверите отличное ядро WP, вы можете найти
Файл: wp-includes/class-wp-ajax-response.php
139: /**
140: * Отображает ответы в формате XML.
141: *
142: * Устанавливает заголовок Content-Type в text/xml.
143: *
144: * @since 2.1.0
145: */
146: public function send() {
147: header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ) );
148: echo "<?xml version='1.0' encoding='" . get_option( 'blog_charset' ) . "' standalone='yes'?><wp_ajax>";
149: foreach ( (array) $this->responses as $response )
150: echo $response;
151: echo '</wp_ajax>';
152: if ( wp_doing_ajax() )
153: wp_die();
154: else
155: die();
Используются оба формата: die()
и wp_die()
. Можете объяснить почему?
Наконец, вот что возвращает admin-ajax.php
: die( '0' );
Почему не wp_die(...)
?

Если вы используете echo
, это заставит вас использовать die()
, die(0)
или wp_die()
.
Если вы не используете echo
, JavaScript может обработать это.
Тогда вам следует использовать более правильный способ возврата данных: wp_send_json()
.
Чтобы отправить данные в вашем колбэке (в формате json
), вы можете использовать следующие функции:
wp_send_json()
wp_send_json_success()
wp_send_json_error()
Все они завершат выполнение за вас. Не нужно вызывать exit или die после них.
ОБНОВЛЕНИЕ
И если вам не нужен json
в качестве формата вывода, используйте:
wp_die($response)
Он вернёт ваш ответ перед завершением работы. Как указано в кодексе:
Функция
wp_die()
предназначена для вывода данных перед завершением, чтобы избежать пустых или зависающих ответов.
Прочтите полную статью в кодексе здесь.

Стоит отметить, что Javascript не работает с echo
. wp_send_json_*
использует echo
и завершает выполнение за вас. Здесь есть путаница между клиентом и сервером.

@prosti тогда вам следует использовать wp_die($response), потому что, согласно кодексу: функция wp_die() предназначена для вывода данных непосредственно перед завершением работы, чтобы избежать пустых ответов или таймаутов.

Спасибо, @FaisalAlvi, пожалуйста, [отредактируйте] ваш ответ, чтобы он выглядел привлекательно для голосов. Не оставляйте важные детали в комментариях.

@prosti Спасибо. Только что обновил ответ.
