PHP 5 起內建 SoapClient,無需額外擴展,支援 WSDL 1.1、SOAP 1.1/1.2,適合快速整合企業級 Web Service(ERP、CRM、物流追蹤、金流通知)。本文以「IP 轉國家」公開服務為例,示範從建立客戶端到多種呼叫風格的完整流程。
<?php
$wsdl = 'https://www.ecubicle.net/iptocountry.asmx?wsdl';
$client = new SoapClient($wsdl, [
'trace' => true, // 關鍵:啟用請求/回應記錄
'exceptions' => true, // 拋出 SoapFault 異常而非回傳 false
'cache_wsdl' => WSDL_CACHE_MEMORY, // 開發期建議 MEMORY,正式環境可用 WSDL_CACHE_DISK
// 'login' => 'user', 'password' => 'pass', // HTTP Basic Auth 若需
]);
?>
<?php
print_r($client->__getFunctions());
/* 典型輸出:
Array (
[0] => FindCountryAsStringResponse FindCountryAsString(FindCountryAsString $parameters)
[1] => FindCountryAsStringResponse FindCountryAsString(string $V4IPAddress)
)
*/
<?php
$ip = '64.156.132.140';
// 風格 1:__soapCall 完整控制(最繁瑣)
$result = $client->__soapCall('FindCountryAsString', [
'FindCountryAsString' => ['V4IPAddress' => $ip]
]);
echo $result->FindCountryAsStringResult;
// 風格 2:魔術方法直呼(最直觀,推薦)
$result = $client->FindCountryAsString(['V4IPAddress' => $ip]);
echo $result->FindCountryAsStringResult;
// 風格 3:SoapParam 封裝參數(需對應 WSDL 訊息部件名)
$param = new SoapParam(['V4IPAddress' => $ip], 'FindCountryAsStringSoapIn');
$result = $client->__soapCall('FindCountryAsString', [$param]);
echo $result->FindCountryAsStringResult;
// 風格 4:SoapParam + 魔術方法
$param = new SoapParam(['V4IPAddress' => $ip], 'FindCountryAsStringSoapIn');
$result = $client->FindCountryAsString($param);
echo $result->FindCountryAsStringResult;
?>
<?php
try {
$result = $client->FindCountryAsString(['V4IPAddress' => $ip]);
} catch (SoapFault $e) {
// 1. 最後發送的 XML 請求
echo '<h3>Request XML</h3><pre>'.htmlspecialchars($client->__getLastRequest()).'</pre>';
// 2. 最後請求 HTTP Header
echo '<h3>Request Headers</h3><pre>'.htmlspecialchars($client->__getLastRequestHeaders()).'</pre>';
// 3. 最後收到的 XML 回應
echo '<h3>Response XML</h3><pre>'.htmlspecialchars($client->__getLastResponse()).'</pre>';
// 4. 最後回應 HTTP Header
echo '<h3>Response Headers</h3><pre>'.htmlspecialchars($client->__getLastResponseHeaders()).'</pre>';
throw $e;
}
| 現象 | 原因 | 解決 |
|---|---|---|
| 找不到函式/參數錯誤 | WSDL 定義複雜型別、命名空間 | 用 __getTypes() 檢視型別結構,對應建立 SoapVar 或陣列 |
| 編碼亂碼 | 服務端非 UTF-8 | 建立客戶端時加 'encoding'=>'ISO-8859-1' 或轉碼 |
| 憑證驗證失敗 | HTTPS 自簽憑證 | 加 'stream_context'=>stream_context_create(['ssl'=>['verify_peer'=>false,'verify_peer_name'=>false]])(僅測試環境) |
| 效能慢 | 每次請求下載 WSDL | 正式環境設 'cache_wsdl'=>WSDL_CACHE_DISK,並定期清理 /tmp/wsdl-* |
<?php
class AuthSoapClient extends SoapClient {
private string $token;
public function __construct($wsdl, array $options, string $token) {
$this->token = $token;
parent::__construct($wsdl, $options);
}
protected function __doRequest($request, $location, $action, $version, $one_way = 0) {
$headers = [
"Authorization: Bearer {$this->token}",
"Content-Type: text/xml; charset=utf-8",
];
return parent::__doRequest($request, $location, $action, $version, $one_way, $headers);
}
}
// 使用
$client = new AuthSoapClient($wsdl, ['trace'=>true], 'my-jwt-token');
PHP SoapClient 功能完整但參數傳遞靈活度高導致易混淆。建議流程:1. 開啟 trace 2. 用 __getFunctions/__getTypes 理解結構 3. 先用魔術方法直呼 4. 失敗時用 __getLastRequest 對照 WSDL 調整參數結構。掌握此流程,整合任何 SOAP 服務皆可游刃有餘。