How to See the Actual Xml Generated by PHP Soap Client Class

How do I see the actual XML generated by PHP SOAP Client Class?

Use getLastRequest. It returns the XML sent in the last SOAP request.

echo "REQUEST:\n" . $SOAP->__getLastRequest() . "\n";

And remember, this method works only if the SoapClient object was created with the trace option set to TRUE. Therefore, when creating the object, use this code:

$SOAP = new SoapClient($WDSL, array('trace' => 1));

Inspect XML created by PHP SoapClient call before/without sending the request

Upfront remark: In order to use the __getLastRequest() method successfully, you have to set the 'trace' option to true on client construction:

$client = new SoapClient('wsdldoc.asmx?WSDL', array('trace' => TRUE));

This way, your request will still be sent (and therefore still fail), but you can inspect the sent xml afterwards by calling $client->__getLastRequest().


Main answer:

To get access to the generated XML before/without sending the request, you'd need to subclass the SoapClient in order to override the __doRequest() method:

class SoapClientDebug extends SoapClient
{
public function __doRequest($request, $location, $action, $version, $one_way = 0) {
// Add code to inspect/dissect/debug/adjust the XML given in $request here

// Uncomment the following line, if you actually want to do the request
// return parent::__doRequest($request, $location, $action, $version, $one_way);
}
}

You'd then use this extended class instead of the original SoapClient while debugging your problem.

Getting the correct data from an XML file for a SoapClient request

If you're using SoapClient, you don't need to also construct the whole XML yourself. Depending on the service, you either need to pass style individual variables, or the contents of the "body".

As you say, you can just run:

$webService = new SoapClient($url);
$result = $webService->NumberToWords(["ubiNum"=>500]);

Underneath, the SoapClient class is generating the rest of the XML for you and sending it as an HTTP request.

If you want to get the data to send out of an XML document, you need to extract just that part, rather than trying to send the whole SOAP envelope inside the parameter. In this example, you need to navigate to the "NumberToWords" element; see this reference question for tips on navigating the XML namespaces but in this example you'd use something like this:

$requestData = simplexml_load_file($file);
$soapBody = $requestData->children('http://schemas.xmlsoap.org/soap/envelope/')->Body;
$numberToWords = $soapBody->children('http://www.dataaccess.com/webservicesserver/')->NumberToWords;
// Or to get the 500 directly:
$ubiNum = (int)$numberToWords->ubiNum;

Alternatively, you can just ignore the SoapClient class, construct the XML yourself, and post it with an HTTP client like Guzzle. Often the only extra step you'll need is to set the correct "SOAPAction" HTTP header.

Can I preview the XML that PHP SOAP wants to send before sending it?

You could use a derived class and overwrite the __doRequest() method of the SoapClient class.

<?php
//$clientClass = 'SoapClient';
$clientClass = 'DebugSoapClient';
$client = new $clientClass('http://www.webservicex.com/CurrencyConvertor.asmx?wsdl');
$client->sendRequest = false;
$client->printRequest = true;
$client->formatXML = true;

$res = $client->ConversionRate( array('FromCurrency'=>'USD', 'ToCurrency'=>'EUR') );
var_dump($res);

class DebugSoapClient extends SoapClient {
public $sendRequest = true;
public $printRequest = false;
public $formatXML = false;

public function __doRequest($request, $location, $action, $version, $one_way=0) {
if ( $this->printRequest ) {
if ( !$this->formatXML ) {
$out = $request;
}
else {
$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->loadxml($request);
$doc->formatOutput = true;
$out = $doc->savexml();
}
echo $out;
}

if ( $this->sendRequest ) {
return parent::__doRequest($request, $location, $action, $version, $one_way);
}
else {
return '';
}
}
}

prints

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.webserviceX.NET/">
<SOAP-ENV:Body>
<ns1:ConversionRate>
<ns1:FromCurrency>USD</ns1:FromCurrency>
<ns1:ToCurrency>EUR</ns1:ToCurrency>
</ns1:ConversionRate>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
NULL

But you'd have to change the actual code a bit for this to work which I try to avoid when possible (i.e. let tools do the work).

Get SOAP XML before __soapCall?

It is very, very hard (nigh impossible) to do that. What is much easier is using the SoapClient class' built-in debugging functionality to output the request after it has been sent. You can do that like so:

First, when creating your SOAPClient, enable tracing, like so:

$client = new SoapClient($wsdl, array('trace' => true));

Then do whatever processing is necessary to get ready to make the SOAP call and make it. Once it has been made, the following will give you the request you have just sent:

echo("<pre>"); //to format it legibly on your screen
var_dump($client->__getLastRequestHeaders()); //the headers of your last request
var_dump($client->__getLastRequest()); //your last request

And, if you want to see the response as well, the following should work:

var_dump($client->__getLastResponseHeaders()); //response headers
var_dump($client->__getLastResponse()); //the response

Php Soap, Dump Result as XMl FILE?

If you want to get the XML returned by the web service, you should use SoapClient::__getLastResponse() (along with the trace option).

$client = new SoapClient(..., array('trace' => 1));

// Do your __soapCall here

$xml = $client->__getLastResponse();

// Work with the XML string here

PHP - SOAP Request | XML/WSDL

I have found the soltuion! :)

<?php
$xml_data = '
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<ThirdPartyTokenHeader xmlns="http://moviestarplanet.com/">
<ThirdPartyToken>8346D304-F85E-4dc1-98EB-033CBEE0217F</ThirdPartyToken>
</ThirdPartyTokenHeader>
</soap:Header>
<soap:Body>
<Login xmlns="http://moviestarplanet.com/">
<username>USER</username>
<password>PASS</password>
</Login>
</soap:Body>
</soap:Envelope>
';

$headers = array(
"POST /WebService/ThirdParty/ThirdPartyService.asmx HTTP/1.1",
"Referer: www.moviestarplanet.fr",
"User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9505 Build/KOT49H)",
"Content-Type: text/xml; charset=utf-8",
"Host: www.moviestarplanet.fr",
"Content-length: ".strlen($xml_data),
"Expect: 100-continue"
);

$url = 'http://www.moviestarplanet.fr/WebService/ThirdParty/ThirdPartyService.asmx?WSDL';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_data);

$reply = curl_exec($ch);

echo($reply);
?>

How to create soap request and send XML to the WSDL then receive back the response in XML?

Soap requests with PHP are actually quite simple if you follow the web standard. The PHP SoapClient class is designed for doing the whole xml stuff for you. If you only work with objects that are derived from the WSDL file, it becomes pretty easy.

You are on the right way. The first step should be that you determine the functions and data types from the WSDL file. You 'Ve done that already with the $client->__getFunctions() and $client->__getTypes() function.

ResponseMessage getReport(RequestMessage $getReportRequest)

This gives the following information. If you want to call the getReport function of the web service, you must use the RequestMessage object in the request and receive the ResponseMessage object as a response. So where do we get this methods? This informations are written in the linked XSD file or you can use the $client->__getTypes() function as well. This will give us the following informations.

struct ResponseMessage {
messageId responseId;
dateTime responseDateTime;
messageId requestId;
dateTime requestDateTime;
string user;
protocolEnum protocol;
string customProtocol;
string data;
}

RequestMessage {
messageId requestId;
dateTime requestDateTime;
protocolEnum protocol;
string customProtocol;
boolean testDataMarker;
string data;
}

We can translate a struct into a PHP class. This class is a so-called value object.

class RequestMessage
{
protected $requestId;

protected $requestDateTime;

protected $protocol;

protected $customProtocol;

protected $testDataMarker;

protected $data;

public function getRequestId(): ?string
{
return $this->requestId;
}

public function setRequestId(?string $requestId): self
{
$this->requestId = $requestId;
return $this;
}

public function getRequestDateTime(): ?string
{
return $this->requestDateTime;
}

public function setRequestDateTime(?string $requestDateTime): self
{
$this->requestDateTime = $requestDateTime;
return $this;
}

public function getProtocol(): ?string
{
return $this->protocol;
}

public function setProtocol(?string $protocol): self
{
$this->protocol = $protocol;
return $this;
}

public function getCustomProtocol(): ?string
{
return $this->customProtocol;
}

public function setCustomProtocol(?string $customProtocol): self
{
$this->customProtocol = $customProtocol;
return $this;
}

public function getTestDataMarker(): ?bool
{
return $this->testDataMarker;
}

public function setTestDataMarker(?bool $testDataMarker): self
{
$this->testDataMarker = $testDataMarker;
return $this;
}

public function getData(): ?string
{
return $this->data;
}

public function setData(?string $data): self
{
$this->data = $data;
return $this;
}
}

class Credentials
{
protected $user;

protected $password;

public function getUser(): ?string
{
return $this->user;
}

public function setUser(?string $user): self
{
$this->user = $user;
return $this;
}

public function getPassword(): ?string
{
return $this->password;
}

public function setPassword(?string $password): self
{
$this->password = $password;
return $this;
}
}

This is a simple PHP value object with getters and setters for the class properties. The properties are exactly the same as stated out in the struct information. You can do this for all structs the $client->__getTypes() function returns. If all types are mapped to PHP value object classes the soap client will even parse a response in a mapped php class. If there 's no mapping to a response type, the soap client will parse the xml response into a stdClass PHP object.

For the getReport example we need the RequestMessage and the Credentials value object.

The next step is initializing the SoapClient class with the right option parameters. After initialization we need to set the credentials header and after that we set the data for our request.

try {
$client = new SoapClient(
'https://wasstt.infomonitor.pl/bigApi/v1/ReportOrderService/WEB-INF/wsdl/wsBigApi1v1.wsdl',
[
'trace' => true,
'exception' => true,
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
'soap_version' => SOAP_1_1,
'classmap' => [
'Credentials' => Credentials::class,
'RequestMessage' => RequestMessage::class,
],
]
);

// set the credentials header
$credentials = (new Credentials())
->setUser('user')
->setPassword('password');

$header = new SoapHeader('http://api.big.pl/bigApi/v1/types', 'credentials', $credentials, false);
$client->__setSoapHeaders([ $header ]);

// set the request data
$requestMessage = (new RequestMessage())
->setRequestId(1)
->setRequestDateTime('2020-01-15T15:00:00')
->setProtocol('bimo v1')
->setTestDataMarker(true)
->setData('test');

$result = $client->getReport($requestMessage);
var_dump($result);
} catch (SoapFault $fault) {
var_dump($fault, $client->__getLastRequest(), __LINE__);
}

As you can see, the initialization of the SoapClient class is contained in a try / catch block. This enables you to catch errors. The trace option enables you to get the sent request and the received response xml. The classmap option contains the xsd types that we 've got from the $client->__getTypes() function. The classmap enables the soap client to map the types to your value object classes.

As we know, that the getReport webservice function needs a RequestMessage object as parameter, we use our RequestObject value object class with all the values we want to submit in the request.

The sent xml request looks as follows:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://api.big.pl/bigApi/v1/types">
<SOAP-ENV:Header>
<ns1:credentials>
<user>user</user>
<password>password</password>
</ns1:credentials>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:getReportRequest>
<requestId>1</requestId>
<requestDateTime>2020-01-15T15:00:00</requestDateTime>
<protocol>bimo v1</protocol>
<testDataMarker>true</testDataMarker>
<data>test</data>
</ns1:getReportRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This xml was automatically generated by the SoapClient class. Ultimately, this request results in an authentication error because I do not know your credentials. But now you know, how to send and receive data with php native PHP classes without writing any xml.

Hope this helped you a bit.



Related Topics



Leave a reply



Submit