SOAP

添加笔记

用户贡献笔记 15 个笔记

18
nodkz at mail dot ru
16 年前
问题(在 PHP5 下使用 SOAP 扩展)传输包含对象或对象数组的对象。嵌套对象无法传输。

解决方案
此类由我通过反复试验开发。因此,对于大多数在 PHP5 下编写代码的开发人员来说,这 23 行代码解决了使用 SOAP 扩展的命运。

<?php
/*
根据 PHP5 中 SOAP 类组织过程的具体情况,我们必须将复杂对象包装在 SoapVar 类中。否则,对象将无法正确编码,也无法在远程 SOAP 处理程序上加载。

函数 "getAsSoap" 调用对要传输的对象进行编码。编码后,它可以正确传输。
*/
abstract class SOAPable {
public function
getAsSOAP() {
foreach(
$this as $key=>&$value) {
$this->prepareSOAPrecursive($this->$key);
}
return
$this;
}

private function
prepareSOAPrecursive(&$element) {
if(
is_array($element)) {
foreach(
$element as $key=>&$val) {
$this->prepareSOAPrecursive($val);
}
$element=new SoapVar($element,SOAP_ENC_ARRAY);
}elseif(
is_object($element)) {
if(
$element instanceof SOAPable) {
$element->getAsSOAP();
}
$element=new SoapVar($element,SOAP_ENC_OBJECT);
}
}
}

// ------------------------------------------
// 抽象示例
// ------------------------------------------

class PersonList extends SOAPable {
protected
$ArrayOfPerson; // 变量必须是受保护的或公开的!
}

class
Person extends SOAPable {
// 任何数据
}

$client=new SoapClient("test.wsdl", array( 'soap_version'=>SOAP_1_2, 'trace'=>1, 'classmap' => array('Person' => "Person", 'PersonList' => "PersonList") ));

$PersonList=new PersonList;

// 一些操作

$PersonList->getAsSOAP();

$client->someMethod($PersonList);

?>

因此,每个将通过 SOAP 传输的类都必须扩展自 SOAPable 类。
如您所见,在上面的代码中,函数 prepareSOAPrecursive 在父对象或数组中搜索另一个嵌套对象,如果找到,则尝试调用函数 getAsSOAP() 来准备嵌套对象,然后简单地通过 SoapVar 类包装。

因此,在代码传输之前,只需调用 $obj->getAsSOAP()
2
Ryan
15 年前
如果您遇到了 SOAP 无法找到实际上存在的函数(如果您查看 wsdl 文件)的问题,那是因为 PHP 正在缓存 wsdl 文件(一次缓存一天)。要关闭此功能,请在每个使用 SOAP 的脚本中添加以下行:ini_set("soap.wsdl_cache_enabled", "0"); 以禁用缓存功能。
2
Raphal Gertz
15 年前
只需注意一下,以避免在 php-soap 协议和格式支持上浪费时间。

至少在 php 5.2.9 之前,soap 扩展只能理解 wsdl 1.0 和 1.1 格式。

wsdl 2.0,自 2007 年 6 月以来 W3C 的一项推荐,在 php soap 扩展中不受支持。
(soap/php_sdl.c 源代码不处理 wsdl2.0 格式)

wsdl 2.0 只是 1.2 版本,因为它与 WSDL 1.1 存在重大差异,因此被重命名。

如果您不太在意,两种格式之间的区别可能并不明显。

wsdl 1.0 格式结构(参见 http://www.w3.org/TR/wsdl
<definitions ...>
<types ...>
</types>
<message ...>
<part ...>
</message>
<portType ...>
<operation ...>
<input ... />
<output ... />
<fault ... />
</operation>
</portType>
<binding ...>
<operation ...>
<input ... />
<output ... />
<fault ... />
</operation>
</binding>
<service ...>
<port ...>
</service>
</definitions>

而 wsdl 2.0 格式结构(参见 http://www.w3.org/TR/wsdl20/
<description ...>
<types ...>
</types>
<interface ...>
<fault ... />
<operation ...>
<input ... />
<output ... />
<fault ... />
</operation>
</interface>
<binding ...>
<fault ... />
<operation ...>
<input ... />
<output ... />
<fault ... />
</operation>
</binding>
<service ...>
<endpoint ...>
</service>
</description>

如果您提供 wsdl 2.0 格式文件,典型的错误消息是
PHP Fatal error: SOAP-ERROR: Parsing WSDL: Couldn't find <definitions> in 'wsdl/example.wsdl' in /path/client.php on line 9
0
Luke
8 年前
调用 asmx 方法,例如 $success=$x->AuthenticateUser($userName,$password),这会返回一个错误。

但是我改变了它,并将用户名和密码添加到数组中,现在它可以正常工作了...
0
moazzam at moazzam-khan dot com
14 年前
如果有人尝试使用它来访问 Sabre 的 Web 服务,它将无法工作。Sabre 检查请求头“Content-Type”以查看它是否为“text/xml”。如果不是 text/xml,则它会返回一个错误。

您需要创建一个套接字连接并使用它来发送请求。
-2
stephenlansell at gmail dot com
14 年前
这是一个 php 客户端与 asmx 服务器通信的示例

<?php

$soapClient
= new SoapClient("https://soapserver.example.com/blahblah.asmx?wsdl");

// 准备 SoapHeader 参数
$sh_param = array(
'Username' => 'username',
'Password' => 'password');
$headers = new SoapHeader('http://soapserver.example.com/webservices', 'UserCredentials', $sh_param);

// 准备 Soap 客户端
$soapClient->__setSoapHeaders(array($headers));

// 设置远程函数参数
$ap_param = array(
'amount' => $irow['total_price']);

// 调用远程函数 ()
$error = 0;
try {
$info = $soapClient->__call("RemoteFunction", array($ap_param));
} catch (
SoapFault $fault) {
$error = 1;
print(
"
alert('抱歉,blah 返回以下错误:"
.$fault->faultcode."-".$fault->faultstring.". 我们现在将带您回到我们的主页。');
window.location = 'main.php';
"
);
}

if (
$error == 0) {
$auth_num = $info->RemoteFunctionResult;

if (
$auth_num < 0) {
....

// 设置其他远程函数 () 参数
$at_param = array(
'amount' => $irow['total_price'],
'description' => $description);

// 调用其他远程函数 ()
$trans = $soapClient->__call("OtherRemoteFunction", array($at_param));
$trans_result = $trans->OtherRemoteFunctionResult;
....
} else {
// 在数据库中记录交易错误

// 终止与 Soap 的连接
unset($soapClient);
}
}
}
}

?>
-5
rafinskipg at gmail dot com
12 年前
支持 MTOM,将此代码添加到您的项目中

<?php
class MySoapClient extends SoapClient
{
public function
__doRequest($request, $location, $action, $version, $one_way = 0)
{
$response = parent::__doRequest($request, $location, $action, $version, $one_way);
// 解析 $response,提取多部分消息等

// 这部分删除了一些东西
$start=strpos($response,'<?xml');
$end=strrpos($response,'>');
$response_string=substr($response,$start,$end-$start+1);
return(
$response_string);
}
}

?>

然后您可以执行此操作
<?php
new MySoapClient($wsdl_url);
?>
-8
dennis at pleasedontspam dot clansman dot nl
12 年前
当您收到 soapfault 消息:“看起来我们没有获得 XML 文档”时,可能是您正在与支持 MTOM 的服务器进行通信 (http://www.w3.org/TR/soap12-mtom/).

到目前为止 (5.3.6),SoapClient 还不支持 MTOM。
-9
spectrumcat at gmail dot com
9 年前
如果您要为您的 SOAP 客户端(或任何其他客户端)获取 .p12 证书,请确保将其转换为 PEM 并与私钥合并。

将 p12 转换为包含合并私钥的 PEM
openssl pkcs12 -in supplied_cert.p12 -out php_soap_cert.pem -clcerts

示例
$wsdl = "./mywsdl.wsdl"; // 或 "http://provider.com/api/api.wsdl"

$options = [
'location' => "http://provider.com/api/",
'local_cert' => "./php_soap_cert.pem",
'passphrase' => "certificate_password",
];

try {
$soapClient = new SoapClient($wsdl, $options);
} catch(Exception $e) {
var_dump($e);
}
-11
aeg at mallxs dot nl
16 年前
SoapFault 异常:[SOAP-ENV:Client] 似乎我们没有获得 XML 文档

此错误可能有以下两个原因
1: 您的服务器脚本有一些隐藏的输出,例如 <?php ... ?> 之前或之后的空间,或者回显了一些文本
2: 您的服务器脚本中存在 bug;
导致错误消息输出

通过从浏览器直接调用您的服务器脚本,测试您的服务器脚本。
结果应该是干净的输出,例如

−<SOAP-ENV:Envelope>
−<SOAP-ENV:Body>
−<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>错误的请求。找不到 HTTP_RAW_POST_DATA</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

这表明有一个请求,但没有数据;这是正常的
现在,如果您在使用 SOAP 客户端调用时遇到错误;
请记住,您的服务器现在将调用 server.php 脚本中的其他函数,因为它现在有一些要处理的数据
-12
vishal dot thakkar at exateam dot com
14 年前
我花了几天时间才弄明白如何使 SOAP 服务器返回一个关联数组而不是对象...

要在 SOAP 响应中获得数组(而不是对象),只需在将数组返回给客户端之前对其进行序列化即可。

希望对您有所帮助...
谢谢.....
Vishal Thakkar。
-12
jeff at jeffrodriguez dot com
15 年前
一旦您知道如何操作,命名参数实际上非常容易。大多数情况下,无需理会 SoapParam 或 SoapVar。

代码
<?php
$ws
->getAsset(array(
'assetId' => 5967
));
?>

生成以下 SOAP 主体:(为可读性而格式化)
<SOAP-ENV:Body>
<ns1:getAsset>
<assetId>5967</assetId>
</ns1:getAsset>
</SOAP-ENV:Body>
-9
mariamr at gmail dot com
13 年前
“SOAP-ERROR: Parsing WSDL: Couldn’t find in ‘PATH/TO/YOUR/WSDL’”

发生此错误是因为某些版本的 PHP SOAP 客户端不会发送您作为 SoapClient 构造函数的选项输入参数传递的用户名和密码。您需要在 Web 服务 URL 中传递凭据,如下所示
$soapClient =new SoapClient(“http://user:[email protected]/blahblah.wsdl”);
-18
beau dot scott at gmail dot com
15 年前
如果你尝试向一个将空属性标签视为空字符串而不是 null 的服务发送 null,这里有一个扩展类可以利用 xsi:nil 来解决这个问题。

<?php
class XSoapClient extends SoapClient
{
const
XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
const
_NULL_ = "xxx_replacedduetobrokephpsoapclient_xxx";

protected
$mustParseNulls = false;

public function
__doRequest($request, $location, $action, $version, $one_way = null)
{
if(
$this->mustParseNulls)
{
$this->mustParseNulls = false;
$request = preg_replace('/<ns1:(\w+)>'.self::_NULL_.'<\/ns1:\\1>/',
'<ns1:$1 xsi:nil="true"/>',
$request, -1, &$count);

if (
$count > 0)
{
$request = preg_replace('/(<SOAP-ENV:Envelope )/',
'\\1 xmlns:xsi="'.self::XSI_NS.'" ',
$request);
}
}
return
parent::__doRequest($request, $location, $action, $version, $one_way);
}

public function
__call($method, $params)
{
foreach(
$params as $k => $v)
{
if(
$v === null)
{
$this->mustParseNulls = true;
$params[$k] = self::_NULL_;
}
}
return
parent::__call($method, $params);
}
}
?>
-16
ivan dot cubillas at xeridia dot com
16 年前
PHP WebService 客户端的问题

我们有一个可能附带文档的 JAVA WebService 服务器。当 PHP 客户端发送日期时,服务器可以正确接收,但是当
服务器将数据返回给 PHP 客户端时,客户端总是回复说这不是一个有效的 XML 文档。

问题可能是 JAVA 服务器添加了 (对于这种类型的 XML 二进制文件) documentType 头:xml+xop,而客户端无法
识别此头作为 XML 文档。

有没有办法在 PHP 客户端解决这个问题,而无需对 JAVA 服务器进行任何更改?
To Top