SOAP(Simple Object Access Protocol)是企業級整合、跨平台服務呼叫的經標準協定。PHP 5 內建 `ext/soap` 擴展,無需額外安裝即可快速建立 Web Service 客戶端與伺服器端。本文深入解析 SOAP 架構、PHP 擴展配置、核心類別(`SoapServer`、`SoapClient`、`SoapFault`)用法,並提供完整可執行範例。
| 元件 | 功能說明 |
|---|---|
| SOAP 封裝 | 定義訊息結構:將傳輸資料、發送端/接收端資訊、處理規則封裝為標準 XML 格式 |
| SOAP 編碼規則 | 定義資料型別表示方式,確保異構系統間資料正確序列化/反序列化 |
| SOAP 遠程程序呼叫 (RPC) | 規範遠端方法呼叫與回應的協定流程 |
| SOAP 綁定協定 | 定義底層傳輸協定綁定(通常為 HTTP/HTTPS,也支援 SMTP 等) |
PHP 5 起預設編譯 `ext/soap`,但可能未載入。執行 `php -m | grep soap` 或 `phpinfo()` 檢查。
; Windows
extension=php_soap.dll
; Linux/Unix
extension=soap.so
; 若 extension_dir 未設定,需指定擴展目錄
extension_dir="C:/php/ext/" ; Windows(使用正斜線)
extension_dir="/usr/local/php/lib/php/extensions/no-debug-nts-xxx/" ; Linux
[soap]
; 開發階段建議關閉快取,避免 WSDL 變更不生效
soap.wsdl_cache_enabled=0
; 生產環境開啟快取提升效能
; soap.wsdl_cache_enabled=1
; soap.wsdl_cache_dir="/tmp"
; soap.wsdl_cache_ttl=86400 ; 24 小時(秒)
黃金法則:開發時關閉快取(`wsdl_cache_enabled=0`),上線前開啟並設定適當 TTL。
// 建構式
$server = new SoapServer($wsdl, $options);
// 參數說明
$wsdl: WSDL 檔案路徑或 URL;設為 NULL 表示不使用 WSDL(非 WSDL 模式)
$options: 關聯陣列,常用鍵值:
'uri' => 命名空間 URI(非 WSDL 模式下**必填**,可為空字串但不可為 null)
'encoding' => 字元編碼(中文環境**必填**,建議 'utf-8' 或 'gb2312',預設 utf-8)
'actor' => SOAP 角色
'classmap' => WSDL 型別對應 PHP 類別映射
'cache_wsdl'=> WSDL 快取策略(WSDL_CACHE_NONE, WSDL_CACHE_DISK 等)
// 核心方法
$server->addFunction($function_name); // 註冊可被呼叫的函數(字串或陣列)
$server->setClass($class_name); // 註冊整個類別的 public 方法
$server->handle($request); // 處理請求並輸出回應($request 可選,預設讀取 HTTP RAW POST)
// 建構式
$client = new SoapClient($wsdl, $options);
// 非 WSDL 模式必填選項
$options = [
'location' => 'https://example.com/soap_server.php', // **必填**:伺服器端點 URL,必須準確
'uri' => 'https://example.com/namespace', // **必填**:命名空間,建議與 Server 一致
'encoding' => 'utf-8', // 中文環境必填
'trace' => 1, // 除錯用:啟用請求/回應追蹤
'exceptions' => true, // 拋出 SoapFault 異常
'connection_timeout' => 30, // 連線逾時(秒)
];
// 呼叫遠端方法:如同呼叫本地物件方法
$result = $client->remoteMethod($param1, $param2);
// 除錯利器
echo $client->__getLastRequest(); // 最後發送的 XML 請求
echo $client->__getLastResponse(); // 最後接收的 XML 回應
// 主動拋出錯誤(伺服器端)
throw new SoapFault('Server', '資料庫連線失敗', null, 'DB_CONN_ERROR');
// 客戶端捕獲
try {
$result = $client->someMethod();
} catch (SoapFault $fault) {
echo "錯誤代碼: {$fault->faultcode}\n";
echo "錯誤訊息: {$fault->faultstring}\n";
echo "錯誤細節: {$fault->detail}\n";
// 除錯時可檢視完整請求/回應
if (isset($client)) {
echo "請求:\n" . $client->__getLastRequest() . "\n";
echo "回應:\n" . $client->__getLastResponse() . "\n";
}
}
<?php
// soap_server.php
function reserve($arr) {
// 簡單業務邏輯:將陣列轉為查詢字串
return http_build_query($arr);
}
// 非 WSDL 模式:wsdl = NULL
$server = new SoapServer(null, [
'uri' => 'https://www.example.com/soap', // 命名空間
'encoding' => 'utf-8'
]);
// 註冊可呼叫函數
$server->addFunction('reserve');
// 處理請求
$server->handle();
?>
<?php
// soap_client.php
try {
$client = new SoapClient(null, [
'location' => 'https://localhost/soap/soap_server.php', // Server 實際 URL
'uri' => 'https://www.example.com/soap', // 與 Server 一致
'encoding' => 'utf-8',
'trace' => 1, // 開啟追蹤
'exceptions' => true
]);
$params = [
'id' => 3,
'tag' => 'PHP MYSQL',
'search' => 'soap'
];
// 呼叫遠端 reserve() 方法
$result = $client->reserve($params);
echo "回應結果: $result\n"; // id=3&tag=PHP+MYSQL&search=soap
} catch (SoapFault $fault) {
echo "SOAP 錯誤: [{$fault->faultcode}] {$fault->faultstring}\n";
if (isset($client)) {
echo "請求內容:\n" . $client->__getLastRequest() . "\n";
}
}
?>
| 模式 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| WSDL 模式 | 契約優先、強型別、IDE 自動完成、跨語言互通性佳 | 維護 WSDL 檔案成本高、初學曲線陡 | 企業級整合、異構系統、長期維護專案 |
| 非 WSDL 模式 | 開發快速、無需維護 WSDL、彈性高 | 缺乏契約約束、型別檢查弱、文檔不足 | 內部微服務、快速原型、PHP 間通訊 |
// 定義對應 WSDL 複雜型別的 PHP 類別
class User {
public $id;
public $name;
public $email;
public $roles = []; // 陣列型別
}
// Server 端使用 classmap 映射
$server = new SoapServer('service.wsdl', [
'classmap' => [
'User' => 'User', // WSDL 型別名 => PHP 類別名
'UserArray' => 'ArrayObject' // 陣列型別映射
]
]);
$server->setClass('UserService');
// 客戶端發送 SOAP Header(如 API Token)
$header = new SoapHeader('https://example.com/auth', 'ApiToken', 'your-secret-token');
$client->__setSoapHeaders($header);
// 伺服器端驗證 Header
$server->setClass('MyService');
$server->handle();
// 在服務類別中驗證
class MyService {
public function someMethod() {
$headers = $this->__getSoapHeaders(); // 取得所有 Header
// 驗證邏輯...
}
}
| 錯誤現象 | 可能原因 | 解決方案 |
|---|---|---|
| Class 'SoapClient' not found | ext/soap 未啟用 | 檢查 php.ini 載入 soap.so/php_soap.dll,重啟 PHP-FPM/Apache |
| SOAP-ERROR: Parsing WSDL | WSDL URL 不可存取、格式錯誤、防火牆阻擋 | 瀏覽器直接開啟 WSDL URL 驗證;檢查網路連通性 |
| Encoding: Violation of encoding rules | 字元編碼不一致(如中文亂碼) | 統一 Client/Server `encoding` 選項為 'utf-8';確保資料庫/檔案編碼一致 |
| Function not found | Server 未註冊該函數、大小寫不符 | 檢查 `addFunction()` 或 `setClass()` 註冊名稱;SOAP 區分大小寫 |
| Looks like we got no XML document | Server 輸出非 XML 內容(如 PHP 錯誤、空白輸出) | 關閉 `display_errors`;確保 Server 腳本無任何 echo/print/空白輸出 |
PHP `ext/soap` 提供了完整、標準合規的 SOAP 實作。掌握其核心類別(`SoapServer`、`SoapClient`、`SoapFault`)、WSDL/非 WSDL 兩種模式差異、編碼處理、錯誤追蹤與效能優化技巧,能讓你輕鬆建立或整合企業級 Web Service。實務上,除非受限於既有系統契約,優先考慮 REST/JSON 架構以獲得更佳開發體驗與效能。