I have to do requets to a SOAP API with PHP and I need the following SOAP-Header structure:
<soapenv:Header>
<ver:authentication>
<pw>xxx</pw>
<user>xxx</user>
</ver:authentication>
</soapenv:Header>
How can I build this header?
I tried
$auth = [
"ver:authentication" => [
"pw" => $this->pw,
"user" => $this->user
]
];
$options = [];
$options["trace"] = TRUE;
$options["cache_wsdl"] = WSDL_CACHE_NONE;
$options["compression"] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP;
$client = new SoapClient("www.my-url.com/wsdl", $options);
$header = new SoapHeader("www.my-url.com", "authentication", $auth, false);
$client->__setSoapHeaders($header);
but it does not work. The respons is "failure" which I get, when the header structure is incorrect...
please help
the solution could be object driven. In the following code an example is given. Please keep in mind, that the following code is not testet.
class Authentication
{
protected $user;
protected $pw;
public function getUser() : ?string
{
return $this->user;
}
public function setUser(string $user) : Authentication
{
$this->user = $user;
return $this;
}
public function getPw() : string
{
return $this->pw;
}
public function setPw(string $pw) : Authentication
{
$this->pw = $pw;
return $this;
}
}
The above shown class is a simple entity, which contains two properties $user fpr the username and $pw for the password. Further it contains the getter and setter functions for retrieving or setting the values for the two properties.
For the next step just fill the class with data and store it in a SoapVar object.
$authentication = (new Authentication())
->setUser('Username')
->setPw('YourEncodedPassword');
$soapEncodedObject = new \SoapVar(
$authentication,
SOAP_ENC_OBJECT,
null,
null,
'authentication',
'http://www.example.com/namespace'
);
As you can see above, your authentication class will be stored as soap var object. It is encoded as soap object. The only thing you have to do is setting the namespace for this object. In your given example it is ver:. With this namespace prefix somewhere in your wsdl file a namespace is noted. You have to find out this namespace url and just replace the example url http://www.example.com/namespace with the right url noted in your wsdl.
The next step is setting this as soap header. That 's quite simple.
try {
$client = new SoapClient('http://www.example.com/?wsdl', [
'trace' => true,
'exception' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
]);
// set the soap header
$header = new SoapHeader('http://www.example.com/namespace', 'authentication', $authentication, false);
$client->setSoapHeaders($header);
// send the request
$result = $client->someWsdlFunction($params);
} catch (SoapFault $e) {
echo "<pre>";
var_dump($e);
echo "</pre>";
if ($client) {
echo "<pre>";
var_dump($client->__getLastRequest());
echo "</pre>";
echo "<pre>";
var_dump($client->__getLastResponse());
echo "</pre>";
}
}
As you can see it 's a bit different from your given example. Instead of an array it 's the soap encoded authentication object, that is given to the soap header class. For failure purposes there is a try/catch block around your soap client. In that case you can identify the error and if the client was initiated correctly, you can also see the last request and last response in xml.
I hope, that I helped you. ;)
I would strongly advise you 2 things:
Use a WSDL to PHP generator in order to properly construct your request. In addition, it will ease you the response handling. Everything is then using the OOP which is much better. Take a look to the PackageGenerator project.
Use the WsSecurity project in order to easily add your dedicated SoapHeader without wondering how to construct it neither.
Related
I have to implement a SOAP Web Service using PHP.
I did it by using the SoapServer class and all works fine.
I need to use a specific format for the request: they have to contain a "Header" tag with an "Authentication" tag in which there is a token that I have to use to authenticate the client that performed the request.
I used "file_get_contents('php //input')" to get the entire request that I received and then parsed it to retrieve the token that I needed.
This works fine if I try to simulate a SOAP request by using SoapUI. But, if I try to do the request by using PHP SoapClient and use the function SoapHeader to set the header, on the server side "file_get_contents('php //input')" returns only the fields of the entire request (contained in the XML tags of the XML request) merged together in a string, instead of returning the entire XML in a string format.
I cannot understand why.
The SoapServer class isn 't well documented in the PHP documentation. The SoapServer class does everything that you have in mind completely automatically. You have to use a decorator class. What a decorator is and what it does I 'll explain in the next lines. I 'm trying to give you a push in the right direction.
A while ago I had to implement the WSSE authentication standard. I 'll take some parts from the WSSE standard for this example.
The incoming request had a header that looked like this ...
<soapenv:Header>
<wsse:Security xmlns:wsc="http://schemas.xmlsoap.org/ws/2005/02/sc" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsc:SecurityContextToken>
<wsc:Identifier>identifier</wsc:Identifier>
</wsc:SecurityContextToken>
</wsse:Security>
</soapenv:Header>
The key (identifier) identifies an authorized user to perform a function of the web service. In this sense, we must check that the key is valid before executing any function. For this purpose we need a decorator class, that is executed before the actual function is executed.
class AuthDecorator
{
/**
* Name of the class, which contains the webservice methods
* #var string
*/
protected $class;
/**
* Flag, if the recieved identifier is valid
* #var boolean
*/
protected $isValid = false;
public function getClass() : string
{
return $this->class;
}
public function setClass($class) : AuthDecorator
{
$this->class = $class;
return $this;
}
public function getIsValid() : bool
{
return $this->isValid;
}
public function setIsValid(bool $isValid) : AuthDecorator
{
$this->isValid = $isValid;
return $this;
}
public function __call(string $method, array $arguments)
{
if (!method_exists($this->class, $method)) {
throw new \SoapFault(
'Server',
sprintf(
'The method %s does not exist.',
$method
)
);
}
if (!$this->getIsValid()) {
// return a status object here, wenn identifier is invalid
}
return call_user_func_array(
[ $this->class, $method ],
$arguments
);
}
/**
* Here 's the magic! Method is called automatically with every recieved request
*
* #param object $security Security node form xml request header
*/
public function Security($security) : void
{
// auth against session or database or whatever here
$identifier = $this->getIdentifierFromSomewhereFunc();
if ($security->SecurityContextToken->Identifier == $identfier) {
$this->setIsValid(true);
}
}
}
That 's the decorator class. Looks easy, hm? The decorator contains a class named like the first child of the xml header of the recieved request. This method will be executed automatically every time we recieve a request with the soap server. Beside that the decorator checks, if the called soap server function is available. If not a soap fault is thrown that the soap client on the consumer side recieves. If a method exists is quite easy, too. Every webservice method we put in a class.
class SimpleWebservice
{
public function doSomeCoolStuff($withCoolParams) : \SoapVar
{
// do some fancy stuff here and return a SoapVar object as response
}
}
For illustrative purposes, our web service just has this one function.
But how the hell we bring the decorator to work with the soap server?
Easy, mate. The SoapServer class has some pretty tricky functionality, that is not documented. The class has a method called setObject. This method will do the trick.
$server = new \SoapServer(
$path_to_wsdl_file,
[
'encoding' => 'UTF-8',
'send_errors' => true,
'soap_version' => SOAP_1_2,
]
);
$decorator = new AuthDecorator();
$decorator->setClass(SimpleWebservice::class);
$server->setObject($decorator);
$server->handle();
That 's awesome, right? Just initializing the SoapServer class, add the decorator with the setObject method and run it with the handle method. The soap server recieves all requests and before calling the webservice method the decorator will check, if the identifier is valid. Only if the identifier is valid, the called webservice method will be executed.
How 's the soap client request looking?
On the other side the soap client can look like this ...
$client = new SoapClient(
$path_to_wsdl_file,
[
'cache_wsdl' => WSDL_CACHE_NONE,
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
'exceptions' => true,
'trace' => true,
]
);
$securityContextToken = new \stdClass();
$securityContextToken->Identifier = 'identifier';
$securityContextToken = new \SoapVar(
$securityContextToken,
SOAP_ENC_OBJ,
null,
null,
'SecurityContextToken',
'http://schemas.xmlsoap.org/ws/2005/02/sc'
);
$security = new stdClass();
$security->SecurityContextToken = $securityContextToken;
$security = new \SoapVar(
$security,
SOAP_ENC_OBJ,
null,
null,
'Security',
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
);
$header = new \SoapHeader(
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd',
'Security',
$security
);
$client->__setSoapHeaders($header);
$result = $client->doSomeCoolStuff(new \SoapParam(...));
Conclusion
When working in an object orientated context the SoapServer and SoapClient classes are pretty cool. Because the documentation doesn 't really give much about both classes, you have to test and learn. You can easily create a SOAP webservice when you know how. Without writing any xml as a string.
Before you productively use the code examples seen here, please make sure that they are only examples and not intended for productive use. The shown examples should push you in the right direction. ;)
Questions?
I have a client call with a WSSE Security Header:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsse:UsernameToken wsu:Id="UsernameToken-7BCCD9337425FBA038149772606059420"><wsse:Username>USERNAME</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">PASSWORD</wsse:Password><wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">NONCE</wsse:Nonce><wsu:Created>2017-06-17T19:01:00.594Z</wsu:Created></wsse:UsernameToken></wsse:Security></soapenv:Header>
<soapenv:Body>
<ns:FUNCTION/>
</soapenv:Body>
</soapenv:Envelope>
As SoapServer I have a simple one:
// the SOAP Server options
$options=array(
'trace' => true
, 'cache_wsdl' => 0
, 'soap_version' => SOAP_1_1
, 'encoding' => 'UTF-8'
);
$wsdl = 'http://localhost/index.php?wsdl';
$server = new \SoapServer($wsdl, $options);
$server->setClass('ServerClass');
$server->handle();
$response = ob_get_clean();
echo $response;
As soon the property MustUnderstand = 1, then I become from the server the exception: Header not understood.
How to understand the header?
How to make the WSSE validation on the SoapServer side?
The solution is very tricky! I don't know why is this handled by this way from the SoapServer, but here is the solution:
class ServerClass {
public function Security($data) {
// ... do nothing
}
public function myFunction(){
// here the body function implementation
}
}
We need to define a function in our class, which is handling the soap request with the name of the header tag, which is holding the soap:mustUnderstand property. The function doesn't need to be implemented in some way.
That's all!
Mutatos' question / answer got me on the right track. I was working outside of a class structure so what worked for me was the following:
function Security($data)
{
$username = $data->UsernameToken->Username;
$password = $data->UsernameToken->Password;
//check security credentials here
}
$server = new SoapServer("schema/wsdls/FCI_BookingPullService.wsdl", array('soap_version' => SOAP_1_2));
$server->addFunction("Security");
$server->handle();
Essentially a function with the same name as the SOAP header "<wsse:Security>" (ignore the namespace) is being defined, then telling the server to use that to process the header with the 'addFunction' method.
Not ideal from a scope point of view, if that's an issue, try the class approach.
I connected to a SOAP server from a client and am trying to send form information back.
The connection works, but I have no idea how to send data back. I have received documentation ( -> http://s000.tinyupload.com/?file_id=89258359616332514672) and am stuck at the function AddApplication
This is the PHP code I've written so far. There is no form integration yet, only dummy data.
<?
$client = new SoapClient(
'https://wstest.hrweb.be/TvBastards/TvBastards/Job.svc?singleWsdl',
array(
'soap_version' => SOAP_1_1
)
);
$params = array(
'username' => 'XXX',
'password' => 'XXX',
'environmentKey' => 'XXX',
);
//Open session
try{
$token = $client->OpenSession($params);
}catch(SoapFault $ex){
echo "<pre>";
print_r($ex->detail->ExceptionDetail);
echo "</pre>";
}
//Add Application
try{
$resp = $client->AddApplication($params, ___THE_XML_SHOULD_BE_HERE___); // I have no idea how I can implement a XML file over here, and make this part work
}catch(SoapFault $ex){
echo "<pre>";
print_r($ex->detail->ExceptionDetail);
echo "</pre>";
}
//Close session
try{
$app = $client->CloseSession($token);
}catch(SoapFault $ex){
echo "<pre>";
print_r($ex);
echo "</pre>";
}`
The error I receive now is the following:
End element 'Body' from namespace 'http://schemas.xmlsoap.org/soap/envelope/' expected. Found element 'param1' from namespace ''. Line 2, position 156.
Which is understandable as I don't provide any XML.
I receive my token so the OpenSession works perfectly. As said, I'm completely stuck at the AddApplication function. This is my first encounter with a SOAP service, so every possible bit of explanation is highly appreciated.
Fixed it, and hopefully it can help out some others. I'll try and put it into steps.
define('SIM_LOGIN', 'LOGIN NAME HERE');
define('SIM_PASSWORD', 'LOGIN PASSWORD HERE');
define('ENV_KEY', 'ENVIRONMENT KEY HERE');
/*** login parameters ***/
$params = array(
'username' => SIM_LOGIN,
'password' => SIM_PASSWORD,
'environmentKey' => ENV_KEY,
);
/*** Set up client ***/
$client = new SoapClient(
__SOAP URL HERE__,
array(
'soap_version' => SOAP_1_1
)
);
After setting up the parameters and connecting to the client, we can start calling functions within the SOAP service. Every SOAP service will be different, so function names and parameters can be different. In below example I need to open a session to retrieve a token. This token is used in all the other functions, so this function is necessary. If something fails I call the "abort()" function.
try{
$token = $client->OpenSession($params);
}catch(SoapFault $ex){
abort();
}
If the token is received I call upon the function AddApplication. This expects the token parameter and an "object" (which is basically an STDClass).
I create an stdClass with all my data:
/*** Create stdClass with requested data ***/
$std = new stdClass();
$std->Firstname = $firstname;
$std->Lastname = $lastname;
$std->Birthdate = $birthdate;
$std->Phone = $phone;
$std->Email = $email;
Be sure to check camelcasing of names or capitals, as this makes all the difference.
Now we call upon the AddApplication function with parameters "token(string)" and "application(object)".
/*** AddApplication ***/
try{
$result = $client->AddApplication(array("token" => $token, "application" => $std));
}catch(SoapFault $ex){
abort();
}
If all goes well the data is stored on the external server and you receive a "success" message. It's possible that you receive a "fail" even without going into the SoapFault. Be sure to log both "$result" and "$ex", as a SOAP service can return a "Fail" but the try-catch sees this as a well formed result.
Last thing to do is close the session (and destroy the token)
/*** CloseSession ***/
try{
$app = $client->CloseSession($token);
}catch(SoapFault $ex){
abort();
}
If any questions, don't hesitate to ask them here, I'll be glad to help as I had such problems figuring this out.
What I want is get an object from an API with a HTTP (eg, jQuery's AJAX) request to an external api. How do I start? I did research on Mr Google but I can't find anything helping.
Im starting to wonder is this is even possible?
In this post Laravel 4 make post request from controller to external url with data it looks like it can be done. But there's no example nor any source where to find some documentation.
Please help me out?
Based upon an answer of a similar question here:
https://stackoverflow.com/a/22695523/1412268
Take a look at Guzzle
$client = new GuzzleHttp\Client();
$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]);
echo $res->getStatusCode(); // 200
echo $res->getBody(); // { "type": "User", ....
We can use package Guzzle in Laravel, it is a PHP HTTP client to send HTTP requests.
You can install Guzzle through composer
composer require guzzlehttp/guzzle:~6.0
Or you can specify Guzzle as a dependency in your project's existing composer.json
{
"require": {
"guzzlehttp/guzzle": "~6.0"
}
}
Example code in laravel 5 using Guzzle as shown below,
use GuzzleHttp\Client;
class yourController extends Controller {
public function saveApiData()
{
$client = new Client();
$res = $client->request('POST', 'https://url_to_the_api', [
'form_params' => [
'client_id' => 'test_id',
'secret' => 'test_secret',
]
]);
echo $res->getStatusCode();
// 200
echo $res->getHeader('content-type');
// 'application/json; charset=utf8'
echo $res->getBody();
// {"type":"User"...'
}
You just want to call an external URL and use the results? PHP does this out of the box, if we're talking about a simple GET request to something serving JSON:
$json = json_decode(file_get_contents('http://host.com/api/stuff/1'), true);
If you want to do a post request, it's a little harder but there's loads of examples how to do this with curl.
So I guess the question is; what exactly do you want?
As of Laravel v7.X, the framework now comes with a minimal API wrapped around the Guzzle HTTP client. It provides an easy way to make get, post, put, patch, and delete requests using the HTTP Client:
use Illuminate\Support\Facades\Http;
$response = Http::get('http://test.com');
$response = Http::post('http://test.com');
$response = Http::put('http://test.com');
$response = Http::patch('http://test.com');
$response = Http::delete('http://test.com');
You can manage responses using the set of methods provided by the Illuminate\Http\Client\Response instance returned.
$response->body() : string;
$response->json() : array;
$response->status() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->serverError() : bool;
$response->clientError() : bool;
$response->header($header) : string;
$response->headers() : array;
Please note that you will, of course, need to install Guzzle like so:
composer require guzzlehttp/guzzle
There are a lot more helpful features built-in and you can find out more about these set of the feature here: https://laravel.com/docs/7.x/http-client
This is definitely now the easiest way to make external API calls within Laravel.
Updated on March 21 2019
Add GuzzleHttp package using composer require guzzlehttp/guzzle:~6.3.3
Or you can specify Guzzle as a dependency in your project's composer.json
{
"require": {
"guzzlehttp/guzzle": "~6.3.3"
}
}
Include below line in the top of the class where you are calling the API
use GuzzleHttp\Client;
Add below code for making the request
$client = new Client();
$res = $client->request('POST', 'http://www.exmple.com/mydetails', [
'form_params' => [
'name' => 'george',
]
]);
if ($res->getStatusCode() == 200) { // 200 OK
$response_data = $res->getBody()->getContents();
}
Definitively, for any PHP project, you may want to use GuzzleHTTP for sending requests.
Guzzle has very nice documentation you can check here.
I just want to say that, you probably want to centralize the usage of the Client class of Guzzle in any component of your Laravel project (for example a trait) instead of being creating Client instances on several controllers and components of Laravel (as many articles and replies suggest).
I created a trait you can try to use, which allows you to send requests from any component of your Laravel project, just using it and calling to makeRequest.
namespace App\Traits;
use GuzzleHttp\Client;
trait ConsumesExternalServices
{
/**
* Send a request to any service
* #return string
*/
public function makeRequest($method, $requestUrl, $queryParams = [], $formParams = [], $headers = [], $hasFile = false)
{
$client = new Client([
'base_uri' => $this->baseUri,
]);
$bodyType = 'form_params';
if ($hasFile) {
$bodyType = 'multipart';
$multipart = [];
foreach ($formParams as $name => $contents) {
$multipart[] = [
'name' => $name,
'contents' => $contents
];
}
}
$response = $client->request($method, $requestUrl, [
'query' => $queryParams,
$bodyType => $hasFile ? $multipart : $formParams,
'headers' => $headers,
]);
$response = $response->getBody()->getContents();
return $response;
}
}
Notice this trait can even handle files sending.
If you want more details about this trait and some other stuff to integrate this trait to Laravel, check this article. Additionally, if interested in this topic or need major assistance, you can take my course which guides you in the whole process.
I hope it helps all of you.
Best wishes :)
Basic Solution for Laravel 8 is
use Illuminate\Support\Facades\Http;
$response = Http::get('http://example.com');
I had conflict between "GuzzleHTTP sending requests" and "Illuminate\Http\Request;" don't ask me why... [it's here to be searchable]
So looking for 1sec i found in Laravel 8 Doc...
**Guzzle is inside the Laravel 8 Http Request !**
https://laravel.com/docs/8.x/http-client#making-requests
as you can see
https://laravel.com/docs/8.x/http-client#introduction
Laravel provides an expressive, minimal API around the Guzzle HTTP
client, allowing you to quickly make outgoing HTTP requests to
communicate with other web applications. Laravel's wrapper around
Guzzle is focused on its most common use cases and a wonderful
developer experience.
It worked for me very well, have fun and if helpful point up!
I also created trait similar to #JuanDMeGonthat's that u can use anywhere in your project.Please check this out
trait ApiRequests
{
public function get($url, $data = null)
{
try {
$response = Http::get($this->base_url . $url, $data);
} catch (\Exception $e) {
info($e->getMessage());
abort(503);
}
if ( $response->status() == 401) {
throw new AuthenticationException();
} else if (! $response->successful()) {
abort(503);
}
return $response->json();
}
public function post($url, $data = [])
{
$token = session()->get('token');
try {
$response = Http::acceptJson()->withToken($token)->post($this->base_url . $url, $data);
} catch (\Exception $e) {
abort(503);
}
if ($response->status() == 401 && !request()->routeIs('login')) {
throw new AuthenticationException();
}
return $response;
}
}
class Controller extends BaseController
{
protected $base_url;
use AuthorizesRequests, DispatchesJobs, ValidatesRequests, ApiRequests;
public function __construct()
{
$this->base_url = env("BASE_URL","http://192.168.xxxxxxx");
View::share('base_url', $this->base_url);
}
}
You can use Httpful :
Website : http://phphttpclient.com/
Github : https://github.com/nategood/httpful
Here is the simple call for laravel 9.4
Route::get('/currency', function () {
$response = Http::withHeaders([
'x-api-key' => 'prtl6749387986743898559646983194',
])->get('https://partners.api.skyscanner.net/apiservices/v3/culture/currencies');
return response()->json(['status'=> true,'data'=> json_decode($response->body()), 'Message'=>"Currency retrieved successfully"], 200);
});
Don't forget to import
use Illuminate\Support\Facades\Http;
I have a WCF Service written in .Net 4.0 that accepts two parameters. One is a complex type consisting of User, MerchantName, and Password, the second variable is an int. The service returns a third complex type.
It's structure looks like the following:
//*C# Code *
public sub AccountData Log(Login LoginData, int AccountID)
{
//do stuff here
}
Using SoapClient and removing the int AccountID from the C# service, I can pass the complex data in and parse through the complex data output succesfully. Adding the AccountID parameter, breaks the soap call. I can't seem to compound the variables into one array in a fashion that WCF will accept.
The question is how to form the array to pass in the call?
I have tried the following:
//****Attempt one *******
$login = array('MerchantName' => 'merchantA',
'User' => 'userA',
'password' => 'passwordA');
$account = '68115'; //(also tried $account = 68115; and $account = (int)68115;)
$params = array('LoginData' => $login, 'AccountID' => $account);
$send = (object)$params; //Have tried sending as an object and not.
$client = new SoapClient($wsdl);
$client->soap_defencoding = 'UTF-8';
$result = $client->__soapCall('Log', array($send);
var_dump($send);
echo("<pre>");
var_dump($result);
The latest attempt was to class the variables but I got stuck when tring to form into the $client call.
class LogVar
{
public $MerchantName;
public $User;
public $Password;
}
class AccountID
{
public $AccountID;
}
$classLogin = new LogVar();
$classLogin->MerchantName = 'merchantA';
$classLogin->User = 'userA';
$classLogin->Password = 'passwordA';
$classAccount = new AccountID();
$classAccount->AccountID = '68115';
//How to get to $client->__soapCall('Log', ???????);
P.S. I'm a .Net coder, please be kind with the PHP explanations... Also NuSoap didn't seem much better, however it may have undiscovered ways of dealing with complex types.
This worked for me with standard SoapClient:
$client = new SoapClient($wsdl, array('trace' => true));
$data = $client->Log(array('AccountID' => 23, 'LoginData' => array('User' => '123', 'Password' => '123', 'MerchantName' => '123')));
// echo $client->__getLastRequest();
var_dump($data);
You can display the last request XML and compare it with what a WCF client is generating. This is how I figured it out: I generated a WCF client, inspected the XML message generated by it and compared to $client->__getLastRequest.
Note: You can call the method by its name on a SoapClient rather than use $client->__soapCall('operationName')