Лучший способ завершения 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
@prosti Спасибо, я добавил абзац о том, когда и почему вы/WordPress можете использовать die вместо wp_die().
J.D.
Ценю ваши усилия, однако, я не понимаю, почему в ядре WordPress иногда используется die(), а иногда wp_die().
prosti
Спасибо @prosti. Что касается того, почему WordPress иногда использует die(), в некоторых случаях это просто устаревший код, или die() используется для аварийного завершения скрипта в крайнем случае, когда произошло что-то действительно непредвиденное и wp_die() не был вызван. В других случаях для части кода просто не были созданы тесты, и специальная обработка из wp_die() не требуется, поэтому это было упущено.
J.D.
Из кодекса 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 его не придерживается. Как насчет этого?
prosti
Все функции wp_send_json_* используют wp_send_json, который по-прежнему вызывает wp_die
Tunji
Но почему, я что-то упускаю. Вы проанализировали эти функции и пришли к таким выводам?
prosti
Вы также можете использовать 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
@prosti wp_send_json() делает некоторые вещи за нас. Этот вопрос также касается wp_send_json().
RRikesh
Именно поэтому @RRikesh я спрашиваю, использует ли WP core эту функцию. Так зачем это? Так лучше?
prosti
Для использования 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 и завершает выполнение за вас. Здесь есть путаница между клиентом и сервером.
Brian Fegter
@prosti тогда вам следует использовать wp_die($response), потому что, согласно кодексу: функция wp_die() предназначена для вывода данных непосредственно перед завершением работы, чтобы избежать пустых ответов или таймаутов.
Faisal Alvi
Спасибо, @FaisalAlvi, пожалуйста, [отредактируйте] ваш ответ, чтобы он выглядел привлекательно для голосов. Не оставляйте важные детали в комментариях.
prosti
@prosti Спасибо. Только что обновил ответ.
Faisal Alvi