我們成功地與 Weather 服務取得了聯系
並顯示了結果
但是如果出現錯誤
得不到預期的結果
該怎麼辦?ext/soap 可以顯示客戶機與服務器之間交換的 SOAP 消息
能夠幫助我們確定問題所在
只有使用 trace 選項創建 SoapClient 時
才要使用跟蹤功能
我們在 options 數組參數中設置 trace 選項
將該參數傳遞給 SoapClient 構造函數
我們將構造函數的使用改為
$soapClient = new SoapClient(//localhost:
/
ItsoWebService
RouterWeb/wsdl/itso/session/WeatherForecastEJB
wsdl
array(
trace
=>
));
並在調用 goForecast 之後調用 trace 方法
echo
Request :<br>
htmlspecialchars($soapClient
>__getLastRequest())
<br>
;
echo
Response :<br>
htmlspecialchars($soapClient
>__getLastResponse())
<br>
;
一定要使用 htmlspecialchars 內置函數對 trace 輸出進行編碼
因為它將 SOAP xml 分界符轉換成特殊字符
如 <
這樣可以避免浏覽器將其解釋成標記
下面是某個請求的 trace 輸出
<?xml version=
encoding=
UTF
?>
<SOAP
ENV:Envelope xmlns:SOAP
ENV=
xmlns:ns
=
>
<SOAP
ENV:Body>
<ns
:getForecast>
<ns
:startDate>
T
:
:
</ns
:startDate>
<ns
:days>
</ns
:days>
</ns
:getForecast>
</SOAP
ENV:Body>
</SOAP
ENV:Envelope>
對應的應答是
<?xml version=
encoding=
UTF
?>
<soapenv:Envelope xmlns:soapenv=
xmlns:soapenc=
xmlns:xsd=
xmlns:xsi=
instance
>
<soapenv:Body>
<getForecastResponse xmlns=
>
<getForecastReturn xmlns:ns
=
>
<ns
:condition>sunny</ns
:condition>
<ns
:date>
T
:
:
Z</ns
:date>
<ns
:windDirection>W</ns
:windDirection>
<ns
:windSpeed>
</ns
:windSpeed>
<ns
:temperatureCelsius>
</ns
:temperatureCelsius>
<ns
:dbflag>
</ns
:dbflag>
</getForecastReturn>
</getForecastResponse>
</soapenv:Body>
</soapenv:Envelope>
如果在開啟跟蹤功能的情況下運行客戶機來收集這些輸出
那麼需要將 days 參數設置為
只有這樣做
SOAP 應答才會輸出較少的行
但是我們遇到了沒有預料到的行為
我們本來期望 getForecastResponse 和以前一樣是一個 Weather 對象數組
但是它應該只有一個元素
而不是
個元素
然而
它被轉換成了一個簡單的 Weather 對象
我們必須根據這種行為進行編碼
就像您在最終的示例 PHP 客戶機代碼中看到的那樣
這與 Java 客戶機的行為有所不同
在客戶機行為中
getForecast 總是返回 Weather 對象數組
無論服務器響應中有多少個 Weather 對象
SoapClient::_getTypes() 輸出並沒有為我們理解這種差異提供足夠的細節
因此我們要求助於 WSDL 文檔來了解完整的接口規范
解釋 WSDL 我們已經成功地調用了 Weather 服務
但是還沒有看過它的 WSDL 文檔
WSDL 中的細節要比 SoapClient 公開的多
我們如何知道應該在 startDate 參數中放什麼呢?我們應該期望從返回的數據中實際得到什麼?要回答這些問題
必須更深入地分析 WSDL
可以從下載部分下載 Weather Forecast 應用程序的 WSDL
如果使用不同的 Web 服務
只需要在浏覽器中打開相應的 WSDL 文檔即可
getForecast 操作的 WSDL 是
<wsdl:operation name=
getForecast
>
<wsdl:input message=
intf:getForecastRequest
name=
getForecastRequest
/>
<wsdl:output message=
intf:getForecastResponse
name=
getForecastResponse
/>
</wsdl:operation>
其中的 getForecastRequest 消息被定義為
<wsdl:message name=
getForecastRequest
>
<wsdl:part element=
intf:getForecast
name=
parameters
/>
</wsdl:message>
而 getForecast 結構被定義為
<element name=
getForecast
>
<complexType>
<sequence>
<element name=
startDate
nillable=
true
type=
xsd:dateTime
/>
<element name=
days
type=
xsd:int
/>
</sequence>
</complexType>
</element>
於是我們知道該函數需要兩個參數
xsd:dateTime 類型的 startDate 和整數類型的 days
這與我們所了解的 SoapClient::_getTypes 函數完全匹配
但是現在我們還知道 startDate 可以為空(nillable)
毫無疑問
如果我們簡化輸入參數
那麼該函數將如下所示
$forecastResponse = $soapClient
>getForecast(array(
startDate
=>Null
days
=>
));
如果明確指定今天的日期
結果會與所指定的完全一致
如果希望制定其他起始日期怎麼辦呢?XML Schema將 dateTime 定義成一種基本類型
按照 ISO
標准格式化
比如
T
:
:
假設希望了解三天之後的天氣預報
可以使用內置函數 strtotime(
+
days
) 獲得需要的日期
該函數與 time() 函數相同
都返回標准 UNIX 格式的日期時間
即表示從公元紀年開始到現在的秒數的一個整數
我們知道 XML Schema 要求日期采用具有字符串字段的 ISO
格式進行編碼
於是在示例客戶機中編寫了 timeToIso
函數
將整數日期轉換成 SOAP 編碼定義的格式
但我們吃驚地發現
其實並不需要這樣做
ext/soap 非常聰明地將整數日期轉化成了需要的字符串字段格式
無論傳遞的是整數還是預格式化的字符串
都沒有關系
最終傳送的 SOAP 消息都是一樣的
響應中的日期又如何呢?在回程中
ext/soap 從 SOAP 響應獲得了 dateTime 字段
但是沒有做任何格式轉換
我們希望它返回一個整數
以表示從公元紀年到現在的秒數
但實際上得到的是按照 ISO
格式化的字符串
於是我們使用 strtotime 函數將其轉化成整數
然後使用 strftime 格式化該整數
以便於表示
Weather Service 按日期提供預報
但它忽略了 dateTime 編碼中的時間成分
所以我們沒有考慮這方面的調整
如果從運行在不同時區內的服務中請求天氣預報
那麼可能必須這樣做
如果希望進一步了解時區轉換
請參閱參考資料中給出的描述 ISO
標准的文章
現在再回到響應格式上來
上一節中曾經提到 getForecast 返回數據的不一致性
WSDL 描述告訴我們 getForecast 返回一個 getForecastResponse 對象
getForecastResponse 可以包含無限多個稱為 Weather 的復雜類型的列表
<element name=
getForecastResponse
>
<complexType>
<sequence>
<element maxOccurs=
unbounded
name=
getForecastReturn
type=
tns
:Weather
/>
</sequence>
</complexType>
</element>
<complexType name=
Weather
>
<sequence>
<element name=
condition
nillable=
true
type=
xsd:string
/>
<element name=
date
nillable=
true
type=
xsd:dateTime
/>
<element name=
windDirection
nillable=
true
type=
xsd:string
/>
<element name=
windSpeed
type=
xsd:int
/>
<element name=
temperatureCelsius
type=
xsd:int
/>
<element name=
dbflag
type=
xsd:boolean
/>
</sequence>
</complexType>
WSDL 不允許出現單元素數組這種特例
不幸的是
當響應只包含一個 Weather 對象時
ext/soap 沒有考慮 WSDL 中應用於 getForecastResponse 的 <sequence> 標簽
因為這種行為在客戶機代碼中造成了不必要的復雜性
最後
WSDL 文檔還告訴 SOAP 客戶機可以從網絡中的哪個地方找到該服務
<wsdl:service name=
WeatherForecastEJBService
>
<wsdl:port binding=
intf:WeatherForecastEJBSoapBinding
name=
WeatherForecastEJB
>
<wsdlsoap:address location=
//localhost:
/ItsoWebService
RouterWeb/services/WeatherForecastEJB
/>
</wsdl:port>
</wsdl:service>
處理 SOAP 錯誤 如果運行客戶機時出現錯誤怎麼辦?與其他語言(如 Java)一樣
PHP
新增加了一種異常機制
ext/soap 使用這種新的機制
以 SoapFault 對象的形式返回錯誤
比方說
可以用下面這種形式將代碼包裝起來
try {
some SOAP operation
} catch (SoapFault $soapFault) {
echo $soapFault;
}
注意
與 Java 有所不同
PHP 語言的 try
catch 塊不能包含 finally
From:http://tw.wingwit.com/Article/program/PHP/201311/20772.html