PHP项目中的SOAP与WebService详解
基本概念
SOAP(Simple Object Access Protocol)

- 基于XML的协议
- 使用WSDL描述服务
- 支持多种传输协议(HTTP、SMTP等)
- 重量级、标准化程度高
WebService
- 跨平台、跨语言的远程调用技术
- SOAP和REST是两种主要实现方式
- 面向服务架构(SOA)的实现
PHP中的SOAP支持
PHP通过soap扩展提供SOAP支持,主要包含:
SoapClient:客户端类SoapServer:服务端类SoapFault:异常处理类
实战示例
服务端实现 (server.php)
<?php
// 定义服务类
class CalculatorService {
public function add($a, $b) {
return $a + $b;
}
public function subtract($a, $b) {
return $a - $b;
}
public function multiply($a, $b) {
return $a * $b;
}
public function divide($a, $b) {
if ($b == 0) {
throw new SoapFault("Server", "Division by zero");
}
return $a / $b;
}
}
// 创建SOAP服务器
$options = array(
'uri' => 'http://localhost/soap',
'soap_version' => SOAP_1_2,
'cache_wsdl' => WSDL_CACHE_NONE
);
$server = new SoapServer(null, $options);
$server->setClass('CalculatorService');
$server->handle();
?>
客户端实现 (client.php)
<?php
try {
$options = array(
'location' => 'http://localhost/server.php',
'uri' => 'http://localhost/soap',
'trace' => true,
'soap_version' => SOAP_1_2
);
$client = new SoapClient(null, $options);
// 调用服务
$result = $client->add(10, 20);
echo "Addition: 10 + 20 = " . $result . "\n";
$result = $client->subtract(50, 30);
echo "Subtraction: 50 - 30 = " . $result . "\n";
$result = $client->multiply(6, 7);
echo "Multiplication: 6 * 7 = " . $result . "\n";
try {
$result = $client->divide(10, 0);
} catch (SoapFault $e) {
echo "Error: " . $e->getMessage() . "\n";
}
// 查看请求和响应
echo "\nRequest:\n" . $client->__getLastRequest() . "\n";
echo "\nResponse:\n" . $client->__getLastResponse() . "\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
?>
WSDL方式实现
生成WSDL (calculator.wsdl)
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://localhost/soap"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://localhost/soap">
<types>
<xsd:schema targetNamespace="http://localhost/soap">
<xsd:element name="add">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="a" type="xsd:float"/>
<xsd:element name="b" type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="addResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="result" type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</types>
<message name="addRequest">
<part name="parameters" element="tns:add"/>
</message>
<message name="addResponse">
<part name="parameters" element="tns:addResponse"/>
</message>
<portType name="CalculatorPortType">
<operation name="add">
<input message="tns:addRequest"/>
<output message="tns:addResponse"/>
</operation>
</portType>
<binding name="CalculatorBinding" type="tns:CalculatorPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="add">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="CalculatorService">
<port name="CalculatorPort" binding="tns:CalculatorBinding">
<soap:address location="http://localhost/server.php"/>
</port>
</service>
</definitions>
使用WSDL的服务端
<?php
class CalculatorWSDL {
public function add($a, $b) {
return $a + $b;
}
}
$server = new SoapServer("calculator.wsdl");
$server->setClass('CalculatorWSDL');
$server->handle();
?>
复杂数据类型处理
<?php
class UserService {
public function getUser($id) {
return array(
'id' => $id,
'name' => 'John Doe',
'email' => 'john@example.com',
'age' => 30
);
}
public function createUser($userData) {
// 处理用户数据
return true;
}
}
// 客户端调用
$client = new SoapClient("user.wsdl");
$user = $client->getUser(1);
echo $user->name; // 输出: John Doe
?>
错误处理与调试
<?php
class SOAPHandler {
public function secureMethod($param) {
try {
if (empty($param)) {
throw new Exception("参数不能为空");
}
return "处理成功: " . $param;
} catch (Exception $e) {
throw new SoapFault("Client", $e->getMessage());
}
}
}
// 客户端错误处理
try {
$client = new SoapClient("service.wsdl", array(
'exceptions' => true,
'trace' => true,
'connection_timeout' => 30
));
$result = $client->secureMethod("");
} catch (SoapFault $e) {
echo "SOAP错误: " . $e->faultcode . ": " . $e->faultstring;
echo "\n详细错误: " . $e->detail;
// 调试信息
if ($client) {
echo "\n请求内容:\n" . $client->__getLastRequestHeaders();
echo "\n" . $client->__getLastRequest();
echo "\n响应内容:\n" . $client->__getLastResponseHeaders();
echo "\n" . $client->__getLastResponse();
}
}
?>
安全考虑
<?php
class SecureService {
private $validTokens = array('token123', 'token456');
public function authenticate($token) {
if (!in_array($token, $this->validTokens)) {
throw new SoapFault("Client", "无效的认证令牌");
}
return true;
}
public function secureOperation($token, $data) {
if (!$this->authenticate($token)) {
throw new SoapFault("Client", "认证失败");
}
// 数据验证
if (!is_numeric($data)) {
throw new SoapFault("Client", "数据类型错误");
}
return $data * 2;
}
}
// 客户端使用HTTPS
$options = array(
'location' => 'https://secure.server.com/service.php',
'uri' => 'https://secure.server.com/soap',
'stream_context' => stream_context_create(array(
'ssl' => array(
'verify_peer' => true,
'cafile' => '/path/to/certificate.crt',
'verify_depth' => 5
)
))
);
?>
SOAP与REST对比
| 特性 | SOAP | REST |
|---|---|---|
| 协议 | 基于XML的协议 | 使用HTTP协议 |
| 状态 | 有状态或无状态 | 无状态 |
| 性能 | 较慢(XML解析) | 较快(JSON格式) |
| 安全性 | 内置WS-Security | 依赖HTTPS |
| 缓存 | 不支持 | 支持 |
| 复杂度 | 高 | 低 |
| 适用场景 | 企业级应用、金融系统 | 移动应用、Web API |
最佳实践
- 使用WSDL: 明确接口定义
- 错误处理: 完善的异常捕获
- 性能优化:
- 使用WSDL缓存
- 压缩SOAP消息
- 使用持久连接
- 安全加固:
- HTTPS传输
- 输入验证
- 认证授权
- 日志记录: 记录请求和响应
性能优化示例
<?php
// 使用缓存配置
ini_set('soap.wsdl_cache_enabled', '1');
ini_set('soap.wsdl_cache_dir', '/tmp/soap_cache');
ini_set('soap.wsdl_cache_ttl', '86400'); // 24小时缓存
$client = new SoapClient("service.wsdl", array(
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
'keep_alive' => true,
'cache_wsdl' => WSDL_CACHE_BOTH
));
?>
SOAP在PHP项目中适合企业级应用,特别是需要严格契约、安全性和事务支持的场景,但在轻量级应用或移动端场景,REST API可能是更好的选择。