如何通过PHP调用SOAP服务

来源:AI教程网作者:北京网站建设头衔:草根站长
导读:本期聚焦于小伙伴创作的《如何通过PHP调用SOAP服务》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何通过PHP调用SOAP服务》有用,将其分享出去将是对创作者最好的鼓励。

SOAP(简单对象访问协议)是一种基于XML的协议,用于在网络上交换结构化信息,广泛用于企业级Web服务对接场景。PHP提供了内置的SOAP扩展,可以方便地实现SOAP服务的调用,当服务需要WS-Security安全规范时,还需要额外处理安全头信息。

如何通过PHP调用SOAP服务

环境准备

首先要确保PHP环境已经安装了SOAP扩展,可以通过phpinfo()函数查看是否存在SOAP模块。如果没有安装,在Linux环境下可以通过包管理器安装,比如Ubuntu系统执行以下命令:

sudo apt-get install php-soap
sudo systemctl restart php-fpm
sudo systemctl restart nginx

安装完成后重启服务,再次查看phpinfo确认SOAP扩展已启用。

基础SOAP服务调用

PHP的SoapClient类是实现SOAP调用的核心类,基础调用只需要指定WSDL地址即可。以下是一个简单的调用示例:

<?php
// 初始化SoapClient,传入WSDL地址
$wsdl = "http://ipipp.com/soap/service?wsdl";
$client = new SoapClient($wsdl, [
    'trace' => 1, // 开启追踪,方便调试请求和响应
    'exceptions' => true, // 开启异常抛出
]);

// 调用SOAP服务的方法,假设服务有getUserInfo方法,需要传入用户ID参数
$params = [
    'userId' => 1001
];
$result = $client->__soapCall('getUserInfo', [$params]);

// 输出结果
echo "<pre>";
print_r($result);
echo "</pre>";

// 调试:输出请求和响应的XML内容
echo "请求XML:\n" . $client->__getLastRequest() . "\n";
echo "响应XML:\n" . $client->__getLastResponse() . "\n";
?>

上面的代码中,trace参数设置为true后可以获取最后一次请求的XML和响应XML,方便排查调用问题。__soapCall方法的第一个参数是要调用的SOAP方法名,第二个参数是方法的参数数组。

自定义SOAP请求头

有些SOAP服务需要自定义请求头,比如传递认证信息。可以通过SoapHeader类来构造请求头,再传递给SoapClient。示例如下:

<?php
$wsdl = "http://ipipp.com/soap/service?wsdl";
$client = new SoapClient($wsdl, [
    'trace' => 1,
    'exceptions' => true,
]);

// 构造自定义请求头,命名空间为http://ipipp.com/soap/header,头名称为AuthHeader
$headerParams = [
    'username' => 'test_user',
    'token' => 'abc123xyz'
];
$header = new SoapHeader('http://ipipp.com/soap/header', 'AuthHeader', $headerParams);

// 设置请求头
$client->__setSoapHeaders($header);

// 调用服务方法
$result = $client->__soapCall('getOrderList', [['status' => 1]]);
print_r($result);
?>

WS-Security实现

WS-Security是Web服务安全规范,用于在SOAP消息中添加签名、加密、时间戳等安全信息。PHP原生SoapClient没有直接支持WS-Security,需要手动构造安全头。以下是一个实现WS-Security UsernameToken的示例:

<?php
class WsseSoapClient extends SoapClient {
    private $username;
    private $password;

    public function __construct($wsdl, $options = []) {
        parent::__construct($wsdl, $options);
        $this->username = $options['username'] ?? '';
        $this->password = $options['password'] ?? '';
    }

    // 重写__doRequest方法,手动添加WS-Security头
    public function __doRequest($request, $location, $action, $version, $one_way = 0) {
        // 解析原始SOAP请求XML
        $dom = new DOMDocument();
        $dom->loadXML($request);

        // 创建WS-Security相关节点
        $wsseNs = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
        $wsuNs = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';

        // 创建Security节点
        $security = $dom->createElementNS($wsseNs, 'wsse:Security');
        $security->setAttribute('soap:mustUnderstand', '1');

        // 创建UsernameToken节点
        $usernameToken = $dom->createElementNS($wsseNs, 'wsse:UsernameToken');

        // 创建Username节点
        $usernameNode = $dom->createElementNS($wsseNs, 'wsse:Username', $this->username);
        $usernameToken->appendChild($usernameNode);

        // 创建Password节点,密码类型为密码文本
        $passwordNode = $dom->createElementNS($wsseNs, 'wsse:Password', $this->password);
        $passwordNode->setAttribute('Type', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText');
        $usernameToken->appendChild($passwordNode);

        // 创建Nonce节点(随机数)
        $nonce = base64_encode(random_bytes(16));
        $nonceNode = $dom->createElementNS($wsseNs, 'wsse:Nonce', $nonce);
        $nonceNode->setAttribute('EncodingType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary');
        $usernameToken->appendChild($nonceNode);

        // 创建Created节点(时间戳)
        $created = gmdate('Y-m-d\TH:i:s\Z');
        $createdNode = $dom->createElementNS($wsuNs, 'wsu:Created', $created);
        $usernameToken->appendChild($createdNode);

        // 将UsernameToken添加到Security节点
        $security->appendChild($usernameToken);

        // 找到SOAP Header节点,如果没有则创建
        $soapNs = 'http://schemas.xmlsoap.org/soap/envelope/';
        $headerNode = $dom->getElementsByTagNameNS($soapNs, 'Header')->item(0);
        if (!$headerNode) {
            $envelope = $dom->getElementsByTagNameNS($soapNs, 'Envelope')->item(0);
            $headerNode = $dom->createElementNS($soapNs, 'soap:Header');
            $envelope->insertBefore($headerNode, $envelope->firstChild);
        }

        // 将Security节点添加到Header
        $headerNode->appendChild($security);

        // 获取修改后的XML
        $newRequest = $dom->saveXML();
        // 调用父类方法发送请求
        return parent::__doRequest($newRequest, $location, $action, $version, $one_way);
    }
}

// 使用自定义的WsseSoapClient调用服务
$wsdl = "http://ipipp.com/soap/secure_service?wsdl";
$client = new WsseSoapClient($wsdl, [
    'trace' => 1,
    'exceptions' => true,
    'username' => 'secure_user',
    'password' => 'secure_pass_123'
]);

// 调用服务方法
$result = $client->__soapCall('querySecureData', [['queryKey' => 'test_key']]);
echo "<pre>";
print_r($result);
echo "</pre>";
?>

上面的代码通过继承SoapClient类,重写了__doRequest方法,在发送请求前手动构造WS-Security的UsernameToken安全头,满足需要WS-Security认证的SOAP服务调用需求。如果需要更复杂的签名或加密逻辑,可以在此基础上扩展对应的XML节点构造逻辑。

常见问题排查

  • 如果调用时出现SOAP-ERROR: Parsing WSDL: Couldn't load from错误,先检查WSDL地址是否可访问,网络是否通畅,防火墙是否拦截了请求。
  • 如果返回结果为空或者报错,开启trace选项,查看__getLastRequest__getLastResponse的内容,对比服务要求的XML格式排查问题。
  • WS-Security相关报错时,检查命名空间是否正确,各节点的属性是否符合服务要求的WS-Security规范版本。

PHPSOAPWS_SecurityWeb服务修改时间:2026-06-05 15:38:21

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。