PHP SOAP Server response in XML format - php

I'm creating SOAP web service using PHP.
Here is my code..
SoapServer.php
class server{
public function RegisterComplaint($strInputXml){
$str = "<RESULT><complaintNo>09865678</complaintNo></RESULT>";
$arr['RegisterComplaintResult'] = trim($str);
return $arr;
}
}
$custom_wsdl = 'custom.wsdl';
$server = new SoapServer($custom_wsdl);
$server->setClass('server');
$server->handle();
When I call RegisterComplaint using Wizdler(chrome extension) I get below result:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.Insurer.com/webservices/">
<SOAP-ENV:Body>
<ns1:RegisterComplaintResponse>
<ns1:RegisterComplaintResult><RESULT><complaintNo>09865678</complaintNo></RESULT></ns1:RegisterComplaintResult>
</ns1:RegisterComplaintResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Here I want result in below format(special characters to HTML entities):
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.Insurer.com/webservices/">
<SOAP-ENV:Body>
<ns1:RegisterComplaintResponse>
<ns1:RegisterComplaintResult><RESULT><complaintNo>09865678</complaintNo></RESULT></ns1:RegisterComplaintResult>
</ns1:RegisterComplaintResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Does any one know what I have to change for required output?
I tried html_entity_decode() & htmlspecialchars() on $str variable, but it's not working.

The solution as response. (Already quoted out in the comments)
The SoapServer class awaits an object as return value. This object will be automatically encoded by the server using the definitions from the used wsdl file. If a string is returned, its entities will always be encoded.
class Server
{
public function registerComplaint()
{
$registerComplaintResponse = new stdClass();
$registerComplaintResult = new stdClass();
$result = new \stdClass();
$result->complaintNo = '09865678';
$registerComplaintResult->RESULT = $result;
$registerComplaintResponse->RegisterComplaintResult = $registerComplaintResult;
return $registerComplaintResponse;
}
}
All definitions of return types (complex types) are defined in the wsdl file.

Related

Why not working canonicalizer XML in nojejs xml-c14n

I have problem in XML, when I need that a element child contain Parent's attributes in Nodejs. Now only atribute show in child canonicalized is xmlns, but, for instance, there are attributes xmlns:ns1, xmlns:types, the attributes don't in child element after function canonicalize.
I cant't to search function/lib in Node, make this for me.
In PHP I can do this, with function C14N.
In PHP i have :
Input :
$xml = '<?xml version="1.0" encoding="UTF-8"?>
<ns1:SendTest xmlns:ns1="http://localhost:8080/TestA/a" xmlns:type="http://localhost:8080/TestA/b" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://localhost:8080/TestA/a http://localhost:8080/TestA/xsd/SendTest.xsd">
<HEAD>
<TESTA>6291</TESTA>
</HEAD>
<TESTB Id="testeb:1ABCDZ">
<TESTC>
<TESTD>e4c47c91392cd57088c28468be0ef2349782f2df</TESTD>
</TESTC>
</TESTB>
</ns1:SendTest>';
PHP Code:
$d = new DOMDocument('1.0');
$d->loadXML($xml);
$data = $d->getElementsByTagName('TESTB')->item(0)->C14N(FALSE,FALSE,NULL,NULL);
echo ($data);
die();
OUTPUT :
<?xml version="1.0" encoding="UTF-8"?>
<TESTB xmlns:ns1="http://localhost:8080/TestA/a" xmlns:type="http://localhost:8080/TestA/b" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Id="testeb:1ABCDZ">
<TESTC>
<TESTD>e4c47c91392cd57088c28468be0ef2349782f2df</TESTD>
</TESTC>
</TESTB>
not Working in NodeJs (xml-c14n) :
const c14n = require("xml-c14n")();
const DOMParser = require("xmldom").DOMParser;
async function test (){
let xml = xml(input PHP);
let doc = (new DOMParser()).parseFromString(xml);
let canonicalizer = c14n.createCanonicaliser("http://www.w3.org/2001/10/xml-exc-c14n#");
let lote = doc.getElementsByTagName('TESTB');
let canonicalized = await canonicalizer.canonicalise(lote[0]);
console.log(canonicalized.toString());
}
test();
output :
<TESTB Id="testeb:1ABCDZ">
<TESTC>
<TESTD>e4c47c91392cd57088c28468be0ef2349782f2df</TESTD>
</TESTC>
</TESTB>
The result I get is not correct,suggestion for my question?

Sending SOAP request using PHP with complex type

I have to create a soap request to match this input xml
<api:objects>
<api:nextObject xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "xsi:type="api:Lead">
<api:Custom>
<api:DisbursedAmount_L>25000</api:DisbursedAmount_L>
<api:HoldingNo_L>GL010022016</api:HoldingNo_L>
<api:TransactionalStatus_L>Success</api:TransactionalStatus_L>
<api:DateofPurchase_L>02/10/2016</api:DateofPurchase_L>
</api:Custom>
<api:LeadKey>2279</api:LeadKey>
<api:StatusCodeKey>100010</api:StatusCodeKey>
</api:nextObject>
</api:objects>
How do I create tags with api:tagname? Also how to add xmlns:xsi and xsi:type to the tags. Please help! I am very new to soap and I am stuck with this complex structure and always I get an error object reference not set to an object.
My Code:
class Custom{
public $DisbursedAmount_L;
public $HoldingNo_L;
public $TransactionalStatus_L;
public $DateofPurchase_L;
function __construct($DisbursedAmount_L,$HoldingNo_L,$TransactionalStatus_L,$DateofPurchase_L) {
$this->DisbursedAmount_L = $DisbursedAmount_L;
$this->HoldingNo_L = $HoldingNo_L;
$this->TransactionalStatus_L = $TransactionalStatus_L;
$this->DateofPurchase_L = $DateofPurchase_L;
}
}
class nextObject
{
public $LeadKey;
public $StatusCodeKey;
function __construct(Custom $custom,$leadkey,$statuskey) {
$this->Custom=$custom;
$this->LeadKey = $leadkey;
$this->StatusCodeKey = $statuskey;
}
}
$custom_obj=new Custom(1,2,3,4);
$custom_obj1=new Custom(5,6,7,8);
$sdetail=array();
array_push($sdetail, new nextObject($custom_obj,'sgd','sgfd'));
array_push($sdetail, new nextObject($custom_obj1,'abc','def'));
$data = $wsclient->Save(array("userContext" => $obj_usrdisp, "objects"=>$sdetail, "returnObjectOnSave" => false));
echo htmlentities($wsclient->__getLastRequest());
o/p of last reques
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.crmnext.com/api/"><SOAP-ENV:Body><ns1:Save><ns1:userContext><ns1:IsSuccess>true</ns1:IsSuccess><ns1:ExpiresOn>2016-06-29T12:31:20.435685+05:30</ns1:ExpiresOn><ns1:IsUTC>false</ns1:IsUTC></ns1:userContext><ns1:objects><ns1:CRMnextObject/><ns1:CRMnextObject/></ns1:objects><ns1:returnObjectOnSave>false</ns1:returnObjectOnSave></ns1:Save></SOAP-ENV:Body></SOAP-ENV:Envelope>

SoapClient replaces tags (inside some tag) to htmlentities

The issue: I am sending SOAP request to 3rd-party server and inside one of my tags I have all "<" ">" replaced with html-entities < >
The info:
Lets say I have this class(simplified and pseudocoded for clarity):
Class SoapSender {
private $session = 'sessionHash';
private $soap = new SoapClient(/* settings */);
public function create() {
try {
$agr = [];
$agr['SessionToken'] = $this->session;
$agr['Document'] = $this->prepareDocument();
$request = $this->soap->createRequest($agr); // 3rd-party call
} catch (SoapFault $f) {
/* handle exception */
}
}
The documentation of 3rd-party says that <Document> should be XML string so I do this:
private function prepareDocument() {
$arr = [
/* Create all sub-tags of <Document> */
];
$xml = Formatter::make($arr, 'array')->toXml();
// this operations required for valid structure
$xml = str_replace(array('<?xml version="1.0" encoding="utf-8"?>','<xml>','</xml>'), '', $xml);
$xml = str_replace(['Subject_1', 'Subject_2'], 'Subject', $xml);
$xml = str_replace(['Period_1', 'Period_2'], 'Period', $xml);
$xml = preg_replace('/Store_[1-9]/', 'Store', $xml);
return $xml;
Formatter is SoapBox/laravel-formatter.
Soo, when I send request and recieve response with errors I dump $this->soap->__getLastRequest() and see that:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://someurl.com">
<SOAP-ENV:Body><ns1:CreateRequest>
<Document>
<General><Product>Bread</Product><DateBeg>2016-04-04T00:00:00</DateBeg><DateEnd>2017-04-03</DateEnd><.....
...........blah-blah....
</Document>
<SessionToken>SessionTokeon</SessionToken></ns1:CreateRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>
What I have so far: debuging prepareDocument says that I have valid xml string in utf-8 with all tags intact, so encoding them to html-entities happens on SOAP request. Only the Document tag gets encoded, others are ok.
All my other services that use same aproach but different 3rd-party works perfect. Sending this request (but decoded back to normal) by SOAP UI works as it should. All happens inside laravel project.
The issue again: I am sending request to 3rd-party server and inside one of my tags I have all "<" ">" replaced with html-entities < >
The question: What should I do to fix this issue?

XML signature differences between HEREDOC and DOMDocument

An API client I have developed works with XML messages and the messages are signed according to the XML Signature Syntax and Processing specification. After a long struggle, I finally got the signatures working.
At this moment I am building the XML with HEREDOC (simply php strings) and with a cleanup, I'd like to create the XML with DOMDocument directly. However, this causes the message to be invalidated by the server.
This is the current setup (server accepts this message when signed):
$xml = <<<EOT
<?xml version="1.0" encoding="UTF-8"?>
<DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
<createDateTimestamp>$timestamp</createDateTimestamp>
<Merchant>
<merchantID>$merchantId</merchantID>
<subID>$subId</subID>
</Merchant>
</DirectoryReq>
EOT;
$document = new DOMDocument();
$document->loadXML($xml);
This is the OO approach (server rejects this message when signed):
$document = new DOMDocument('1.0', 'UTF-8');
$request = $document->createElement('DirectoryReq');
$xmlns = $document->createAttribute('xmlns');
$xmlns->value = 'http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1';
$version = $document->createAttribute('version');
$version->value = '3.3.1';
$request->appendChild($xmlns);
$request->appendChild($version);
$merchant = $document->createElement('Merchant');
$merchant->appendChild($document->createElement('merchantID', $merchantId));
$merchant->appendChild($document->createElement('subID', $subId));
$request->appendChild($document->createElement('createDateTimestamp', $timestamp));
$request->appendChild($merchant);
$document->appendChild($request);
What can cause the difference as such the XML signature is invalidated by the server? The code to sign the message is exactly the same. The server is simply reporting "Invalid electronic signature".
If required I can show more code.
EDIT, more output and comparison of XML generated
To give some more information, this is the output of the first (HEREDOC) xml, generated via $document->saveXml():
<?xml version="1.0" encoding="UTF-8"?>
<DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
<createDateTimestamp>2013-08-10T19:41:20.000Z</createDateTimestamp>
<Merchant>
<merchantID>0020XXXXXX</merchantID>
<subID>0</subID>
</Merchant>
</DirectoryReq>
This is the output ($document->saveXML()) for the second (direct DOMDocument generation) method:
<?xml version="1.0" encoding="UTF-8"?>
<DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
<createDateTimestamp>2013-08-10T19:41:20.000Z</createDateTimestamp>
<Merchant>
<merchantID>0020XXXXXX</merchantID>
<subID>0</subID>
</Merchant>
</DirectoryReq>
In php, var_dump() gives the exact same string length. If I compare both strings (=== obviously) they are the same. Comparing both objects, then they are not the same.
Signing example
Signing occurs with the library xmlseclibs with this code (NB. both types are signed the same way!):
public function sign(DOMDocument $document, $fingerprint, $keyfile, $passphrase = null)
{
$dsig = new XMLSecurityDSig();
$dsig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
$dsig->addReference($document, XMLSecurityDSig::SHA256,
array('http://www.w3.org/2000/09/xmldsig#enveloped-signature'),
array('force_uri' => true)
);
$key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'private'));
if ($passphrase !== null) {
$key->passphrase = $passphrase;
}
$key->loadKey($keyfile, true);
$dsig->sign($key);
$dsig->addKeyInfoAndName($fingerprint);
$dsig->appendSignature($document->documentElement);
}
If I dump the XML after it's signed, the <DigestValue> and <SignatureValue> values are different. So the server is correct the signature is invalid, but I cannot come up with a clue why method A works and B not.
You are overwriting $merchant when you create the Merchant element, so just rename the variable
$merchantElement = $document->createElement('Merchant');
I have now solved it by exporting and importing the XML again. It's quite ugly, but allows me to flexibly handle the DOMNodes.
protected function repairDOMDocument(DOMDocument $document)
{
$xml = $document->saveXML();
$document = new DOMDocument;
$document->loadXML($xml);
return $document;
}
If there is a suggestion how to stop doing this, I am pleased to hear so.

SimpleXMLElement() - XPath() & SOAP returning nothing

After hours of searching, finding similar threads and still not being able to get it to work I've resorted to posting my specific problem. I'm getting a SOAP encoded XML response from a server that i want to use SimpleXMLElement() on, but i'm having a real hard time establishing a base path to work from.
I've tried two different methods:
xpath():
public function XMLParseUserData($xml)
{
$ActivityData = new SimpleXMLElement($xml);
$ActivityData->registerXPathNamespace("ns", "http://webservices.website.net/");
$basePath = $ActivityData->xpath('//ns:GetUserActivityDataResult/ActArray');
foreach ($basePath->ACT as $userActivity)
{
$this->uGUID = $userActivity->UserGUID;
echo $this->uGUID."<br />";
}
}
children():
public function XMLParseUserData($xml)
{
$ActivityData = new SimpleXMLElement($xml);
$basePath = $ActivityData->children('soap',true)->Body->GetUserActivityDataResponse->GetUserActivityDataResult->ActArray->ACT;
foreach ($basePath as $userActivity)
{
$this->uGUID = $userActivity->UserGUID;
echo $this->uGUID."<br />";
}
}
The XML response:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetUserActivityDataResponse xmlns="http://webservices.website.net/">
<GetUserActivityDataResult>
<ResponseCode>SUCCESS</ResponseCode>
<FailedUserCount>0</FailedUserCount>
<ActCount>1</ActCount>
<ActArray>
<ACT>
<UserGUID>0dc299ba-XXXX-XXXX-XXXX-7ca097d51eb6</UserGUID>
<ActDataCount>15</ActDataCount>
<ActData>
<ACT_DATA>
<Start>2012-03-05T08:40:00</Start>
<End>2012-03-05T09:00:00</End>
<SourceCount>1</SourceCount>
<SourceData>
<ACT_SRC_DATA>
<Source>ACTIPED</Source>
<TypeCount>3</TypeCount>
<TypeData>
<ACT_TYPE_DATA>
<Type>WALK</Type>
<S>40</S>
<C>2</C>
<D>20</D>
<T>16</T>
</ACT_TYPE_DATA>
<ACT_TYPE_DATA>
<Type>RUN</Type>
<S>20</S>
<C>2</C>
<D>20</D>
<T>10</T>
</ACT_TYPE_DATA>
<ACT_TYPE_DATA>
<Type>OTHER</Type>
<S>0</S>
<C>0</C>
<D>0</D>
<T>28</T>
</ACT_TYPE_DATA>
</TypeData>
<MetricCount>0</MetricCount>
</ACT_SRC_DATA>
</SourceData>
</ACT_DATA>
</ActData>
</ACT>
</ActArray>
<AsOfServerTimeGMT>2012-03-06T16:41:41.513</AsOfServerTimeGMT>
</GetUserActivityDataResult>
</GetUserActivityDataResponse>
</soap:Body>
</soap:Envelope>
Neither method works and both leave me with the same error:
Warning: Invalid argument supplied for foreach() in /c08/domains/dev.mysite.com/html/class/XMLParse.class.php on line 29
It says:
<GetUserActivityDataResponse xmlns="http://webservices.website.net/">
and you are trying to do
$basePath = $soapEnvelope
->children('soap', true)
->Body
->GetUserActivityDataResponse
…
which means you try to get <soap:GetUserActivityDataResponse> which obviously doesnt exist. You have to do (demo)
$basePath = $soapEnvelope
->children('soap', true)
->Body
->children('http://webservices.website.net/')
->GetUserActivityDataResponse
->GetUserActivityDataResult
->ActArray
->ACT;
Actually, you could just do ->children() to jump back to the default namespace, but I find providing the namespace explicitly somewhat clearer. Your choice.
Your XPath fails because you didn't specify the namespace for ActArray. Also, when xpath() is successful, it returns an array of SimpleXmlElements. You tried array->ACT, which doesn't work because an array is not an object. The first ActArray is in $basePath[0]. So you have to adjust the code to
$basePath = $soapEnvelope->xpath('//ns:GetUserActivityDataResult/ns:ActArray');
foreach ($basePath[0]->ACT as $userActivity) {
…
To get the ACT elements directly, change the XPath to
//ns:GetUserActivityDataResult/ns:ActArray/ns:ACT

Categories