如何将 SOAP 调用交换为 cURL,以在 allow_url_fopen 限制内工作?

Posted

技术标签:

【中文标题】如何将 SOAP 调用交换为 cURL,以在 allow_url_fopen 限制内工作?【英文标题】:How to swap SOAP calls to cURL, to work within allow_url_fopen limitation? 【发布时间】:2015-06-24 01:31:55 【问题描述】:

我最近遇到了一个问题,即对 ID3Global/address 服务的 SOAP 调用突然停止工作(它们以前工作正常)。我认为这与托管服务提供商在我们的服务器上关闭了allow_url_fopen 有关,这意味着该服务现在无法正常工作。

有人告诉我,我需要切换到使用 cURL 来获取文件 (WSDL),因为 file_get_contents 需要在 php.ini 文件中设置“allow_url_fopen”才能正常工作。但是我似乎没有在我的文件中使用file_get_contents 来获取 WSDL 文件。

如何切换到使用 cURL?

这是我进行 SOAP 地址调用的 PHP 文件:

<?php
ini_set("soap.wsdl_cache_enabled", "0");

$username = 'xxxxxxx@xxxxxxxx.com';
$password = 'xxxxxxx';

$profile_id = 'xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx';

// Live WSDL
$wsdl = 'https://id3global.com/ID3gWS/ID3global.svc?wsdl';

$postcode = $_POST['ZipPostcode'];

/**
 * Method to arrange the address into a sane
 * order for displaying back to the user
 *
 * @param $item
 * @param $key
 * @internal param $address
 */
function sortAddress(&$item, $key)

    // Convert the object to an array
    $address = (array) $item;

    // Reorder the address lines
    $addressLines = array(
        'Company' => $address['Company'],
        'Building' => $address['Building'],
        'SubBuilding' => $address['SubBuilding'],
        'Premise' => $address['Premise'],
        'SubStreet' => $address['SubStreet'],
        'Street' => $address['Street'],
        'City' => $address['City'],
        'StateDistrict' => $address['StateDistrict'],
        'ZipPostcode' => $address['ZipPostcode'],
        'Country' => $address['Country'],
    );

   // Remove blank address lines
   // $item = array_filter($addressLines);
   $item = $addressLines;



class clsWSSEAuth  
    private $Username; 
    private $Password;  
    function __construct($username, $password)  
        $this->Username=$username; 
        $this->Password=$password; 
     
 

class clsWSSEToken  
    private $UsernameToken; 
    function __construct ($UsernameToken) 
        $this->UsernameToken = $UsernameToken; 
     
 

$strWSSENS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; 

//Auth
$objSoapVarUser = new SoapVar($username, XSD_STRING, NULL, $strWSSENS, NULL, $strWSSENS); 
$objSoapVarPass = new SoapVar($password, XSD_STRING, NULL, $strWSSENS, NULL, $strWSSENS); 
$objWSSEAuth = new clsWSSEAuth($objSoapVarUser, $objSoapVarPass); 

//Token
$objSoapVarWSSEToken = new SoapVar($objWSSEAuth, SOAP_ENC_OBJECT, NULL, $strWSSENS, 'UsernameToken', $strWSSENS); 
$objWSSEToken = new clsWSSEToken($objSoapVarWSSEToken); 

//Header
$objSoapVarWSSEAuth = new SoapVar($objWSSEToken, SOAP_ENC_OBJECT, NULL, $strWSSENS, 'UsernameToken', $strWSSENS); 
$objSoapVarHeaderVal = new SoapVar($objSoapVarWSSEAuth, SOAP_ENC_OBJECT, NULL, $strWSSENS, 'Security', $strWSSENS); 
$objSoapVarWSSEHeader = new SoapHeader($strWSSENS, 'Security', $objSoapVarHeaderVal, true); 

//Client
$client = new SoapClient($wsdl, array(
    'soap_version' => SOAP_1_1, 
    'trace' => 1, 
    'exception' => true, 
)); 

$client->__setSoapHeaders($objSoapVarWSSEHeader);

$results = $client->AddressLookup(array(
    'InputData' => array('ZipPostcode' => strtoupper($postcode)),
));

$addresses = $results->AddressLookupResult->GlobalAddress;


array_walk($addresses, 'sortAddress');
//var_dump($addresses);

echo json_encode( $addresses );

这是使用 AJAX 触发的,这里是 javascript/jQuery 文件:

jQuery(document).ready(function($) 

    var addresses = [];

    $("body").on('click', '.find-address', function(e)
        e.preventDefault();
        url = '/wp-content/themes/Cornhill/gbgroup-address-lookup_2.php';
        postode_id = $(this).data('postcode');
        address_id = $(this).data('address');
        postcode = $('#'+postode_id).val();
        console.log('Stage 1');

        if (postcode != '')
        
            var addressList = $('#'+address_id);
            addressList.find('option').remove();
            opt = $('<option>').html('Loading...');
            opt.val('');
            addressList.append(opt);
            console.log('stage 2');

            $.ajax(
                url: url,
                dataType: 'json',
                type: 'post',
                data: 
                    'ZipPostcode': postcode
                ,
                success: function(response)
                    addressList.find('option').remove();
                    addresses[address_id] = response;

                    opt = $('<option>').html('Please select');
                    opt.val('');
                    addressList.append(opt);

                    for(x=0; x<addresses[address_id].length; x++)

                        addressArray = new Array();
                        addressArray.push(addresses[address_id][x].Building);
                        addressArray.push(addresses[address_id][x].Street);
                        addressArray.push(addresses[address_id][x].City);
                        addressArray.push(addresses[address_id][x].ZipPostcode);

                        addressString = addressArray.join(', ');

                        opt = $('<option>').attr('value', x);
                        opt.html(addressString);

                        addressList.append(opt);
                    
                
            );
        
        else
        
            return;
        
    );

    $("body").on('change', '.select-address', function()
        address_id = $(this).attr('id');
        street_id = $(this).data('street');
        town_id = $(this).data('town');
        postcode_id = $(this).data('postcode');

        value = $(this).val();
        if(value != '')
            address = addresses[address_id][value];

            if (address.Building != '')
            
                $('#'+street_id).val(address.Building+' '+address.Street);
            
            else
            
                $('#'+street_id).val(address.Street);
            
            $('#'+town_id).val(address.City);
            $('#'+postcode_id).val(address.ZipPostcode);
        

    );

);

我之前曾尝试使用以下代码进行切换以获取 WSDL 文件,但我不确定我还应该用它做什么:

$ch =  curl_init('https://id3global.com/ID3gWS/ID3global.svc?wsdl');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$resultSuper = curl_exec($ch);

【问题讨论】:

如果您的主机做出如此巨大的更改而没有提前警告并让您在新服务器上进行测试,我会考虑将更改主机作为一个重要的优先事项。 谢谢,我们希望尽快做出改变,但同时我们需要让这项服务再次运行。你对如何在那个 gbgroup-address-lookup_2.php 文件中使用 cURL 有什么建议吗? (我已将您的代码移至您的问题中 - 我们倾向于不鼓励在此处使用外部粘贴板,因为链接有时会断开,我们希望问题能持续)。 This might help - 请参阅“meltir atmeltir dot com”的评论。扩展SoapClient 并实现callCurl。您可能需要对其进行调整,因为您可能不需要代理功能。 您可以从命令行使用 curl 下载远程 WSDL 一次,然后将其保存到项目的文件夹中。然后将本地文件名和路径传递给 SoapClient,而不是以“http://”开头的东西。 【参考方案1】:

参数allow_url_fopen 对SOAP 的工作方式没有影响。您可以使用以下脚本轻松测试:

<?php

echo "allow_url_fopen status is: " . ini_get('allow_url_fopen') . "\n";

$wsdl = 'https://id3global.com/ID3gWS/ID3global.svc?wsdl';

file_get_contents($wsdl);

$client = new SoapClient($wsdl, array(
    'soap_version' => SOAP_1_1,
    'trace' => 1,
    'cache_wsdl' => WSDL_CACHE_NONE, // this is important for the purpose of the test
    'exception' => true,
));

print_r($client);

?>

启用 allow_url_fopen 后,您将看到以下输出:

allow_url_fopen status is: 1 SoapClient Object ( [trace] => 1 [_soap_version] => 1 [sdl] => Resource id #11 )

当allow_url_fopen 被禁用时,您将看到以下输出:

allow_url_fopen status is: 0 
Warning: file_get_contents(): https:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /var/www/test.php on line 9

Warning: file_get_contents(https://id3global.com/ID3gWS/ID3global.svc?wsdl): failed to open stream: no suitable wrapper could be found in /var/www/test.php on line 9
SoapClient Object ( [trace] => 1 [_soap_version] => 1 [sdl] => Resource id #10 )

请注意,没有报告任何 SOAP 错误。

这种行为的原因是 PHP 源文件 ext/soap/php_xml.c 中的以下代码:

old_allow_url_fopen = PG(allow_url_fopen);
PG(allow_url_fopen) = 1;
ctxt = xmlCreateFileParserCtxt(filename);
PG(allow_url_fopen) = old_allow_url_fopen;

因此,为 WSDL 下载启用了 allow_url_fopen。如果您将这些行注释如下:

/* old_allow_url_fopen = PG(allow_url_fopen);
PG(allow_url_fopen) = 1; */
ctxt = xmlCreateFileParserCtxt(filename);
/* PG(allow_url_fopen) = old_allow_url_fopen; */

并使用更改的源代码编译 PHP,您将看到以下结果:

启用allow_url_fopen:

allow_url_fopen status is: 1 SoapClient Object ( [trace] => 1 [_soap_version] => 1 [sdl] => Resource id #11 )

禁用allow_url_fopen:

allow_url_fopen status is: 0 
Warning: file_get_contents(): https:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /var/www/test.php on line 9

Warning: file_get_contents(https://id3global.com/ID3gWS/ID3global.svc?wsdl): failed to open stream: no suitable wrapper could be found in /var/www/test.php on line 9

Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Couldn't load from 'https://id3global.com/ID3gWS/ID3global.svc?wsdl' : failed to load external entity "https://id3global.com/ID3gWS/ID3global.svc?wsdl" in /var/www/test.php:16 Stack trace: #0 /var/www/test.php(16): SoapClient->SoapClient('https://id3glob...', Array) #1 main thrown in /var/www/test.php on line 16

您可以看到这次我们遇到了致命的 SOAP 错误并且无法加载 WSDL。我在 PHP 5.4.40 和 PHP 5.6.8 中观察到了这种行为。

【讨论】:

以上是关于如何将 SOAP 调用交换为 cURL,以在 allow_url_fopen 限制内工作?的主要内容,如果未能解决你的问题,请参考以下文章

SOAP协议介绍

如何动态启用 SOAP、CURL、OPENSSL 扩展?

如何将 shell 设置为 bash 以在 Capistrano 中运行?

创建 Restful Web 服务以在 C# 中调用存储过程

php下调用soap实现对接

设置 Curl 命令以在 Zapier Webhook 中运行