近期使用PHP的SoapClient调用.net的WebServices始终行不通,然而又没有对方技术人员支持的情况,尝试着 WSDL 模式和 non-WSDL 模式都无果,在 new SoapClient时直接会报错,在网上找了一个服务是可以正常调用的,说明运行环境正常:
$wsdlUrl = "http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?wsdl";
$client = new SoapClient($wsdlUrl, $array('encoding' => 'utf-8'));
//$client = new SoapClient(null, array('encoding' => 'utf-8', 'uri'=> $uri, 'location' => $wsdlUrl, 'trace' => true));
但我这边调用的服务是 HTTPS 带登录验证的服务,连实例化时都过不去,于是改用POST方式尝试着。
对方给的 WebServices URL直接访问的是跳登录页了,登录之后访问 WebServices 是正常的,则先使用cURL的模拟登录然后携带登录返回的cookie请求WebServices,请求的参数中有一个是将XML转为 base64Binary 传输。
传输的编码 Content-Type:application/x-www-form-urlencoded
所以在这里POST的参数为字符串,如 param1=value1¶m2=value2
如果使用数组,则 Content-Type 会变为表单上传的类型。
以下用代码和注释说明:
cookieArr = array(array(),array());
$this->supplyCode = '';
$this->username = 'username';
$this->password = 'password';
//先检测是否已登录
if(!$this->isCheck){
$this->checking();
}
}
/**
* 请求服务
*
*/
public function postdata()
{
try {
//将XML转为 base64Binary , xml模板由服务方提供
$xmlStr = $this->getXml();
//先将XML转为 base64 string
$baseEncode = base64_encode($xmlStr);
//再转为 byte 数组
$base64BinaryArr = $this->getBytes($baseEncode);
//将byte拼接为POST 的字符串参数时, 因对方不需要 &bytes[]= 可以接受到所有参数
$bytesParam = implode('&bytes=', $base64BinaryArr);
$param = array(
'url' => $this->wsdl,
'cookie' => $this->cookieArr[1], //携带登录后的cookie访问
'header' => array(
'Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Content-Type:application/x-www-form-urlencoded'
),
'data' => 'supplyCode='. $this->supplyCode .'&bytes='. $bytesParam
);
$res = $this->curlPost($param);
if($res){
$xmlObj = simplexml_load_string($res);
// print_r($xmlObj); exit;
if(strtoupper($xmlObj->CODE) == 'S'){
return true;
}
}
} catch (Exception $e) {
// libxml_get_last_error();
}
return false;
}
protected function getXml()
{
$xml = '
';
$xmlTpl = '
';
$data = array(
array('id' => 1, 'user_name' => 'test'),
);
foreach ($data as $val){
$xml .= sprintf($xmlTpl, $val['id'], trim($val['user_name']) );
}
$xml .= '
';
//格式化xml
$xml = preg_replace('/\s{2,}/', '', $xml);
$xml = str_replace('><', ">\n<", $xml);
return $xml;
}
/**
* 登录
*
*/
private function checking()
{
$param = array(
'url' => $this->checkUrl,
'nobody' => 1,
'header' => array(
'Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Content-Type:application/x-www-form-urlencoded'
),
'data' => 'username='. $this->username .'&password='. $this->password
);
//多次尝试登陆
for($i=0; $i < $this->testTry; $i++){
$resData = $this->curlPost($param);
if(preg_match('@HTTP/1.1\s*200\s*OK@i', $resData)){
$this->isCheck = true;
//登录成功 保存Cookie
preg_match_all('|Set-Cookie: (.*);|U', $resData, $this->cookieArr);
break;
}
}
}
protected function curlPost($param)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $param['url']);
// 对认证证书来源的检查, false 表示阻止对证书的合法性的检查
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// 从证书中检查SSL加密算法是否存在
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);//关闭直接输出
curl_setopt($ch,CURLOPT_POST,1);//使用post提交数据
curl_setopt($ch,CURLOPT_POSTFIELDS, $param['data']);//设置 post提交的数据, 区别数组与字符串
curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36');//设置用户代理
//Boolean
if(isset($param['nobody'])){
//curl_setopt($ch, CURLOPT_NOBODY, 1); //不需要HTML实体
curl_setopt($ch, CURLOPT_HEADER, 1); //是否头文件信息
}
/**
* Array
* array('Content-Type:application/x-www-form-urlencoded', 'Referer:https://localhost')
*/
if(isset($param['header'])){
curl_setopt($ch,CURLOPT_HTTPHEADER, $param['header']);//设置头信息
}
/**
* 携带Cookie访问
* array(0=>'SID=abcde', 1=>'ppid=123')
*/
if(isset($param['cookie'])){
$cookies = implode('; ', $param['cookie']);
curl_setopt($ch, CURLOPT_COOKIE, $cookies);
}
// curl_setopt($ch, CURLOPT_SSLVERSION, 3); //设定SSL版本
//保存Cookie
//$cookie_file = '/tmp/rqwebservices.cookie'; //tempnam('/tmp','cookie');
// curl_setopt($ch,CURLOPT_COOKIEJAR,$cookie_file);//设置cookie的保存目录
$data = curl_exec($ch);
if (curl_errno($ch)) {
return false;
}
curl_close($ch);
return $data;
}
/**
* 将字节数组转化为String类型的数据
* @param array $bytes
* @return string
*/
public function toStr($bytes) {
$str = '';
foreach($bytes as $ch) {
$str .= chr($ch);
}
return $str;
}
/**
* 转换一个String字符串为byte数组
* @param string $string
* @return array
*/
public function getBytes($string) {
$bytes = array();
for($i = 0; $i < strlen($string); $i++){
$bytes[] = ord($string[$i]);
}
return $bytes;
}
}