无法捕获引发PHP错误的WooCommerce REST API请求,求解决方法
捕获引发WooCommerce REST API内部PHP错误的完整请求内容
问题背景
第三方应用通过WooCommerce REST API插入商品数据,但该应用不记录出站请求。此请求触发了PHP致命错误,错误日志如下:
[16-Jan-2023 14:52:23 UTC] PHP Fatal error: Uncaught TypeError: preg_match(): Argument #2 ($subject) must be of type string, WP_REST_Request given in /home/CENSORED/public_html/wp-includes/formatting.php:1596 Stack trace: #0 /home/CENSORED/public_html/wp-includes/formatting.php(1596): preg_match('/[\\x80-\\xff]/', Object(WP_REST_Request)) #1 /home/CENSORED/public_html/wp-includes/formatting.php(2207): remove_accents(Object(WP_REST_Request)) #2 /home/CENSORED/public_html/wp-includes/class-wp-hook.php(310): sanitize_title(Object(WP_REST_Request)) #3 /home/CENSORED/public_html/wp-includes/plugin.php(205): WP_Hook->apply_filters(Object(WP_REST_Request), Array) #4 /home/CENSORED/public_html/wp-includes/taxonomy.php(1751): apply_filters('pre_term_slug', Object(WP_REST_Request), 'pa_colour') #5 /home/CENSORED/public_html/wp-includes/taxonomy.php(1653): sanitize_term_field('slug', Object(WP_REST_Request), 0, 'pa_colour', 'db') #6 /home/CENSORED/public_html/wp-includes/taxonomy.php(2385): sanitize_term(Array, 'pa_colour', 'db') #7 /home/CENSORED/public_html/wp-content/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-terms-controller.php(407): wp_insert_term('DENIM BLUE', 'pa_colour', Array) #8 /home/CENSORED/public_html/wp-includes/rest-api/class-wp-rest-server.php(1171): WC_REST_Terms_Controller->create_item(Object(WP_REST_Request)) #9 /home/CENSORED/public_html/wp-includes/rest-api/class-wp-rest-server.php(1018): WP_REST_Server->respond_to_request(Object(WP_REST_Request), '/wc/v2/products...', Array, NULL) #10 /home/CENSORED/public_html/wp-includes/rest-api/class-wp-rest-server.php(442): WP_REST_Server->dispatch(Object(WP_REST_Request)) #11 /home/CENSORED/public_html/wp-includes/rest-api.php(410): WP_REST_Server->serve_request('/wc/v2/products...') #12 /home/CENSORED/public_html/wp-includes/class-wp-hook.php(308): rest_api_loaded(Object(WP)) #13 /home/CENSORED/public_html/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters(NULL, Array) #14 /home/CENSORED/public_html/wp-includes/plugin.php(565): WP_Hook->do_action(Array) #15 /home/CENSORED/public_html/wp-includes/class-wp.php(399): do_action_ref_array('parse_request', Array) #16 /home/CENSORED/public_html/wp-includes/class-wp.php(780): WP->parse_request('') #17 /home/CENSORED/public_html/wp-includes/functions.php(1332): WP->main('') #18 /home/CENSORED/public_html/wp-blog-header.php(16): wp() #19 /home/CENSORED/public_html/index.php(17): require('/home/CENSORED...') #20 {main} thrown in /home/CENSORED/public_html/wp-includes/formatting.php on line 1596
测试环境为全新最新版WordPress+WooCommerce,仅安装日志类插件,无其他第三方插件。已尝试以下方法但未捕获到目标请求:
- 查看Apache访问日志,无请求体内容
- 使用WP REST API Log插件,仅记录有效请求,遗漏触发错误的请求
- 使用Log HTTP Requests插件,未捕获到REST API请求
- 自定义代码记录请求,仅无效请求能写入error_log,触发致命错误的请求无记录,推测请求语法合法,错误为内部PHP Bug
解决方案
方法1:通过WordPress钩子提前记录请求
在当前主题的functions.php中添加以下代码,会在REST API处理逻辑启动前,将完整请求信息写入PHP error_log:
add_action( 'rest_api_init', function() { $request = WP_REST_Request::from_global(); $log_entry = sprintf( "[%s] REST API Request:\nMethod: %s\nURL: %s\nHeaders: %s\nBody: %s\nParams: %s\n", current_time( 'mysql' ), $request->get_method(), $request->get_url(), print_r( $request->get_headers(), true ), $request->get_body(), print_r( $request->get_params(), true ) ); error_log( $log_entry ); }, 1 ); // 设置最高优先级,确保在错误触发前执行
触发错误后,查看服务器的PHP error_log即可找到对应的完整请求数据。
方法2:修改PHP配置记录原始请求
编辑服务器的php.ini文件,添加或修改以下配置:
log_errors = On error_log = /var/log/php/error.log ; 替换为实际的日志路径 always_populate_raw_post_data = On
重启Apache或Nginx服务后,PHP会记录所有入站请求的原始数据,包括触发错误的请求内容。
方法3:服务器级日志配置捕获请求体
Apache配置
修改虚拟主机配置文件,添加自定义日志格式:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{Content-Type}i\" \"%POST_DATA\"" combined_with_body CustomLog /var/log/apache2/access.log combined_with_body ; 替换为实际日志路径
重启Apache后,访问日志会包含POST请求的完整内容。
Nginx配置
在Nginx的server块中添加自定义日志格式:
log_format full_request '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$request_body"'; access_log /var/log/nginx/access.log full_request ; 替换为实际日志路径
重启Nginx后,access.log会记录请求体内容。
内容的提问来源于stack exchange,提问作者Robbie Lewis
相关产品推荐
相关产品推荐

