PHP Conference Japan 2024

SoapClient::__soapCall

(PHP 5, PHP 7, PHP 8)

SoapClient::__soapCall呼叫 SOAP 函式

描述

public SoapClient::__soapCall(
    字串 $name,
    陣列 $args,
    ?陣列 $options = null,
    SoapHeader|陣列|null $inputHeaders = null,
    陣列 &$outputHeaders = null
): 混合

這是一個低階 API 函式,用於發出 SOAP 呼叫。通常,在 WSDL 模式下,SOAP 函式可以作為 SoapClient 物件的方法呼叫。當 soapaction 未知、uri 與預設值不同或在傳送和/或接收 SOAP 標頭時,此方法在非 WSDL 模式下很有用。

在發生錯誤時,呼叫 SOAP 函式可能會導致 PHP 拋出例外或在禁用例外的情況下傳回 SoapFault 物件。若要檢查函式呼叫是否失敗而未捕捉到 SoapFault 例外,請使用 is_soap_fault() 檢查結果。

參數

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' => "https://#/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 筆記

vb
12 年前
請注意,呼叫 __soapCall 和從 WSDL 呼叫產生方法需要以兩種不同的方式指定參數。

舉例來說,如果你的網路服務有一個名為 login 的方法,它需要使用者名稱和密碼,你可以用以下方式呼叫它
<?php
$params
= array('username'=>'name', 'password'=>'secret');
$client->login($params);
?>

如果你想呼叫 __soapCall,你必須將參數包裝在另一個陣列中,如下所示
<?php
$client
->__soapCall('login', array($params));
?>
arturklesun at gmail dot com
8 年前
分享我的經驗,因為我相信如果你決定使用這個 Soap Client 實作,這對你來說是最重要的。

在 php 7.0.8 版本中,SoapClient 從回應產生的 stdClass 並不使用 "minOccurs" 和 "maxOccurs" WSDL 修飾符來區分 stdClass 中的屬性(又稱「關聯陣列」中的鍵)和陣列中的元素(又稱「索引陣列」)。

相反地,這個實作僅僅根據序列中是否只有一個具有該標籤的元素或有多個,來決定標籤是_關聯陣列中的鍵_還是_索引陣列中具有相同標籤的元素之一_。

考慮一下我案例中的實際例子
<?php
response xml
:
...
<
ValidatingCarrier>
<
Alternate>AA</Alternate>
</
ValidatingCarrier>
...
<
ValidatingCarrier>
<
Alternate>AA</Alternate>
<
Alternate>AY</Alternate>
</
ValidatingCarrier>
...

response structure generated by SoapClient:
...
[
ValidatingCarrier] => stdClass Object(
[
Alternate] => AA // 這裡是一個字串
)
...
[
ValidatingCarrier] => stdClass Object (
[
Alternate] => Array ( // 而這裡是一個陣列
[0] => AA
[1] => AY
)
)
...

field XSD definition:
<
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 時,這個資訊會對你有幫助。
Shto
15 年前
有一件事需要注意。

這發生在我身上,而且我花了一段時間才發現問題所在。

我試圖從提供的網路服務取得 .NET 物件,然而它似乎總是傳回空的物件。它確實傳回了主幹,但沒有傳回構成結構的物件內的任何內容。

總之,在呼叫這些函數時,你似乎必須對陣列非常精確。例如,執行此操作

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

這表示你必須將一個陣列作為第二個參數,並將 'parameters' 作為鍵,而 soap 呼叫參數作為值。

此外,請確保參數變數(在我的情況下是 $SoapCallParameters)的形式符合網路服務的要求。

所以,不要只建立一個如下形式的陣列
<?php

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

?>

但如果網路服務要求一個 'muid' 變數為 'Mary',一個 'birthyear' 為 1983,那麼請像這樣建立你的陣列

<?php

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

?>

以上陣列指的是 $SoapCallParameters 變數。

希望這能幫助到某些人,而不必花費太多時間來找出問題。
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;"

是彼此相關的。如果你的網路服務函數名為 "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"
?>
ub at sturmundbraem dot ch
8 年前
為了避免 SOAP 客戶端有時傳回物件,有時傳回物件陣列,有一個設定

$this->soapClient = new \SoapClient($wsdlUrl, array(
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
'trace' => DEBUG_SOAP
));
paulsjv at gmail dot com
18 年前
我第一次使用 SOAP,我必須建立一個客戶端,將日期範圍傳送到 WSDL(Web Services Description Language),以傳回我需要的某些資訊。我不知道如何傳遞參數,而且真的沒有任何關於它的文件。你必須確保的主要事情是,當你將參數傳遞到 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;
}
?>
OrionI
19 年前
當透過 SOAP 呼叫 .NET 應用程式時,即使你只是取得簡單的類型(例如布林值結果),你最終可能會得到一個物件作為結果,而不是簡單的類型。使用屬性存取器來取得實際結果,如下所示
<?php
$client
= new SoapClient("http://server/myservice.asmx?WSDL");
$objectresult = $client->MyMethod($param1, $param2);
$simpleresult = $objectresult->MyMethodResult;
?>
請注意,.NET 似乎將方法 MethodName 的結果命名為 MethodNameResult。
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);
}

?>
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"
?>
Tim Williams
15 年前
當您需要屬性和 simpletype 值時,傳遞參數時有一個小陷阱

要取得 XML

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

您將傳入

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

看到那個 "_" 位元了嗎?從文件中真的看不出來。
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);
?>
stefan at datax dot biz
17 年前
呼叫 `__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);

希望對您有所幫助。
deWarlock
15 年前
最惱人的是,如果您嘗試傳遞與 wsdl 不符的物件,您將不會收到任何警告,例如,如果伺服器預期類似 `$Object->expName->...` 的內容,而您傳遞 `$Object->otherName`,則用戶端將會傳送空的請求,而不會通知您。
另請注意,名稱是區分大小寫的。

在我的情況下,我花了數小時嘗試傳遞 `$Post->Lead->...` 物件,而不是 `$Post->lead->...`。
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));

?>
ryan at grunt dot tv
19 年前
如果您想將 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` 參數節點沒有包裝在 SOAP 請求中關聯的 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>
Alejandro Cavallo
16 年前
我遇到了與 ryan 上面回報的相同問題
"ryan at grunt dot tv
2005 年 9 月 22 日 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` 標籤周圍。
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)
{
...
}
....
}
?>
niko dot hujanen at gmail dot com
14 年前
我正在將本機倉庫系統與我們系統的 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>

使用類別進行 SOAP 呼叫的範例
<?php
$client
= ExtendSoapClient::getInstance();
$foo = new Foo();
$response = $client->sendFoo($foo);
?>

發布這些是因為我認為文件不足且非常過時。希望能減輕某些人的負擔。
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));
?>

感謝作者們關於啟用 openssl 和 curl 的評論,方法是在 php.ini 中取消註解 extension=php_opensll.dll 和 extension=php_curl.dll。也感謝 Olaf 的指示「將私鑰檔案和憑證檔案的內容附加到單一檔案」。我使用 DOS 提示符中的 copy 命令執行此操作

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