You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免自定义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 );
}

三、服务器环境排查

  1. 测试服务器到第三方服务的网络延迟:
    在服务器终端执行:

    curl -w "%{time_total}\n" -X POST -H "Content-Type: text/xml" --data @soap_request.xml 你的SOAP服务地址
    

    查看实际响应时间,确认是否是网络链路问题。

  2. 检查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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:59:56