如何避免自定义WooCommerce配送方法中calculate_shipping重复执行SOAP请求
问题分析
你遇到的核心问题是:自定义配送方法的calculate_shipping函数中,SOAP请求在WordPress环境下被重复执行,且单次请求耗时远高于SoapUI。结合现象来看,问题分为两部分:重复触发的SOAP请求和WordPress环境下的SOAP性能瓶颈。
解决方案
一、阻止SOAP请求重复执行
WooCommerce会在多个场景自动调用calculate_shipping(购物车更新、结账页加载、地址变更AJAX触发),必须通过缓存避免重复请求:
用WordPress Transient实现可靠缓存
Transient比会话变量更适配WooCommerce场景,自动处理缓存失效逻辑,缓存键需包含所有影响费率的参数(地址、购物车内容、重量等):
public function calculate_shipping( $package = array() ) { // 生成唯一缓存键:覆盖所有影响费率的变量 $cache_key = 'wc_custom_shipping_rates_' . md5( wp_json_encode( array( 'dest' => $package['destination'], 'cart_weight' => WC()->cart->get_cart_contents_weight(), 'cart_items' => wp_list_pluck( $package['contents'], 'product_id' ), 'cart_total' => WC()->cart->get_cart_total() ) ) ); // 优先读取缓存 $rates = get_transient( $cache_key ); if ( false === $rates ) { // 仅缓存失效时发起SOAP请求 $client = new SoapClient( '你的WSDL地址', [ 'connection_timeout' => 5, 'default_socket_timeout' => 10 ] ); $soapparameters = $this->build_soap_params( $package ); $result = $client->getRates( $soapparameters ); // 解析SOAP响应为WooCommerce费率格式 $rates = $this->parse_soap_result( $result ); // 缓存10分钟(可根据业务调整,比如地址不变时延长) set_transient( $cache_key, $rates, 10 * MINUTE_IN_SECONDS ); } // 添加费率到页面 foreach ( $rates as $rate ) { $this->add_rate( $rate ); } }
禁用不必要的AJAX触发
结账页默认会在地址字段变化时自动触发费率重算,可通过JS逻辑限制仅在地址真正变更时发起请求:
add_action( 'wp_footer', 'restrict_shipping_ajax_trigger' ); function restrict_shipping_ajax_trigger() { if ( !is_checkout() ) return; ?> <script> // 覆盖默认地址变更触发逻辑 jQuery(document).off('change', '#billing_country, #shipping_country, #billing_postcode'); jQuery(document).on('change', '#billing_country, #shipping_country, #billing_postcode', function() { const $el = jQuery(this); if ( $el.data('old-val') !== $el.val() ) { $el.data('old-val', $el.val()); jQuery('body').trigger('update_checkout'); } }); </script> <?php }
二、优化SOAP请求本身的性能
SoapUI中快但WordPress中慢,大概率是PHP SOAP扩展的配置问题:
强制设置超时时间
避免因默认超时过长导致的等待:
$client = new SoapClient( '你的WSDL地址', array( 'connection_timeout' => 5, // 连接超时5秒 'default_socket_timeout' => 10 // 整体请求超时10秒 ) );
调整WSDL缓存策略
如果第三方WSDL不频繁更新,开启磁盘缓存减少解析耗时:
// 代码中临时设置 ini_set( 'soap.wsdl_cache_enabled', 1 ); ini_set( 'soap.wsdl_cache_ttl', 86400 ); // 缓存WSDL一天 $client = new SoapClient( '你的WSDL地址', array( 'cache_wsdl' => WSDL_CACHE_DISK ) );
尝试用CURL替代原生SoapClient
原生SoapClient的底层实现可能存在性能瓶颈,手动用CURL发送SOAP请求:
function send_custom_soap_request( $wsdl_url, $soap_action, $params ) { // 手动构造SOAP XML信封 $soap_xml = '<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="你的命名空间"> <soapenv:Header/> <soapenv:Body> <ser:getRates>' . $this->params_to_xml( $params ) . '</ser:getRates> </soapenv:Body> </soapenv:Envelope>'; $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, $wsdl_url ); curl_setopt( $ch, CURLOPT_POST, true ); curl_setopt( $ch, CURLOPT_POSTFIELDS, $soap_xml ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Content-Type: text/xml; charset=utf-8', 'SOAPAction: "' . $soap_action . '"' ) ); curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 5 ); curl_setopt( $ch, CURLOPT_TIMEOUT, 10 ); $response = curl_exec( $ch ); curl_close( $ch ); // 解析XML响应为数组 $xml = simplexml_load_string( $response ); return json_decode( json_encode( $xml ), true ); }
三、服务器环境排查
测试服务器到第三方服务的网络延迟:
在服务器终端执行:curl -w "%{time_total}\n" -X POST -H "Content-Type: text/xml" --data @soap_request.xml 你的SOAP服务地址查看实际响应时间,确认是否是网络链路问题。
检查SSL配置:
如果第三方服务是HTTPS,服务器CA根证书缺失可能导致握手超时,测试时可临时关闭SSL验证(生产环境不建议):$client = new SoapClient( '你的WSDL地址', array( 'stream_context' => stream_context_create( array( 'ssl' => array( 'verify_peer' => false, 'verify_peer_name' => false ) ) ) ) );
内容的提问来源于stack exchange,提问作者CloDi
相关产品推荐
相关产品推荐

