如何将 SOAP 调用交换到 cURL,以在 allow_url_fopen 限制内工作?
How to swap SOAP calls to cURL, to work within allow_url_fopen limitation?
我最近遇到了一个问题,即对 ID3Global/address 服务的 SOAP 调用突然停止工作(它们以前工作正常)。我认为这与托管服务提供商在我们的服务器上关闭 allow_url_fopen
有关,这意味着该服务现在无法正常工作。
我被告知我需要切换到使用 cURL 来抓取文件 (WSDL),因为 file_get_contents
需要在 php.ini 文件中设置 'allow_url_fopen'这个工作。但是我似乎没有在我的文件中使用 file_get_contents
来获取 WSDL 文件。
如何切换到使用 cURL?
这是我的 PHP 调用 SOAP 地址的文件:
<?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);
参数 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;
因此,allow_url_fopen 已启用 WSDL 下载。如果您对以下行进行注释:
/* 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.
观察到这种行为
我最近遇到了一个问题,即对 ID3Global/address 服务的 SOAP 调用突然停止工作(它们以前工作正常)。我认为这与托管服务提供商在我们的服务器上关闭 allow_url_fopen
有关,这意味着该服务现在无法正常工作。
我被告知我需要切换到使用 cURL 来抓取文件 (WSDL),因为 file_get_contents
需要在 php.ini 文件中设置 'allow_url_fopen'这个工作。但是我似乎没有在我的文件中使用 file_get_contents
来获取 WSDL 文件。
如何切换到使用 cURL?
这是我的 PHP 调用 SOAP 地址的文件:
<?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);
参数 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;
因此,allow_url_fopen 已启用 WSDL 下载。如果您对以下行进行注释:
/* 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.
观察到这种行为