企業級系統整合常需 PHP 呼叫 .NET (ASMX/WCF) 發布的 SOAP WebService。PHP 內建 SoapClient 提供完整支援,但編碼、命名空間、參數結構等細節極易踩坑。
# 安裝擴展 apt-get install php-soap # Debian/Ubuntu yum install php-soap # CentOS/RHEL # php.ini 確認 extension=soap
<?php
// 1. 實例化(WSDL 模式)
$wsdl = 'https://example.com/Service.asmx?WSDL';
$client = new SoapClient($wsdl, [
'trace' => 1, // 除錯:保留最後請求/回應
'exceptions' => true, // 拋出 SoapFault 異常
'soap_version' => SOAP_1_1, // 或 SOAP_1_2
'encoding' => 'UTF-8', // 請求編碼
'cache_wsdl' => WSDL_CACHE_MEMORY, // 開發建議關閉快取
'connection_timeout' => 10,
'stream_context' => stream_context_create([
'ssl' => [
'verify_peer' => false, // 測試環境忽略 SSL 驗證
'verify_peer_name' => false,
]
]),
]);
// 2. 檢視可用函數與型別(除錯用)
// var_dump($client->__getFunctions());
// var_dump($client->__getTypes());
// 3. 呼叫方法
try {
$params = [
'param1' => '01',
'param2' => '02',
];
// 關鍵:參數需包裝成陣列的陣列,對應 WSDL 定義的 message parts
$response = $client->__soapCall('GetArticle', [$params]);
// 或直接呼叫(若方法名符合 PHP 命名規則)
// $response = $client->GetArticle($params);
// 4. 處理回應
if (is_soap_fault($response)) {
throw new \SoapFault($response->faultcode, $response->faultstring);
}
// .NET 回傳通常包裝在 GetArticleResult 屬性
$data = $response->GetArticleResult ?? $response;
var_dump($data);
} catch (SoapFault $e) {
error_log("SOAP Fault: [{$e->faultcode}] {$e->faultstring}");
// 除錯:查看原始 XML
if (isset($client)) {
error_log("Request:\n" . $client->__getLastRequest());
error_log("Response:\n" . $client->__getLastResponse());
}
}
?>
.NET WSDL 定義的 message 通常有多個 part,PHP 需傳遞 [['part1' => val1, 'part2' => val2]](外層陣列對應參數列表,內層對應各 part)。單一 part 也要用雙層:[['xml' => $xmlString]]。
複雜型別(如 .NET 的 DataSet、自定義類)會對應到 PHP 的 stdClass 或陣列。可使用 classmap 選項映射到自定義類:
$client = new SoapClient($wsdl, [
'classmap' => [
'UserInfo' => \App\Dto\UserInfo::class,
'OrderDetail' => \App\Dto\OrderDetail::class,
],
]);
encoding="UTF-8",PHP 端設 'encoding' => 'UTF-8'。utf-8 但實際發送 gb2312,需在 stream_context 設 'http' => ['header' => 'Content-Type: text/xml; charset=utf-8']。Content-Type: text/xml; charset=utf-8,必要時 mb_convert_encoding($xml, 'UTF-8', 'GB2312')。// 生產環境必須驗證
'stream_context' => stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'cafile' => '/etc/ssl/certs/ca-certificates.crt', // 系統 CA 憑證包
'ciphers' => 'HIGH:!aNULL:!MD5:!3DES',
]
])
$client = new SoapClient($wsdl, [
'keep_alive' => true,
'cookies' => true, // 自動處理 Set-Cookie
]);
// 登入
$client->Login(['username' => 'u', 'password' => 'p']);
// 後續呼叫自動攜帶 Cookie
$client->GetData([]);
$client = new SoapClient(null, [
'location' => 'https://example.com/Service.asmx',
'uri' => 'https://tempuri.org/', // 命名空間
'trace' => 1,
]);
$client->__soapCall('GetArticle', [['param1' => '01']]);
// 開啟 trace 後 echo "REQUEST:\n" . $client->__getLastRequest() . "\n"; echo "RESPONSE:\n" . $client->__getLastResponse() . "\n"; echo "REQUEST HEADERS:\n" . $client->__getLastRequestHeaders() . "\n"; echo "RESPONSE HEADERS:\n" . $client->__getLastResponseHeaders() . "\n";
將輸出存檔,用 SoapUI/Postman 對比驗證。
'cache_wsdl' => WSDL_CACHE_DISK,生產環境設定 ini_set('soap.wsdl_cache_ttl', 86400)。SoapClient 實例(單例模式),避免重複解析 WSDL。PHP SoapClient 功能強大但細節多。掌握「參數雙層陣列、命名空間映射、編碼統一、SSL 驗證、原始 XML 除錯」五大核心,即可解決 90% 的 .NET WebService 整合問題。建議封裝統一的 SoapServiceClient 基類,集中處理異常、日誌、重試、快取等橫切關注點。