Generating XML SOAP message in PHP - php

I need help generating XML SOAP message in PHP. I'm using BeSimpleSoap extension. I've tried many different ways to generate SOAP message.
Message should look like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://apis-it.hr/umu/2015/types/kp" xmlns:gsb="http://apis-it.hr/umu/2013/types/gsb">
<soapenv:Header/>
<soapenv:Body>
<gsb:SendMessageRequest>
<gsb:GsbEnvelope>
<gsb:MessageHeader>
<gsb:SenderId>1</gsb:SenderId>
<gsb:ServiceId>126</gsb:ServiceId>
<gsb:MessageId>c4413331-1cff-11e2-f516-242d656ac4b3</gsb:MessageId>
<gsb:SenderTimeStamp>2015-05-31T12:00:00</gsb:SenderTimeStamp>
</gsb:MessageHeader>
<gsb:Content>
<gsb:MimeType>aaa</gsb:MimeType>
<gsb:Data encoding="EMBEDDED">
<tns:Upit>
<tns:IdPosiljatelja>1</tns:IdPosiljatelja>
<tns:TipPoruke>1</tns:TipPoruke>
<tns:IdUpita>732262f1-063f-11e2-892e-0812200c9f68</tns:IdUpita>
<tns:DatumVrijemeUpita>2015-03-26T15:33:29.371+01:00</tns:DatumVrijemeUpita>
</tns:Upit>
</gsb:Data>
</gsb:Content>
</gsb:GsbEnvelope>
</gsb:SendMessageRequest>
</soapenv:Body>
</soapenv:Envelope>
I have tried to generat XML with this:
$encodded = new SoapVar("<tns:Upit>
<tns:IdPosiljatelja>196</tns:IdPosiljatelja>
<tns:TipPoruke>$TipPoruke</tns:TipPoruke>
<tns:IdUpita>$UUID</tns:IdUpita>
<tns:DatumVrijemeUpita>$date_time</tns:DatumVrijemeUpita>
</tns:Upit>", XSD_ANYXML);
$par_envelope=array( "GsbEnvelope" =>
array( "MessageHeader" =>
array("SenderId" => "24",
"ServiceId" => "$ServiceId",
"MessageId" => $UUID,
"SenderTimeStamp" => $date_time),
"Content" => array("MimeType" =>"application/xml","Data" =>array("any"=>$encodded,"encoding"=>"EMBEDDED"))));
$par_WSDL=array("trace"=>TRUE,
"exceptions"=>TRUE,
'location'=>$SERVICE_TEST,
"local_cert" =>$SOAP_cert,
'uri'=>$NAMESPACE_URI,
"passphrase"=>$cert_password,
"connection_timeout" => 60);
$client = new BeSimple\SoapClientSoapClient("GSBService.wsdl",$par_WSDL);
$result=$client->sendMessage($par_envelope);
But that only generated part of a XML message that looks like:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://apis-it.hr/umu/2013/types/gsb">
<SOAP-ENV:Body>
<ns1:SendMessageRequest>
<ns1:GsbEnvelope>
<ns1:MessageHeader>
<ns1:SenderId>1</ns1:SenderId>
<ns1:ServiceId>1</ns1:ServiceId>
<ns1:MessageId>34578b73-988a-4727-bee4-a287218cc0a1</ns1:MessageId>
<ns1:SenderTimeStamp>2015-10-01T09:07:25+02:00</ns1:SenderTimeStamp>
</ns1:MessageHeader>
<ns1:Content>
<ns1:MimeType>application/xml</ns1:MimeType>
<ns1:Data/>
</ns1:Content>
</ns1:GsbEnvelope>
</ns1:SendMessageRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
EDIT Here you can see WSDL and XSD files.

It seems that the library adds its own namespace prefixes. This is not a problem. In XML the following 3 examples have the same meaning:
<ns1:SendMessageRequest xmlns:ns1="http://apis-it.hr/umu/2013/types/gsb">
<gsb:SendMessageRequest xmlns:gsb="http://apis-it.hr/umu/2013/types/gsb">
<SendMessageRequest xmlns="http://apis-it.hr/umu/2013/types/gsb">
The XML parser will resolve all 3 to an element node with the local name SendMessageRequest in the namespace http://apis-it.hr/umu/2013/types/gsb.
So the changed prefix is not a problem. The library can do this to generate/optimize the output.
But, your XML Soap variable is not valid XML. You did not add the namespace definition. If an element has a prefix you will need a namespace definition for this prefix on the element node or one of its ancestors.
$encoded = new SoapVar(
"<tns:Upit xmlns:tns='http://apis-it.hr/umu/2015/types/kp'>
<tns:IdPosiljatelja>196</tns:IdPosiljatelja>
<tns:TipPoruke>$TipPoruke</tns:TipPoruke>
<tns:IdUpita>$UUID</tns:IdUpita>
<tns:DatumVrijemeUpita>$date_time</tns:DatumVrijemeUpita>
</tns:Upit>",
XSD_ANYXML
);

I would advise you to only use array or objects to generate the request that is simply pass as parameter to your SoapClient. And not mix xml strings and Objects.
For example, if ou use a WSDL to php generator, you'll only deal with PHP objects and the XML request will be generated natively by the SoapClient in order to send it to the SOAP Server.
If I can advise you some good WSDL to php generator:
wsdltophp.com
PackageGenerator

Related

PHP - Generating correct SOAP data for Salesforce API

Can anyone help point me in the correct direction to generate valid XML to use with the Salesforce API. I normally avoid SOAP like the plague but have no choice here and it seems like I'm at the bottom of a vertical learning curve.
I've got a full WSDL which as far as I'm aware should cover every part of the request, including all the appropriate namespaces and complex types, but I just can't generate a request that matches their example.
A subset of the example request looks like the following -
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://soap.sforce.com/schemas/class/WebsiteAPI" xmlns:web1="http://soap.sforce.com/schemas/class/WebsiteAPIUtils">
<soapenv:Header>
<web:SessionHeader>
<web:sessionId> SESSION_ID_FROM_LOGIN_METHOD </web:sessionId>
</web:SessionHeader>
</soapenv:Header>
<soapenv:Body>
<web:upsertLeadOrUpdateAccount>
<web:leadFromWebsite>
<!--Optional:-->
<web1:salesforceId></web1:salesforceId>
...
By calling the relevant method I can get an <ns1:upsertLeadOrUpdateAccount> request (not the same namespace id but that doesn't really matter), with an empty <leadFromWebsite> element. However, I just can't work out how to get the <salesforceId> sub element in there with the correct namespace. Everything I do either seems to do nothing, or adds an "xsi-type" attribute to the parent rather than using the namespace prefix.
The closest I've got is the following, which adds the namespace to the root element, but seems to create a weird looking xsi-type="ns1:web1" attribute, rather than just prefixing all the elements with web1.
I do have complexTypes for everything in the WSDL so I'm sure there should be a way for the SOAP library to handle all the heavy lifting for me. I really don't want to resort to just generating the whole lot by hand.
$data = new stdclass();
$data->salesforceId = '123';
$args = [
'leadFromWebsite' => new SoapVar($data, SOAP_ENC_OBJECT, 'web1', 'http://soap.sforce.com/schemas/class/WebsiteAPIUtils')
];
Output -
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://soap.sforce.com/schemas/class/WebsiteAPIUtils"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns2="http://soap.sforce.com/schemas/class/WebsiteAPI">
<SOAP-ENV:Header>
<ns2:SessionHeader>
<ns2:sessionId>123</ns2:sessionId>
</ns2:SessionHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:upsertLeadOrUpdateAccount>
<ns2:leadFromWebsite xsi:type="ns1:web1">
<salesforceId>123</salesforceId>
</ns2:leadFromWebsite>
</ns2:upsertLeadOrUpdateAccount>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I haven't tested this fully with the salesforce API yet, but I think I've managed to generate XML that looks correct - at least a lot closer than I was.
Seems my main issue was confusing type and node namespaces. I wanted to specify the namespace for the node, not the data type.
The following code generates xml that is much closer to the example requests -
$data = [];
$data[] = new SoapVar('123', XSD_STRING, null, null, 'salesforceId', 'http://soap.sforce.com/schemas/class/WebsiteAPIUtils');
$args = [
'leadFromWebsite' => new SoapVar($data, SOAP_ENC_OBJECT, null, null, 'leadFromWebsite', 'http://soap.sforce.com/schemas/class/WebsiteAPI')
];
$s->upsertLeadOrUpdateAccount($args);
XML -
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://soap.sforce.com/schemas/class/WebsiteAPIUtils"
xmlns:ns2="http://soap.sforce.com/schemas/class/WebsiteAPI">
<SOAP-ENV:Header>
<ns2:SessionHeader>
<ns2:sessionId>123</ns2:sessionId>
</ns2:SessionHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:upsertLeadOrUpdateAccount>
<ns2:leadFromWebsite>
<ns1:salesforceId>123</ns1:salesforceId>
</ns2:leadFromWebsite>
</ns2:upsertLeadOrUpdateAccount>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Stuck at converting xml request to corresponding Savon request

I am trying to communicate with a 3-rd party API. I tried both savon and Soap UI but both failed, then I asked them and they gave me equivalent that works. Confused, I toggled the logger and got following XML request which is quite different from what Savon is sending.
Here is the PHP code (I guess that's not needed, but still)
$apiauth =array('UserName'=>'XXXXXX','Password'=>'XXXXXXX','ClientCode'=>'1234')
$wsdl = 'http://stagewebapi.mylescars.com/service.asmx?wsdl';
$soap = new SoapClient($wsdl);
$header = new SoapHeader('http://tempuri.org/', 'AuthHeader', $apiauth);
$soap->__setSoapHeaders($header);
$data = $soap->fetchCities($header);
print_r($data);
Here is the XML request that PHP is sending and which is working just fine
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/"><SOAP-ENV:Header><ns1:AuthHeader><ns1:UserName>XXXXXXX</ns1:UserName><ns1:Password>XXXXXXX</ns1:Password><ns1:ClientCode>1234</ns1:ClientCode></ns1:AuthHeader></SOAP-ENV:Header><SOAP-ENV:Body><ns1:fetchCities/></SOAP-ENV:Body></SOAP-ENV:Envelope>
Here is my ruby code so far
require 'savon'
auth_header = { 'UserName'=>'XXXXXXX','Password'=>'XXXXXXXX','ClientCode'=>'1234'}
client = Savon.client(:wsdl => "http://stagewebapi.mylescars.com/service.asmx?wsdl",
:namespace_identifier => :ns1 ,:namespace => "http://tempuri.org/" , :log => true, :soap_header => auth_header,
:pretty_print_xml => true, :env_namespace => 'SOAP-ENV')
puts client.call(:fetch_cities)
Here is what Savon is producing
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://tempuri.org/" xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header>
<UserName>XXXXXXXXXXXXXXX</UserName>
<Password>XXXXXXXXXXXXX</Password>
<ClientCode>2873</ClientCode>
</soap-env:Header>
<soap-env:Body>
<ns1:fetchCities/>
</soap-env:Body>
</soap-env:Envelope>
I don't know how to make it equivalent to the PHP one. Am I missing something obvious ?
I tried to look at Savon documentation to find if there are some methods to manipulate tags with fine grain control but couldn't find much about it.
Questions similar to this are either unanswered or are specific to the requests.
Some SO questions that helped a little
Convert this xml request to a proper savon request
Replicating xml requests with savon ruby
Little curious, how PHP gets it right most of the time. Is it the parser they use for wsdl?

php and soap headers token auth

I need this xml in out:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="https://server/">
<soapenv:Header>
<authenticate>111111111111</authenticate>
<SOAP-ENV:Header/>
My php code is:
$head = new stdClass();
$head->authenticate='1111111111111111';
$header = new SoapHeader('ns1','authenticate',$head,false);
$client = new SoapClient ('https://server',array("trace" => 1, "exceptions" => 1));
$client->__setSoapHeaders($header);
I get this XML:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="https://server"
xmlns:ns2="https://server">
<SOAP-ENV:Header>
<ns2:authenticate>
<authenticate><BOGUS>anc401t9n4dknmgp2bv629ori7</BOGUS>
</authenticate></ns2:authenticate>
</SOAP-ENV:Header
><SOAP-ENV:Body>...
How can I get needed xml? Can fully rewrite soapheader with php client SoapClient? Or how I can adding one string whithout creating new namespace?
If your Web Service is well documented within its WSDL, try using wsdltophp.com or PackageGenerator as it generates the methods to properly set the SoapHeader very easily and so for the whole request.

PHP Parse Soap Response Issue - SimpleXMLElement

I'm having problems using PHP SimpleXMLElement and simpleSMLToArray() function to parse a SOAP Response. I'm getting the SOAP response from my SOAP Server just fine. I'm writing both the SOAP Client and Server in this case. I'm using NuSoap for the server. To me, the soap response looks perfect, but the PHP5 Soap Client doesn't seem to parse it. So, as in the past, I'm using SimpleXMLElement and the function simpleXMLToArray() from PHP.NET (http://php.net/manual/en/book.simplexml.php), but can't seem to get an array.
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="urn:eCaseWSDL">
<SOAP-ENV:Body>
<ns1:registerDocumentByPatientResponse xmlns:ns1="urn:eCaseWSDL">
<returnArray xsi:type="tns:ReturnResult">
<id xsi:type="xsd:int">138</id>
<method xsi:type="xsd:string">registerDocumentByPatient</method>
<json xsi:type="xsd:string">0</json>
<message xsi:type="xsd:string">success</message>
<error xsi:type="xsd:string">0</error>
</returnArray>
</ns1:registerDocumentByPatientResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
PHP Code from my class ($this references apply to my library code).
// Define SimpleXMLElement
$xml_element = new SimpleXMLElement($response_string); // SOAP XML
$name_spaces = $xml_element->getNamespaces(true);
var_dump($name_spaces);
$soap = $xml_element->children('ns1');
var_dump($soap);
$soap_array = $this->simpleXMLToArray($soap);
var_dump($soap_array);
return $soap_array;
I can see the namespaces; ns1, etc. But, the array is not returned. SimpleXMLElement looks like it's returning an object, but empty.
<pre>array(3) {
["SOAP-ENV"]=>
string(41) "http://schemas.xmlsoap.org/soap/envelope/"
["ns1"]=>
string(13) "urn:eCaseWSDL"
["xsi"]=>
string(41) "http://www.w3.org/2001/XMLSchema-instance"
}
object(SimpleXMLElement)#23 (0) {
}
bool(false)
Anyone have any ideas as to what I'm doing wrong. I must not have drunk enough coffee this morning. I'm tempted to just parse it with regular expressions.
SimpleXML creates a tree object, so you have to follow that tree to get to the nodes you want.
Also, you have to use the actual namespace URI when accessing it, e.g.: urn:eCaseWSDL instead of ns1:
Try this:
$soap = $xml_element->children($name_spaces['SOAP-ENV'])
->Body
->children($name_spaces['ns1'])
->registerDocumentByPatientResponse
->children();
var_dump((string)$soap->returnArray->id); // 138

Adding optional parameters in SOAP call in PHP

The format of my SOAP call needs to be:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/">
<SOAP-ENV:Body>
<ns1:FunctionName>
<ns1:HostUserName>hostuser</ns1:HostUserName>
<ns1:HostPassword>pass</ns1:HostPassword>
<ns1:OwnerName>owner</ns1:OwnerName>
<ns1:UserName>user</ns1:UserName>
<ns1:OptionalParam>
<ns1:Parameters>
<ns1:Parameter Name="FirstName">First<ns1:/Parameter>
<ns1:Parameter Name="LastName">Last<ns1:/Parameter>
<ns1:/Parameters>
</ns1:OptionalParam>
<ns1:/FunctionName>
</SOAP-ENV:Body>
How do I represent this in a PHP request?
I build the request like this:
$client = new SoapClient($url,array('trace' => 1));
$data['HostUserName']=$this->hostname;
$data['HostPassword']=$this->hostpassword;
$data['OwnerName']=$this->ownername;
$data['UserName']=$username;
$data['FirstName']=$FirstName;
$data['LastName']=$LastName;
$result = $client->FunctionName($data);
I've tried a few combinations of building arrays, SOAPVAR and SOAPPARAM but nothing has worked.
The target SOAP server is .NET based.
Any suggestions?
Thanks

Categories