SoapClient::__soapCall

(PHP 5, PHP 7, PHP 8)

SoapClient::__soapCall调用 SOAP 函数

描述

public SoapClient::__soapCall(
    string $name,
    array $args,
    ?array $options = null,
    SoapHeader|array|null $inputHeaders = null,
    array &$outputHeaders = null
): mixed

这是一个低级 API 函数,用于进行 SOAP 调用。通常,在 WSDL 模式下,SOAP 函数可以作为 SoapClient 对象的方法来调用。此方法在非 WSDL 模式下很有用,此时 soapaction 未知,uri 与默认值不同,或者在发送和/或接收 SOAP 标头时。

发生错误时,调用 SOAP 函数会导致 PHP 抛出异常,或者如果禁用异常,则返回一个 SoapFault 对象。若要检查函数调用是否失败,请使用 is_soap_fault() 函数捕获 SoapFault 异常。

参数

name

要调用的 SOAP 函数的名称。

args

要传递给函数的参数数组。它可以是顺序数组,也可以是关联数组。请注意,大多数 SOAP 服务器要求提供参数名称,在这种情况下,它必须是关联数组。

options

要传递给客户端的选项的关联数组。

location 选项是远程 Web 服务的 URL。

uri 选项是 SOAP 服务的目标命名空间。

soapaction 选项是要调用的操作。

inputHeaders

要与 SOAP 请求一起发送的标头数组。

outputHeaders

如果提供,此数组将填充来自 SOAP 响应的标头。

返回值

SOAP 函数可以返回一个或多个值。如果 SOAP 函数只返回一个值,则返回值将是标量。如果返回多个值,则返回一个包含命名输出参数的关联数组。

发生错误时,如果 SoapClient 对象是在 exceptions 选项设置为 false 的情况下构造的,则将返回一个 SoapFault 对象。

示例

示例 #1 SoapClient::__soapCall() 示例

<?php

$client
= new SoapClient("some.wsdl");
$client->SomeFunction($a, $b, $c);

$client->__soapCall("SomeFunction", array($a, $b, $c));
$client->__soapCall("SomeFunction", array($a, $b, $c), NULL,
new
SoapHeader(), $output_headers);


$client = new SoapClient(null, array('location' => "http://localhost/soap.php",
'uri' => "http://test-uri/"));
$client->SomeFunction($a, $b, $c);
$client->__soapCall("SomeFunction", array($a, $b, $c));
$client->__soapCall("SomeFunction", array($a, $b, $c),
array(
'soapaction' => 'some_action',
'uri' => 'some_uri'));
?>

参见

添加注释

用户贡献注释 20 个注释

96
vb
11 年前
请注意,调用 __soapCall 和从 WSDL 调用生成的函数需要以两种不同的方式指定参数。

例如,如果您有一个带有 login 函数的 Web 服务,它接受用户名和密码,您可以通过以下方式调用它
<?php
$params
= array('username'=>'name', 'password'=>'secret');
$client->login($params);
?>

如果您要调用 __soapCall,您必须将参数包装在另一个数组中,如下所示
<?php
$client
->__soapCall('login', array($params));
?>
9
arturklesun at gmail dot com
8 年前
分享我的经验,因为我认为如果您决定使用此 Soap 客户端实现,它对您来说是最重要的。

在 php 7.0.8 版本中,SoapClient 从响应中生成的 stdClass 对象不使用 “minOccurs” 和 “maxOccurs” WSDL 修饰符来区分 stdClass 对象中的属性(即“关联数组”中的键)和数组中的元素(即“索引数组”中的元素)。

相反,实现决定标签是 _关联数组中的键_ 还是 _索引数组中具有相同标签的元素之一_ 的依据仅仅是序列中是否存在只有一个具有该标签的元素还是多个元素。

考虑我的情况中的一个实际例子。
<?php
响应 XML
:
...
<
ValidatingCarrier>
<
Alternate>AA</Alternate>
</
ValidatingCarrier>
...
<
ValidatingCarrier>
<
Alternate>AA</Alternate>
<
Alternate>AY</Alternate>
</
ValidatingCarrier>
...

SoapClient 生成的响应结构:
...
[
ValidatingCarrier] => stdClass Object(
[
Alternate] => AA // 这里它是一个字符串
)
...
[
ValidatingCarrier] => stdClass Object (
[
Alternate] => Array ( // 而这里它是一个数组
[0] => AA
[1] => AY
)
)
...

字段 XSD 定义:
<
xs:element name="Alternate" type="CarrierCode" minOccurs="0" maxOccurs="24">
?>

您会发现,XSD 中的定义告诉我们这个标签可以重复最多 24 次,这意味着它将是一个索引数组,但 SoapClient 并没有考虑这一点,而是将示例中的第一个 <Alternate> 视为一个值,而不是包含该值的数组。

毫无疑问,只有当 maxOccurs 为 1 或更小或未指定时(默认值为 1,请参见 https://www.w3.org/TR/xmlschema-0/#OccurrenceConstraints),一个值才应该是 stdClass 中的属性(关联数组中的键)。我希望这些信息在您实现自己的正确工作的 SoapClient 时对您有所帮助。
25
Shto
15 年前
需要注意的一点。

这种情况发生在我身上,我花了一段时间才发现问题所在。

我试图从提供的 Web 服务中获取 .NET 对象,但它似乎总是返回空对象。它确实返回了主干,但没有返回构成结构的任何对象内部内容。

无论如何,似乎在调用这些函数时,您必须对数组非常精确。例如,执行以下操作

<?php
$obj
= $client->__soapCall($SOAPCall, array('parameters'=>$SoapCallParameters));
?>

这意味着您必须使用一个数组作为第二个参数,其中 “parameters” 作为键,SOAP 调用参数作为值。

还要确保参数变量(在我的情况下为 $SoapCallParameters)采用 Web 服务请求的形式。

所以,不要仅仅创建一个以下形式的数组
<?php

(
[
0] => 'Mary',
[
1] => 1983
)

?>

但如果 Web 服务请求一个名为 “muid” 的变量,值为 “Mary”,以及一个名为 “birthyear” 的变量,值为 1983,那么将您的数组创建为以下形式

<?php

(
[
muid] => 'Mary',
[
birthyear] => 1983
)

?>

上面的数组指的是 $SoapCallParameters 变量。

希望这对某些人有所帮助,让他们不必花太多时间来找出问题。
8
DesmondJ
19 年前
根据 OrionI 的示例

<?php
$client
= new SoapClient("http://server/sumservice.asmx?WSDL");
$params->a = 2;
$params->b = 3;
$objectresult = $client->Sum($params);
$simpleresult = $objectresult->SumResult;
print(
$simpleresult); // 输出 "-1"
?>

请注意,以下几行

"$client->Sum($params);"

以及

"$simpleresult = $objectresult->SumResult;"

是相互关联的。如果您的 Web 服务函数名为 “Sum”,那么在函数名末尾添加 “Result” 来获取调用结果。

例如

<?php
$client
= new SoapClient("http://server/mathservice.asmx?WSDL");
$params->a = 2;
$params->b = 3;
$objectresult = $client->Minus($params); // 注意函数名是 “Minus”
$simpleresult = $objectresult->MinusResult; // 注意结果名引用为 “MinusResult”
print($simpleresult); // 输出 "5"
?>
8
ub at sturmundbraem dot ch
8 年前
为了避免 SOAP 客户端有时返回对象,有时返回对象数组,有一个设置

$this->soapClient = new \SoapClient($wsdlUrl, array(
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
'trace' => DEBUG_SOAP
));
3
paulsjv at gmail dot com
18 年前
我第一次使用 SOAP,我必须创建一个客户端,将日期范围发送到 WSDL(Web 服务描述语言)以返回我需要的一些信息。我不知道如何传递参数,而且实际上没有关于它的文档。您需要确保的是,当您将参数传递给由您调用的 WSDL 定义的方法时,您使用与数组键或对象变量相同的参数名称,如下所示。此外,如果您不知道 WSDL 具有的方法/函数或需要传递的参数,您可以在声明新的 SoapClient 后使用 __getFunctions() 和 __getTypes() 方法。

<?php
// From 和 to 是 execute 函数需要用到的两个参数
// 当从 WSDL 调用时,因此确保它们作为
// 数组的键,如下所示
$params["From"] = "06/01/2005"; // 也可以使用 $params->From = "date";
$params["to"] = "12/31/2005"; // 也可以使用 $params->to = "date";

$client = new SoapClient("some.wsdl");

try {
print(
$client->execute($params));
} catch (
SoapFault $exception) {
echo
$exception;
}
?>
4
OrionI
19 年前
当通过 SOAP 调用 .NET 应用程序时,您最终可能会得到一个对象作为结果,而不是一个简单类型,即使您只是获取一个简单类型(例如布尔结果)。使用属性访问器来获取实际结果,如下所示
<?php
$client
= new SoapClient("http://server/myservice.asmx?WSDL");
$objectresult = $client->MyMethod($param1, $param2);
$simpleresult = $objectresult->MyMethodResult;
?>
请注意,.NET 似乎将结果命名为 MethodNameResult,对应于方法 MethodName。
4
snuufix+nospam at gmail dot com
13 年前
我正在使用 SOAP 调用响应头来签署请求结果。

在经过几个小时的努力后,我终于找到了获取 SOAP 响应头的最佳方法(除了解析 __getLastResponse(),它需要启用跟踪选项),那就是使用 __soapCall() 包装器。

您可以扩展 SoapClient 类并包装一些函数,以确保您获取了头信息。

<?php

class API extends SoapClient
{

// ... 构造函数等

// 获取 SOAP 服务器响应头
public function __soapCall($function, $arguments, $options = array(), $input_headers = null, &$output_headers = null)
{
parent::__soapCall($function, $arguments, $options, $input_headers, $output_headers);

print_r($output_headers); // 响应头的数组
}

// 如果您使用的是 WSDL,则需要此方法,以便您仍然可以调用函数,而无需手动调用 __soapCall
public function __call($func, $args)
{
return
$this->__soapCall($func, $args);
}

?>
1
OrionI
19 年前
之前提交的代码片段的修正...传入 .NET 的参数也必须以对象或数组的形式,以便正确转换为 .NET 预期的 XML 格式(正如 Llu?s P?mies 已经提到的)。使用 WSDL 时,完整的示例应如下所示
<?php
$client
= new SoapClient("http://server/myservice.asmx?WSDL");
$params->param1 = $value1;
$params->param2 = $value2;
$objectresult = $client->MyMethod($params);
$simpleresult = $objectresult->MyMethodResult;
?>
如果您有一个像这样的 C# 函数
//sumservice.asmx
...
[WebMethod]
public int Sum(int a, int b)
{
return a + b;
}
...
PHP 客户端将是这样的
<?php
$client
= new SoapClient("http://server/sumservice.asmx?WSDL");
$params->a = 2;
$params->b = 3;
$objectresult = $client->Sum($params);
$simpleresult = $objectresult->SumResult;
print(
$simpleresult); // 输出 "5"
?>
7
Tim Williams
15 年前
当传递需要属性和简单类型值的参数时,有一个小技巧

要获得 xml

<foo bar="moo">cheese</foo>

您将传入

<?php
array("foo" => array("_" => "cheese", "bar"=>"moo"));
?>

看到那个 "_" 部分了吗?从文档中并不明显。
2
james dot ellis at gmail dot com
14 年前
如果您使用此方法,请记住,参数数组需要按与 SOAP 端点期望的相同顺序传递。

例如
<?php
// 服务器期望: Foo(string name, int age)

// 不起作用
$args = array(32, 'john');
$out = $client->__soapCall('Foo', $args);

// 将起作用
$args = array('john', 32);
$out = $client->__soapCall('Foo', $args);
?>
4
stefan at datax dot biz
16 年前
对 __soapCall 的调用也向我返回了一个对象。这是使我的生活更轻松的函数

function obj2array($obj) {
$out = array();
foreach ($obj as $key => $val) {
switch(true) {
case is_object($val)
$out[$key] = obj2array($val);
break;
case is_array($val)
$out[$key] = obj2array($val);
break;
default
$out[$key] = $val;
}
}
return $out;
}

用法
...
$response = $client ->__soapCall("track", array('parameters' => $request));
$response = obj2array($response);

希望它有帮助。
0
deWarlock
15 年前
最烦人的是,您在尝试传递与 wsdl 不匹配的对象时不会收到任何警告,例如,如果服务器期望 smth 像 $Object->expName->...,而您传递 $Object->otherName,客户端将发送空请求而不会通知您。
还要注意名称区分大小写。

在我的情况下,我花了几个小时试图传递 $Post->Lead->... 对象而不是 $Post->lead-> ...
0
Llu?s P?mies
19 年前
如果您的服务是 .NET doc/lit,这意味着输入消息只有一个名为 'parameters' 的部分,它是包含参数的结构。您的调用应该像这样

<?php

$params
= array('param_name_1'=>$val_1,'param_name_2'=>$val_2);
$client->call('MethodName', array('parameters' => $params));

?>
-3
ryan at grunt dot tv
18 年前
如果您想将 xml 文档节点作为函数参数传递,则需要使用 xml 节点的文本表示和 XSD_ANYXML 编码常量创建一个 SoapVar 对象。但是,此常量未被扩展导出,并且出于某种未知原因没有记录。

因此,要使其正常工作,您必须注册 XSD_ANYXML #define 作为 PHP 常量,或者在创建 SoapVar 时使用常量的整数值,即 147。

$soapvar = new SoapVar($xml_text, 147);

$params = array("ItemXml" => $soapvar, "PropertyView" => "blah");
$result = $this->soapclient->__soapCall("SaveItem", array("parameters"=>$params), null, $this->soapheaders);

但是,这仍然没有给出正确的结果。出于某种原因,ItemXml 参数节点没有被包裹在与之关联的 xml 参数中,并且生成了以下 soap(假设 '<item>blah</item>' 用作 $xml_text)

<SOAP-ENV:Envelope xmlns:SOAP-ENV="..." xmlns:ns1="...">
<SOAP-ENV:Header>...</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:SaveItem>
<item>blah</item>
<ns1:PropertyView>blah</ns1:PropertyView>
</ns1:SaveItem>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
-2
Alejandro Cavallo
16 年前
我遇到了上面 ryan 报告的相同问题
"ryan at grunt dot tv
22-Sep-2005 01:38
如果您想将 xml 文档节点作为函数参数传递,则需要使用 xml 节点的文本表示和 XSD_ANYXML 编码常量创建一个 SoapVar 对象。但是,此常量未被扩展导出,并且出于某种未知原因没有记录."

在定义变量 soapVar 时,我将类型设置为 XSD_STRING 而不是 XSD_ANYXML。
并且在调用函数时,您必须将 soapVar 传递给关联数组。

例如,如果函数(称为 myFunc)期望一个名为 xmlInput 的参数,您应该执行以下操作
$soapvar = new SoapVar($query, XSD_STRING);
$result = $client->myFunc(array('xmlInput'=>$soapvar));

此 XML 片段将被包裹在 xmlImput 标签中。
-3
pmeth at softersoftware dot com
10 年前
注意,在尝试重载 __soapCall 函数时,您不应该使用类型提示,否则 STRICT_STANDARDS 会报错。
正确的签名如下

<?php
class MySoapClient extends \SoapClient
{
...
public function
__soapCall($function_name, $arguments, $options = null, $input_headers = null, &$output_headers = null)
{
...
}
....
}
?>
-5
niko dot hujanen at gmail dot com
13 年前
我正在将本地仓库系统与我们的系统的 SOAP 集成,并注意到一些事情。

1) 服务使用“点命名空间”(例如 Item.Price)我将它映射到 Item_Price,并使用神奇的 __get() 和 __set() 从变量名中删除“Item.”。效果很好。

示例
<Item.Price>1.52</Item.Price>

<?php
class Item_Price {
public
$price;
public function
__get($field) { $field = str_replace('Item.', '', $field); return $this->$field; }
public function
__set($field, $value) { $field = str_replace('Item.', '', $field); $this->$field = $value; }
?>

2) 我注意到使用属性和值对段进行类映射是可能的。类使用变量作为属性,$_ 是值。

示例
<?php
class Foo {
public
$item = 'Bar';
public
$_ = 'Value here';
}
?>
转换为 <Foo item="Bar">Value here</Foo>

使用类与 soapcalls 的示例
<?php
$client
= ExtendSoapClient::getInstance();
$foo = new Foo();
$response = $client->sendFoo($foo);
?>

我发布了这些,因为我认为文档缺乏且过时。希望可以减轻一些人的负担。
-4
lesley at casbah dot com dot au
15 年前
如果您使用证书调用 WebLogic 服务,并且在 SOAP 方法调用语句中遇到以下错误

[EJB:010160]安全违规:用户:'<anonymous>' 权限不足,无法访问 EJB:type=<ejb>,application=app2_0_52_3,module=appsejb.jar,ejb=AppMethod,method=AppMethod,methodInterface=Remote,signature={java.lang.String}。

请检查 SOAP 打开调用中的 WSDL - 它必须是 HTTPS。如果提供的是 HTTP WSDL,则需要花一些时间才能找出原因。

<?php
$client
= new SoapClient("https://example.com/AppWs/Service?WSDL",
array(
'trace'=>true,
'exceptions'=>true,
'local_cert' => "/var/www/html/app/newcert.pem",
'passphrase' => "thepassphrase"));
$response = $client->__soapCall("MyMethod", array("param1" => $value1));
?>

感谢作者关于通过取消注释 php.ini 中的 extension=php_opensll.dll 和 extension=php_curl.dll 来启用 OpenSSL 和 cURL 的评论。也感谢 Olaf 的说明:“将私钥文件和证书文件的内容追加到单个文件中”。我在 DOS 提示符下使用 copy 命令执行了此操作

copy mycert.pem+mykey.pem newcert.pem
To Top