企業系統整合常需呼叫外部 SOAP WebService。PHP 5 內建 SoapClient 提供完整客戶端功能。本文以「查詢 QQ 在線狀態」為例,示範從 WSDL 解析、型別檢視、參數建構、呼叫流程到錯誤處理的完整步驟。
<?php
try {
// 1. 建立客戶端:支援本地 .wsdl 檔或遠端 URL
$wsdl = 'https://webservice.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl';
$client = new SoapClient($wsdl, [
'encoding' => 'UTF-8', // 含中文參數必須指定
'trace' => 1, // 除錯用:記錄請求/回應 XML
'exceptions' => true, // 拋出 SoapFault 異常
'cache_wsdl' => WSDL_CACHE_MEMORY, // 效能優化
]);
// 2. 檢視可用方法與型別(開發階段必做)
echo "可用方法:
";
var_dump($client->__getFunctions());
echo "資料型別:
";
var_dump($client->__getTypes());
echo "";
// 3. 建構參數:方法需結構體時,用 PHP 類別對應
class QqCheckOnline {
public $qqCode = '10000'; // 對應 WSDL 定義的參數名
}
$param = new QqCheckOnline();
// 或陣列寫法:$param = ['qqCode' => '10000'];
// 4. 呼叫方法:__soapCall 或魔術方法
$result = $client->__soapCall('qqCheckOnline', [$param]);
// 等同於:$result = $client->qqCheckOnline($param);
// 5. 取得結果:回傳物件屬性名通常為 方法名+Result
echo "QQ 在線狀態:" . $result->qqCheckOnlineResult . "
";
} catch (SoapFault $e) {
echo "SOAP 錯誤: " . $e->getMessage() . "
";
if (isset($client)) {
echo "最後請求:" . htmlspecialchars($client->__getLastRequest()) . "
";
echo "最後回應:" . htmlspecialchars($client->__getLastResponse()) . "
";
}
}
?>
'cache_wsdl' => WSDL_CACHE_MEMORY // 開發建議 MEMORY 或 NONE,正式環境可用 WSDL_CACHE_DISK
WSDL_CACHE_NONE:每次請求下載 WSDL(開發除錯)WSDL_CACHE_MEMORY:請求生命週期內快取(推薦開發/測試)WSDL_CACHE_DISK:檔案快存 /tmp/wsdl-*(正式環境預設)WSDL_CACHE_BOTH:記憶體+磁碟使用 __getTypes() 檢視輸出,找到對應的 struct 定義,例如:
struct qqCheckOnline {
string qqCode;
}
PHP 類別屬性名 必須為 qqCode,大小寫敏感。陣列鍵名同理。
// WSDL 定義:
// struct UserInfo { string name; int age; Address addr; }
// struct Address { string city; string zip; }
class Address { public $city; public $zip; }
class UserInfo {
public $name;
public $age;
public $addr; // Address 物件
}
$addr = new Address();
$addr->city = 'Taipei';
$addr->zip = '100';
$user = new UserInfo();
$user->name = 'John';
$user->age = 30;
$user->addr = $addr;
$client->__soapCall('SubmitUser', [$user]);
若 WebService 回傳 XML 字串 而非結構化資料,PHP SoapClient 會嘗試解析該 XML 並轉為物件/陣列,導致原始 XML 遺失。對策:
- 協議方調整回傳結構化型別
- 或用
__getLastResponse()取得原始 XML 字串自行解析- 或改用 cURL 發送原始 SOAP Envelope 完全掌控
$client = new SoapClient($wsdl, [
'stream_context' => stream_context_create([
'ssl' => [
'verify_peer' => false, // 正式環境請勿關閉
'verify_peer_name' => false,
'allow_self_signed' => true,
],
]),
]);
use SoapHeader;
$auth = new SoapHeader('https://tempuri.org/', 'AuthHeader', [
'Username' => 'api_user',
'Password' => 'secret',
]);
$client->__setSoapHeaders($auth);
| 方法 | 用途 |
|---|---|
$client->__getLastRequest() | 取得最後發送的原始 SOAP XML 請求 |
$client->__getLastResponse() | 取得最後收到的原始 SOAP XML 回應 |
$client->__getLastRequestHeaders() | 取得 HTTP 請求標頭 |
需在建構子設定 'trace' => 1 才能使用。
| 錯誤 | 原因 | 解決 |
|---|---|---|
SoapFault: Function not found | 方法名拼錯、大小寫不符、WSDL 未更新 | 用 __getFunctions() 確認正確名稱 |
SoapFault: Missing parameter | 參數名稱與 WSDL 定義不符 | 用 __getTypes() 核對 struct 定義 |
Encoding: Violation of encoding rules | 中文參數未設定 encoding | 建構子加 'encoding' => 'UTF-8' |
Could not connect to host | 防火牆、DNS、HTTPS 憑證 | 檢查網路、stream_context ssl 設定 |
| 回傳物件屬性為 null | WSDL 定義 nillable=true 但實際無值 | 程式碼判斷 isset/empty 再存取 |
PHP SoapClient 功能強大但細節多:WSDL 快取、編碼設定、參數名稱精確對應、複雜型別物件建構、除錯追蹤 是成功關鍵。現代專案若可選擇,優先考慮 REST/JSON API;必須用 SOAP 時,善用 __getFunctions/__getTypes 理解契約,再撰寫對應的 DTO 類別,配合 trace 除錯,即能穩定整合。