學習 PHP SOAP 擴展的一些筆記

對 SOAP 的相關學習就先告此一段落,這是最後一篇文章用來記錄下學習過程當中的一些筆記和心得。php

前面三篇文章分別是:git

  1. 《SOAP 介紹》github

  2. 《SOAP Web 服務介紹》web

  3. 《PHP SOAP 擴展的使用》segmentfault

如何理解

由於 SOAP Web 服務是基於 HTTP 協議的,發出一個 SOAP 消息請求,實際上利用 HTTP 動詞中的 POST,而後把 SOAP 消息放置在 HTTP body 裏面發送。簡單來講就是:每調用一次 SOAP 服務,就是發送一條 POST 請求。數組

下面是一次請求接口所發送的內容:服務器

POST /webservices/qqOnlineWebService.asmx HTTP/1.1
Host: www.webxml.com.cn
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.4.29
Content-Type: application/soap+xml; charset=utf-8; action="http://WebXml.com.cn/qqCheckOnline"
Content-Length: 247

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope
    xmlns:env="http://www.w3.org/2003/05/soap-envelope"
    xmlns:ns1="http://WebXml.com.cn/">
    <env:Body>
        <ns1:qqCheckOnline>
            <ns1:qqCode>8698053</ns1:qqCode>
        </ns1:qqCheckOnline>
    </env:Body>
</env:Envelope>

咱們能夠看到,一次 SOAP 請求,實際上就是向服務器端發送了一個 POST 請求。而發送的內容正是 SOAP 消息(它代表你本次調用的接口方法以及參數等等)。網絡

這個 POST 請求,有一些特色,好比:它發送的內容類型爲:application/soap+xml,用戶代理爲 PHP-SOAP/5.4.29 ,其餘特色本身觀察,很少說。app

SOAP 擴展的做用

實際上,既然咱們知道請求一次接口只是發送一次 POST 請求,那麼咱們徹底可使用一些工具或 PHP 自己自帶的一些庫(好比 curl、fsockopen)模擬發送 POST 請求,而不須要使用 PHP 的 SOAP 擴展。對,沒錯!在 PHP 尚未提供 SOAP 擴展前,的確不少人也是這樣作的。curl

那幹嗎要用 SOAP 擴展呢? 由於它官網的唄,由於它用 C 語言寫的,速度槓槓的,並且封裝得很好用,也不須要本身編寫繁瑣的 XML 代碼了,因此就用它。

換句話說,實際上 SOAP 擴展就是一個更好用,速度更快,專門用於處理 SOAP 服務的 HTTP 封裝庫,沒有什麼很深奧的東西。

使用方法

PHP 的幫助手冊,有關於 SOAP 擴展的詳細說明文檔,已經對如何使用說得很清楚了,特別手冊後面的一些用戶的評論和貢獻的代碼片斷,基本上能夠解決你大部分的問題。下面記錄一下本身在開發過程當中遇到的一些問題,以及解決的方法和一些須要注意的地方。

WSDL 和 non-WSDL

如今基本上全部 SOAP Web 服務都提供 WSDL 接口描述文件,因此 non-WSDL 這種模式基本上不用考慮。

關於 SOAP 版本

PHP SOAP 擴展同時支持 SOAP 1.1 和 SOAP 1.2 兩個版本。通常來講,如今的接口基本上也同時支持這兩個 SOAP 協議版本進行通訊,那麼在這種狀況下,固然是採用高版本的 SOAP 1.2 了。實際上,不管你是使用哪一個版本,若是你是使用 SOAP 擴展來調用服務的話,在使用該擴展過程當中沒有任何區別,對你來講都是同樣的。惟一須要你去作的是在 SoapCient 進行初始化時,把 soap_version 設置爲 SOAP_1_1 或 SOAP_1_2 便可,就像下面這樣:

// SOAP 1.1 
$client = new SoapClient($wsdl, [
    'soap_version' => SOAP_1_1
]);

// SOAP 1.2
$client = new SoapClient($wsdl, [
    'soap_version' => SOAP_1_2
]);

SoapParam 和 SoapVar

上面已經說了,如今的服務基本上都提供 WSDL 描述文件,若是是這樣的話,這兩個類 SoapParamSoapVar 你基本上能夠不用管,由於 SOAP 之因此提供這兩個類,主要仍是爲了 PHP SOAP 擴展可以去使用一些沒有 WSDL 描述文件的服務,固然這種狀況基本上已經不存在了。

關於 __soapCall 方法

該方法也同樣,通常都它只會用於 non-WSDL 模式下,由於在 WSDL 模式下,徹底能夠把你須要調用的方法做爲 SoapClient 對象的一個方法進行調用。不過,若是調用方法的 uri 與 默認的 uri 不同時,又或者調用該方法時,你必須爲它帶上一個 SOAP Header 時,就須要使用 __soapCall 方法了。下面是摘自官方手冊的一段話:

This is a low level API function that is used to make a SOAP call. Usually, in WSDL mode, SOAP functions can be called as methods of the SoapClient object. This method is useful in non-WSDL mode when soapaction is unknown, uri differs from the default or when sending and/or receiving SOAP Headers.

__soapCall 在使用上與經過方法名直接調用的方式有一些區別。下面咱們來看看幾個例子。一樣的,咱們仍是使用一個網絡上能夠無償使用的 SOAP 服務配合咱們,該服務的主要做用是經過 QQ 號來查詢該用戶的在線狀態。服務地址

下面是請求該服務時,應該發送的 SOAP 消息:

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
    <soap12:Body>
        <qqCheckOnline xmlns="http://WebXml.com.cn/">
            <qqCode>string</qqCode>
        </qqCheckOnline>
    </soap12:Body>
</soap12:Envelope>

經過方法名調用該接口:

<?php
$wsdl = 'http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl';
$client = new SoapClient($wsdl, [
    'soap_version' => SOAP_1_2
]);

$client->qqCheckOnline([
    'qqCode' => 8698053
]));

使用 __soapCall 方法調用該接口:

<?php
$wsdl = 'http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl';
$client = new SoapClient($wsdl, [
    'soap_version' => SOAP_1_2
]);

$client->__soapCall('qqCheckOnline', [
    ['qqCode' => 8698053]
]);

重點關注兩種調用方式時,參數的不同。經過方法名直接調用的方式,參數是一維數組,而經過 __soapCall 方法調用時,參數是二維數組,這是它們之間的區別之一。

還有第二個區別,就是 __soapCall 方法能夠在調用接口時,添加額外的 SOAP Header,好比這樣:

$wsdl = 'http://www.example.com/service.asmx?wsdl';
$client = new SoapClient($wsdl, [
    'soap_version' => SOAP_1_2
]);

$auth = ['sAuthenticate' => 'ab3cde34f5r4545g'];
$namespace = 'http://www.example.com';

$header = new SoapHeader($namespace, 'AuthenHeader', $auth, false);
$client->__soapCall("SomeFunction", $parameters, null, $header);

雖然 SoapClient 也有 __setSoapHeaders 方法,可是它會給該實例的全部方法都添加上 SOAP Header,若是存在有些方法須要 SOAP Header 而有些又不須要的話,那麼就必須使用 __soapCall 方法,針對某個方法來添加 SOAP Header 了。

關於命名空間(namespace)

實際上,在 WSDL 模式下,若是不須要發送 SOAP Header 的話,那麼 namespace 是用不上的,由於 namespace 實際上已經在 WSDL 文件中有所描述了,PHP 的 SOAP 擴展會自動把它從 WSDL 文件中解析出來,用於構造 SOAP 請求。若是 SOAP 消息中,須要添加 SOAP Header 的話,那麼必須提供 namespace。舉個例子:

好比說,有一個服務它須要你發送的 SOAP 消息中必須有 SOAP Header,像下面同樣:

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
    <soap12:Header>
        <AuthenHeader xmlns="http://www.example.cn">
            <sAuthenticate>string</sAuthenticate>
        </AuthenHeader>
    </soap12:Header>
    <soap12:Body>
        <GetUserInfoById xmlns="http://www.example.cn">
            <UserID>int</UserID>
        </GetUserInfoById>
    </soap12:Body>
</soap12:Envelope>

下面是構造該 SOAP 請求的代碼:

<?php
$wsdl = 'http://www.example.com/service.asmx?wsdl';
$client = new SoapClient($wsdl, [
    'soap_version' => SOAP_1_2
]);

$auth = ['sAuthenticate' => 'ab3cde34f5r4545g'];
$namespace = 'http://www.example.com';

$header = new SoapHeader($namespace, 'AuthenHeader', $auth, false);
$client->__setSoapHeaders($header);    

$response = $client->GetUserInfoById([
    'UserID' => 100
]);

能夠看到,使用 SoapHeader 來構建一個 SOAP Header 時,必須提供 namespace,並且是正確的命名空間。

其實,構造一個 SOAP Header 的方法不止這一種寫法,還有其餘寫法,好比你還能夠這樣構造與上面同樣的 SOAP 消息:

<?php
class AuthenHeader
{
    private $sAuthenticate;
    
    public function __construct($auth)
    {
        $this->sAuthenticate = $auth;
    }
}

$wsdl = 'http://www.example.com/service.asmx?wsdl';
$client = new SoapClient($wsdl, [ 
    'soap_version' => SOAP_1_2 
]);

$auth = 'ab3cde34f5r4545g';
$namespace = 'http://www.example.com';

$authenHeader = new AuthenHeader($auth);

$header = new SoapHeader($namespace, 'AuthenHeader', $authenHeader, false);
$client->__setSoapHeaders($header);

$response = $client->GetUserInfoById([ 
    'UserID' => 100 
]);

關於 SoapHeader 其餘更多的用法,推薦翻閱 PHP 手冊中的 SOAP 章節

關於 SoapFault

服務端在處理客戶端請求發生錯誤時,將會拋出 SoapFault 異常。對於 SOAP 擴展中,哪些方法可能會拋出異常能夠查看手冊。一旦發生了異常,咱們都應該捕捉它們,並妥善處理。像下面這樣:

try {
    $client = new SoapClient($wsdl, [
        'trace' => true,
        'soap_version' => SOAP_1_2
    ]);

    .....
} catch(SoapFault $e) {
    //在這裏處理異常
}

getLastRequest 和 getLastResponse

這兩個方法能夠查看最近一次請求和響應的內容,這兩個方法對於調試頗有幫助。固然,這兩個方法只有在 SoapClient 實例化時,trace 參數設置爲 true 纔會生效。好比像這樣:

<?php
$wsdl = 'http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl';
$client = new SoapClient($wsdl, [
    'trace' => true,
    'soap_version' => SOAP_1_2
]);

$client->qqCheckOnline([
    'qqCode' => 8698053
]));

echo $client->__getLastRequest();
echo $client->__getLastResponse();

getLastRequest() 輸出的內容:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope
    xmlns:env="http://www.w3.org/2003/05/soap-envelope"
    xmlns:ns1="http://WebXml.com.cn/">
    <env:Body>
        <ns1:qqCheckOnline>
            <ns1:qqCode>8698053</ns1:qqCode>
        </ns1:qqCheckOnline>
    </env:Body>
</env:Envelope>

getLastResponse() 輸出的內容:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <qqCheckOnlineResponse
            xmlns="http://WebXml.com.cn/">
            <qqCheckOnlineResult>Y</qqCheckOnlineResult>
        </qqCheckOnlineResponse>
    </soap:Body>
</soap:Envelope>

SoapUI 調試工具

在調試 SOAP 服務接口時,咱們可使用功能強大的 SoapUI 工具,能夠很方便地調試接口。

SoapUI

總結

上面都是本身在學習 PHP SOAP 擴展時的一些零散的筆記,若是有不對的地方,但願你們指出,謝謝。(本文已存檔 GitHub

相關文章
相關標籤/搜索