chore(debug): 增加 ajax 调试工具定位 admin-ajax 400 "0" 来源

- assets/js/core/ajax-debug.js:ajaxSend 记录发往 admin-ajax 的
  action 与参数,ajaxError/fetch 记录失败请求的 url/status/响应
- inc/aksrt-ajax-debug.php:admin_init 时记录每个 admin-ajax 请求的
  action、是否有 wp_ajax_/wp_ajax_nopriv_ 处理器、POST 载荷至
  wp-content/aksrt-ajax-debug.log(handled=0 即 400 "0" 的来源)
- 开关:WP_DEBUG 或 URL 带 ?aksrt_ajax_debug=1(或常量 AKSRT_AJAX_DEBUG)
This commit is contained in:
2026-08-22 13:39:13 +08:00
parent 4d6fd641e5
commit 8d4f07656a
4 changed files with 167 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
<?php
/**
* AJAX Debug — 记录 admin-ajax 收到的每个请求,用于定位未知 action(WP 返回 400 "0")。
*
* 启用条件:WP_DEBUG 开启,或请求带 ?aksrt_ajax_debug=1
* 日志位置:wp-content/aksrt-ajax-debug.log
*
* 每行格式:
* [时间] action=xxx handled=0/1(nopriv) login=0/1 post={...} referer=...
* handled=0 表示 WP 没有任何 wp_ajax_{action} / wp_ajax_nopriv_{action} 处理器,
* 这种请求 admin-ajax 会直接 die('0') 并返回 400 —— 即控制台看到的报错来源。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_ajax_debug_enabled')) :
/**
* Whether ajax debugging is on for this request.
*
* @return bool
*/
function aksrt_ajax_debug_enabled(): bool {
if (defined('AKSRT_AJAX_DEBUG') && AKSRT_AJAX_DEBUG) {
return true;
}
if (defined('WP_DEBUG') && WP_DEBUG) {
return true;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- 调试开关,无副作用。
return isset($_GET['aksrt_ajax_debug']) && '1' === (string) wp_unslash($_GET['aksrt_ajax_debug']);
}
endif;
if (!function_exists('aksrt_ajax_debug_log')) :
/**
* Append a line to the ajax debug log.
*
* @param string $line Log line.
*/
function aksrt_ajax_debug_log(string $line): void {
$file = WP_CONTENT_DIR . '/aksrt-ajax-debug.log';
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- 调试日志。
@file_put_contents($file, '[' . current_time('Y-m-d H:i:s') . '] ' . $line . "\n", FILE_APPEND | LOCK_EX);
}
endif;
if (!function_exists('aksrt_ajax_debug_boot')) :
/**
* Hook the admin-ajax logger.
*/
function aksrt_ajax_debug_boot(): void {
if (!aksrt_ajax_debug_enabled()) {
return;
}
add_action('admin_init', static function (): void {
if (!defined('DOING_AJAX') || !DOING_AJAX) {
return;
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- 只读记录,不修改状态。
$action = isset($_REQUEST['action']) ? sanitize_key((string) wp_unslash($_REQUEST['action'])) : '';
$has_logged = $action !== '' && has_action('wp_ajax_' . $action);
$has_nopriv = $action !== '' && has_action('wp_ajax_nopriv_' . $action);
$payload = $_POST;
unset($payload['_wpnonce'], $payload['nonce'], $payload['_ajax_nonce']);
aksrt_ajax_debug_log(sprintf(
'action=%s handled=%d|%d login=%d post=%s referer=%s',
$action !== '' ? $action : '(empty)',
$has_logged ? 1 : 0,
$has_nopriv ? 1 : 0,
is_user_logged_in() ? 1 : 0,
wp_json_encode($payload, JSON_UNESCAPED_UNICODE),
isset($_SERVER['HTTP_REFERER']) ? (string) wp_unslash($_SERVER['HTTP_REFERER']) : ''
));
// phpcs:enable
}, 1);
}
endif;
aksrt_ajax_debug_boot();