请注意,如果您需要设置 soap 请求的超时时间,可以使用 ini_set 更改 default_socket_timeout 的值。我之前使用过 NuSOAP,它的 soap 客户端类有一个超时选项,我花了一段时间才弄清楚 PHP 的 soap 使用与其他所有内容相同的套接字选项。
请注意,如果您需要设置 soap 请求的超时时间,可以使用 ini_set 更改 default_socket_timeout 的值。我之前使用过 NuSOAP,它的 soap 客户端类有一个超时选项,我花了一段时间才弄清楚 PHP 的 soap 使用与其他所有内容相同的套接字选项。
要使用 SoapServer()、WSDL 文件和 Zend Studio 客户端/服务器调试 SOAP 服务,您必须将 ?start_debug=1&debug_port=10000 附加到服务位置
--- 剪辑 ---
... 方法/服务定义 ....
<service name="SOAPService">
<port
name="SOAPServicePort"
binding="typens:SOAPServiceBinding">
<soap:address
location="$URL?start_debug=1&debug_port=10000"/>
</port>
</service>
--- 剪辑 ---
使用 WSDL 模式下的 PHP SoapServer 通过 SOAP 传递复杂类型时遇到问题?解码不正确?这可能是您正在寻找的解决方案!
在 WSDL 文件的模式部分使用 ComplexType 时,您需要使用一个额外的步骤来告诉 PHP SOAP 如何对对象进行编码。第一个方法是将对象显式地封装在 SoapVar 对象中——告诉 PHP 使用广义的 SOAP 编码规则(将所有 ComplexType 编码为结构)。但是,如果客户端期望对象根据 WSDL 的模式进行编码,则此方法将不起作用。因此,实际的方法是
* 首先,定义一个特定的 PHP 类,它实际上只是一个数据结构,它保存着各种属性,以及 WSDL 中的适当 ComplexType。
<?php
class MyComplexDataType {
public $myProperty1;
public $myProperty2;
}
?>
<complexType name="MyWSDLStructure">
<sequence>
<element name="MyProperty1" type="xsd:integer"/>
<element name="MyProperty2" type="xsd:string"/>
</sequence>
</complexType>
* 接下来,在初始化 SoapServer 时告诉它将这两个结构映射在一起。
<?php
$classmap = array('MyWSDLStructure' => 'MyComplexDataType');
$server = new SoapServer("http://MyServer/MyService.wsdl", array('classmap' => $classmap))
?>
* 最后,让您的方法直接返回您的类的实例,并让 SoapServer 处理编码!
<?php
public function MySoapCall() {
$o = new MyComplexDataType();
$o->myProperty1 = 1;
$o->myProperty2 = "MyString";
return $o
}
?>
处理 wsdl 和 flash soap 与 php 时
(不完全是 php,但适当的 wsdl - 我花了几个小时才让它在 flash webservice 中正常工作)
命名定义时,
不要使用“tns:”而是使用“typens:”
确保您的“definitions/targetNamespace”
与您的“soap:body”命名空间相同
示例:“urn:mynamespace”
确保您的“binding/type”已使用“typens”声明
确保您的 service/port/binding 设置为 'typens:...'
如果您操作不当,您将最终得到
WSDL.UnrecognizedNamespace - 在 flash 中
但在 php soapclient 中似乎没问题...
编码愉快 :)
希望这可以帮助您节省 wsdl 上的时间 :)
如果您不想手动维护 classmap,请确保您的 PHP 对象类和 WSDL complexType 使用相同的名称,并使用以下代码
$classmap = array();
$tmpClient = new SoapClient("soapserver.wsdl");
foreach($tmpClient->__getTypes() as $type)
{
$array = split(" ", $type);
if($array[0] == "struct" && class_exists($array[1]))
{
$classmap[$array[1]] = $array[1];
}
}
unset($tmpClient);
$server = new SoapServer("soapserver.wsdl", array("classmap" => $classmap));
unset($classmap);
$server->setClass("someclass");
$server->handle();
我希望这能为某人节省时间。在开发和测试您的 SOAP 服务器时,请记住在客户端和服务器中都禁用 WSDL 缓存
$ini = ini_set("soap.wsdl_cache_enabled", 0);
如果您正在
- 本地主机上
- 在 WSDL 模式下
并且在尝试使用 soap 客户端连接到 soap 服务器时遇到以下错误
致命错误:未捕获的 SoapFault 异常: [HTTP] 无法连接到主机...
然后在您的 WSDL 文件中将“localhost”更改为“127.0.0.1”
<soap:address location='http://127.0.0.1/soap-server.php'/>
wokan at cox dot net 对通过 HTTPS URI 传递给 HTTPS URI 的值的安全性不正确。HTTPS 连接是 SSL 内部的 HTTP - 所有 HTTP 流量,包括请求,都被加密。
如果您对为什么 NuSOAP 在 PHP 5.x 上无法正常工作感到困惑,原因是此内置的 SOAP 扩展使用与 Nusoap 相同的 soapclient() 类名。
在 nusoap.php 中将 'soapclient' 替换为 'soapclient_xxx' 就可以了...
遇到以下错误消息时
[faultstring] => 函数 (“yourMethod”) 不是此服务的有效方法
尽管它存在于 WSDL 等中,但请注意,PHP 会将 wsdl 缓存在本地以提高性能。您可以通过 php.ini/.htaccess 完全禁用缓存,或者删除缓存文件(如果您在 Linux 上,则为 /tmp/wsdl-..)以强制重新生成它。
提醒任何使用 PHP Soap + 会话 + PEAR DB 类的人。
每次您通过 soap 客户端对您的 web 服务进行调用时,您的 PEAR DB 会话都会被挂起,并且默认情况下不会在下一个请求时唤醒。
要解决此问题,我只需在我的 $soap->handle(); 下面调用我特定的数据库关闭调用 ifx_close()。
在 Windows 配置下使用 SOAP 时使用证书的人员请注意:似乎需要提供证书文件的完整路径 - 并且不要在前面加上 'file://'
<?php
$wsdl = "test.wsdl";
$local_cert = "c:\htdocs\mycert.pem";
$passphrase = "xyz";
$client = new SoapClient($wsdl, array('local_cert' => $local_cert, 'passphrase' => $passphrase);
?>
哇,实际上是一个很酷的程序,soap 对我来说是新的。
我发现了一些有趣的事情,我无法调试,因为脚本会在没有任何错误消息或笔记的情况下退出。:-(
在高服务器负载的情况下,尤其是在“共享服务器”上,您可能会遇到内存问题。
有时脚本可以正常运行,有时它会在未知位置停止。
以下是我的脚本执行的步骤:
* 从远程服务器获取数据(约 4.5 MB)
* 解析请求的对象并将数据存储在数据库中。
带调试信息的返回值很有趣:
-> 检查内存限制:30M
-> $client = new new SoapClient($url_wsdl, $options);
-> 内存使用量:185888
-> $client->[requested_method_to_get_data]();
-> 检查:__getLastResponseHeaders() - 之后
-> HTTP/1.1 200 OK // 远程服务器一切正常 :-)
-> Content-Length: 4586742 // 我已经获取了数据
-> 检查:当前内存使用量:23098872 // 糟糕!!!不可能!!!
所以,如果现在服务器上的其他人占用剩余的 RAM,数据遍历就会中断 :-(
因此,我需要存储 XML 树 ($client->client->__last_response) 并以传统方式解析它。(如果你需要更多 RAM,如果你经常运行这样的脚本,你可能会遇到管理员的麻烦!(在共享服务器上))
这可能会有所帮助,因为我花了相当长的时间才发现这一点。
如果你正在使用一些 .wsdl 文件,并且其中存在可以多次出现的序列(例如:maxOccurs > 1),如果你有多个项目,你可以为它指定一个非关联数组,或者你也可以只指定一个项目,如果只有一个。
<?php
'items' => array(
array(
'itemId' => 5,
'name' => 'some name',
),
array(
'itemId' => 6,
'name' => 'some other name',
),
),
?>
这也行。
<?php
'items' => array(
'itemId' => 5,
'name' => 'some name',
),
?>
这里有 73 个测试用例,详细说明了 PHP5 当前支持的架构类型以及您应该插入它们的结构,以作为服务返回值。 比猜测好多了!
http://cvs.php.net/co.php/pecl/soap/tests/schema
您可以通过更改 URL 中的主索引来循环浏览列表,而不是进出页面。
http://cvs.php.net/co.php/pecl/soap/tests/schema/schema052.phpt?r=1.2
我用这个下载了整个内容,CVS 也可能有用。
http://www.httrack.com/
下载完所有内容后,我用 Textpad 浏览它们。
想知道为什么您刚刚添加到 WSDL 文件中的函数在 SOAP 客户端中不可用吗? 关闭 WSDL 缓存,它(正如文档所说)默认情况下是开启的。
在脚本的顶部,使用
$ini = ini_set("soap.wsdl_cache_enabled","0");
问题(在 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()。
关于 “DTD 无法识别...” 错误的说明。 确保您的 wsdl 文件包含以下内容:
<?xml version ='1.0' encoding ='UTF-8' ?>
还要确保您使用服务的完整路径(在 wsdl、客户端和服务器中)。
...
<wsdlsoap:address location='http://www.mysite.com.au/web_services/myserver.php' />
...
<?php
// SOAP 服务器
$server = new SoapServer('http://www.mysite.com.au/web_services/hello.wsdl');
...
...
?>
<?php
// SOAP 客户端
$client = new SoapClient('http://www.mysite.com.au/web_services/hello.wsdl');
...
...
?>
供您参考,我不是 SOAP 专家,但我希望这能帮到某人 ;)
与第三方服务合作时,我遇到了以下错误:“SOAP-ERROR: Parsing Schema: unexpected <text> in restriction”。
我觉得值得分享。 它表示在无效的位置存在一些文本。 C# 似乎忽略了它,如果您使用 nusoap,它也不会注意到。 但导致我问题的原因是类似以下内容:
<types>
<schema ...
<simpleType name="some type">
<restriction base="xsd:string">
<enumeration value="foo"/>;
</restriction>
</simpleType>
注意分号(;)。 过滤掉它,一切就正常了。
如果您使用 wsdl,
请确保正确定义输入。
如果您的方法不包含任何输入参数,
您必须确保您
- 不要为输入创建 message 标签。
- 不要将输入放在 porttype/operation 中。
- 不要将输入放在 binding/operation 中。
否则,您将收到以下错误:
[Client] Looks like we got no XML
天哪,我花了好几个小时才弄明白...
对于那些使用充满复杂类型 wsdl 的人来说,他们只是想要一个类结构来挂钩他们的代码,而不必担心输入冗长的参数列表(或创建脚本来完成此操作):wsdl2php 是一个很棒的省时工具。 它会创建一个结构,以便您可以添加所需的验证和特殊数据处理:http://www.urdalen.no/wsdl2php/
干得好,Knut。
如果您使用 SSL 与证书和密码身份验证
$wsdl = "https://ws.ecopatz.de/ProductInfo?wsdl";
$pass = 'a password';
$certFile = "./mycert.pem";
$client = new SoapClient($wsdl,
array(
'local_cert' => $certFile,
'passphrase' => $pass
)
);
如果您遇到证书文件问题,例如以下情况
Warning: SoapClient::__construct(): Unable to set local cert chain file `./mycert.pem'; Check that your cafile/capath settings include details of your certificate and its issuer in productinfo.php on line 27
那么证书文件可能“格式错误”(可能对 php 来说格式错误)。 当我将私钥文件的内容和证书文件的内容追加到单个文件“mycert.pem”中时,它对我有用。
cat mycert.key >mycert.pem # mycert.key 是私钥
cat mycert.crt >>mycert.pem # mycert.crt 是签名证书
感谢某位作者,他指出了 “curl --cert”,在那里提到了这个“不那么重要”的依赖关系。
如果您想为 Microsoft Office 的客户端(如 Microsoft Office Research Service)构建一个 Soap Server,您需要重写 SOAP 的命名空间。
<?php
// (...)
$server = new SoapServer($wsdl, array('uri' => $uri, 'classmap' => $classmap));
$server->setClass($class);
function callback($buffer)
{
$s = array('<ns1:RegistrationResponse>', 'ns1:', 'xmlns:ns1="urn:Microsoft.Search"');
$r = array('<RegistrationResponse xmlns="urn:Microsoft.Search">', '', '');
return (str_replace($s, $r, $buffer));
}
ob_start('callback');
$server->handle();
ob_end_flush();
// (...)
?>
在这个 URL 上有一个完整的示例:http://touv.ouvaton.org/article.php3?id_article=104
这是一个关于如何传递 ArrayOfAnyType 参数的示例
包含复杂类型。
假设您的 WSDL 文件将 "http://any.url.com/" 定义为默认命名空间,以及一个复杂类型 "SomeComplexType"。
如果您想调用一个 WebServices,它接受一个包含 "SomeComplexType" 的 ArrayOfAnyType 参数,您需要执行以下操作
<?php
// complexTypes 是一个包含多个 SomeComplexType 实例的数组
myWSParameter = array();
foreach (complexTypes as ct)
{
// 不要拼错类型或命名空间。还要注意,php 不会假设在 WSDL 文件中定义的默认命名空间。
myWSParameter []= new SoapVar(ct, 0, "SomeComplexType", "http://any.url.com/");
}
?>
另一方面,当一个 WebService 返回一个 ArrayOfAnyType 时,您需要执行以下操作来访问它的每个元素。
<?php
// 在这里,我们将回显每个返回项
$res = $someWS->myFunction($myArgs)
// 如果只返回一个元素,则不会构建数组
if (is_array(myFunctionResult->anyType))
{
foreach (myFunctionResult->anyType as $soapVar)
{
echo $soapVar->enc_value;
}
}
else
{
echo myFunctionResult->anyType->enc_value;
}
?>
这些都已使用 .NET WebService 进行测试。
如果您尝试使用带自定义 PEM 文件的 SOAP 扩展通过 SSL 进行操作,您需要执行以下操作
$client->_local_cert = "C:\\path\myCert.pem";
如果您在调用 .NET web service 时遇到问题,请查看 https://php.net/soap_soapclient_soapcall 上的注释(__soapCall 方法)。
在 URI 中传递用户名和密码不是一个好的安全做法,因为 SSL 的目的是防止这些信息被拦截。将这些信息放入 URI 会导致它们被拦截。HTTPS-Posted 值是安全的,因为在 SSL 握手完成之后才会发送头文件中传递的值。
我花了一段时间才在 windows/apache1.3 上通过 https 建立一个受密码保护的客户端连接。以下是我的简要指南
1. SOAP 扩展默认情况下未启用(PHP5 RC1)。只需在 php.ini 中添加 "extension=php_soap.dll",不要忘记正确设置 extension_dir(在大多数情况下为 "c:\php\ext")。
2. 在 php.ini 中添加 "extension=php_openssl.dll"。此模块依赖于 libeay32.dll 和 ssleay32.dll - 将它们从您的 php 文件夹复制到您的 system32 文件夹。
3. 重新启动 apache
4. 源代码
$client = new SoapClient("https://yourLogin:[email protected]/bar.wsdl", array(
"login" => "yourLogin",
"password" => "yourPassword",
"trace" => 1,
"exceptions" => 0));
$client->yourFunction();
print "<pre>\n";
print "Request: \n".htmlspecialchars($client->__getLastRequest()) ."\n";
print "Response: \n".htmlspecialchars($client->__getLastResponse())."\n";
print "</pre>";
目前似乎有必要在 uri 和选项数组中都添加您的登录名和密码。不确定这是否是预期的行为。
我的电脑上装的是 PHP 5.2.5。我得到以下结果
Array
(
[0] => stdClass Object
(
[Client] => stdClass Object
(
[Nom] => LeNom:00000
[Prenom] => LePrenom:00000
)
)
我的主机是 5.1.6,相同的调用返回以下结果
Array
(
[0] => stdClass Object
(
[Client] => Array
(
[0] => stdClass Object
(
[Nom] => LeNom:00000
[Prenom] => LePrenom:00000
)
5.2.5 是正确的结构。
注意您使用的版本。更改所有代码可能会花费大量时间。
对于那些想知道如何在 PHP5 SOAP 中设置节点属性的人,可以这样做
<... soap env/header>
<foo bar="blah">12345</foo>
array("foo" => array("_" => 12345, "bar" => "blah"));