Issue with sending SOAP request, through soapclient, to service that expects CDATA - php

I am trying to make a SOAP request to a service that expects CDATA as part of the request. I managed to do successfull calls using Insomnia with the following, either of the two works:
<ExecuteRequest xmlns="<url>">
<TAG1>
<![CDATA[
<TAG2>
<TAG3>TEXT1</TAG3>
<TAG4>TEXT2</TAG3>
<TAG5>TEXT3</TAG5>
</TAG2>
]]>
</TAG1>
</ExecuteRequest>
OR
<ExecuteRequest xmlns="<url>">
<TAG1>
<TAG2>
<TAG3>TEXT1</TAG3>
<TAG4>TEXT2</TAG4>
<TAG5>TEXT3</TAG5>
</TAG2>
</TAG1>
</ExecuteRequest>
But I've been having trouble translating the above to work with the php soapclient. For example using the following:
$soapclient = new SoapClient('url?wsdl', array('trace' => 1));
$xmlWriter = new \XMLWriter();
$xmlWriter->openMemory();
$xmlWriter->startElement('TAG1');
$xmlWriter->startElement('TAG2');
$xmlWriter->writeElement('TAG3','TEXT1');
$xmlWriter->writeElement('TAG4','TEXT2');
$xmlWriter->writeElement('TAG5','TEXT3');
$xmlWriter->endElement();
$xmlWriter->endElement();
$myXml = $xmlWriter->outputMemory(true);
$params = array(
new \SoapParam(new \SoapVar($myXml, XSD_ANYXML), 'param')
);
$response = $soapclient->__soapCall('ExecuteRequest',$params);
$lastrequest = $soapclient->__getLastRequest();
$soapclient->__getLastRequest() gives me this output which obviously isn't what I want, no CDATA:
<TAG1>
<TAG2>
<TAG3>TEXT1</TAG3>
<TAG4>TEXT2</TAG4>
<TAG5>TEXT3</TAG5>
</TAG2>
</TAG1>
If instead in the xmlwriter I use this to write the CDATA manually:
$xmlWriter->writeCdata('<TAG2>
<TAG3>TEXT1</TAG3>
<TAG4>TEXT2</TAG4>
<TAG5>TEXT3</TAG5>
</TAG2>');
Then for some weird reason $soapclient->__getLastRequest() returns the CDATA section commented out and I can't figure out why that is happening:
<TAG1>
<!--[CDATA[<TAG2-->
<TAG3>TEXT1</TAG3>
<TAG4>TEXT2</TAG4>
<TAG5>TEXT3</TAG5>
""]]>"
</TAG1>
Notice how TAG2's closing tag is lost as well.
EDIT
The same issue with the CDATA being commented out happens if I don't use the xml writter.
$wholeTag = new \SoapVar("<TAG1><![CDATA[{$text}]]></TAG1>", XSD_ANYXML);
$params = [
'param' => $wholeTag,
];
Any ideas? It's very likely that I'm going about this in the wrong way, so I'm open to any suggestion.

After wasting hours on this I ended up discarding using the soapclient altogether and solved it with Guzzle.
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
$xml = new \SimpleXMLElement("<put the whole xml body here, using the Webservice's SOAP 1.1 sample as a reference>");
$xmlstring = $xml->asXML();
$client = new Client();
$url = "< the **full** webservice url (used the Webservice's SOAP 1.1 sample as reference)>";
$action = "action url (used the Webservice's SOAP 1.1 sample as reference)";
try {
$response = $client->post($url,
['headers' =>
['SOAPAction' => $action,
'Content-Type' => 'text/xml; charset=utf-8',
'Host' => "<host url (used the Webservice's SOAP 1.1 sample as reference)>"
],
'body' => $xmlstring
]);
}
catch (GuzzleException $e)
{
return [ 'status' => $e->getResponse()->getStatusCode(), 'message' => $e->getResponse()->getReasonPhrase()];
}
if ($response->getStatusCode() === 200) {
// Success!
return $response->getBody();
}

Related

guzzle xml request returns only plain text

im trying to make a xml request to a ws using guzzle,(and i try with curl to) in php but always the response its in plain text no in xml
$client = new \GuzzleHttp\Client(['verify' => false]);
$soapRequest = <<<XML
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:san="mywebsservice">
<soap:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Action>http://tempuri.org/mywebsservice</wsa:Action>
<To soap:mustUnderstand="1" xmlns="http://www.w3.org/2005/08/addressing">mywebsservice </To>
</soap:Header>
<soap:Body>
<tem:GetSecurityToken>
<tem:request>
<san:Connection>mywebsservice</san:Connection>
<san:Passwoord>mywebsservice</san:Passwoord>
<san:System>mywebsservice</san:System>
<san:UserName>mywebsservice</san:UserName>
</tem:request>
</tem:GetSecurityToken>
</soap:Body>
</soap:Envelope>
XML;
$request = $client->request('POST','mywebsservice', [
'headers' => [
'Content-Type' => 'application/soap+xml'
],
'body' => $soapRequest
]);
$response = $request->getBody()->getContents();
var_dump($response);
this is the response
this is the response
string(1870) "
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/webservices/GetSecurityTokenResponse</a:Action>
<ActivityId CorrelationId="cf1c12da-af1b-4013-ba89-25db2fa67dc1" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">00000000-0000-0000-0000-000000000000</ActivityId>
</s:Header>
<s:Body>
<GetSecurityTokenResponse xmlns="http://tempuri.org/">
<GetSecurityTokenResult xmlns:b="webservices" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<b:AccessToken>token access</b:AccessToken>
<b:IdToken>the token</b:IdToken>
<b:TokenType>Bearer</b:TokenType>
</GetSecurityTokenResult>
</GetSecurityTokenResponse>
</s:Body>
</s:Envelope>
"
The headers you are sending is what the receiving server uses to decide what content to serve. It will still be text content though, but only with a different Content-Type header.
guzzlehttp/guzzle 6.x
The $response->json() and $response->xml() helpers were removed in 6.x. The following lines can be used to replicate that behaviour:
// Get an associative array from a JSON response.
$data = json_decode($response->getBody(), true);
See https://www.php.net/manual/en/function.json-decode.php
// Get a `SimpleXMLElement` object from an XML response.
$xml = simplexml_load_string($response->getBody());
See https://www.php.net/manual/en/function.simplexml-load-string.php
guzzlehttp/guzzle 5.x
Guzzle 5.x has some shortcuts to help you out:
$client = new Client(['base_uri' => 'https://example.com']);
$response = $client->get('/');
// $response = Psr\Http\Message\ResponseInterface
$body = (string) $response->getBody();
// $body = raw request contents in string format.
// If you dont cast `(string)`, you'll get a Stream object which is another story.
Now whatever you do with $body is up to you. If it is a JSON response, you'd do:
$data = $response->json();
If it is XML, you can call:
$xml = $response->xml();
I never work with XML APIs so i can't give you any more examples on how to traverse the XML you will retrieve.

Soap requests with Curl PHP

How can I make Soap request using Curl php by using below soap format and url? I have tried avaliable solutions online and none of them worked out.
$soap_request = '<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:alisonwsdl" 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/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Header>
<credentials>
<alisonOrgId>9ReXlYlpOThe24AWisE</alisonOrgId>
<alisonOrgKey>C2owrOtRegikaroXaji</alisonOrgKey>
</credentials>
</soap:Header>
<SOAP-ENV:Body>
<q1:login xmlns:q1="urn:alisonwsdl">
<email xsi:type="xsd:string">email</email>
<firstname xsi:type="xsd:string">fname</firstname>
<lastname xsi:type="xsd:string">lname</lastname>
<city xsi:type="xsd:string">city</city>
<country xsi:type="xsd:string">country</country>
<external_id xsi:type="xsd:string"></external_id>
</q1:login>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
Url
https://alison.com/api/service.php?wsdl
Assuming you already have php_soap extension installed, you can access the SOAP API like this:
<?php
$client = new SoapClient('https://alison.com/api/service.php?wsdl', array(
'stream_context' => stream_context_create(array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
))
));
You might want to define the header for authentication as well
$auth = array(
'alisonOrgId' => '9ReXlYlpOThe24AWisE',
'alisonOrgKey' => 'C2owrOtRegikaroXaji'
);
$header = new SoapHeader('https://alison.com/api/service.php?wsdl', 'credentials', $auth, false);
$client->__setSoapHeaders($header);
Then you can get the list of functions available
// Get function list
$functions = $client->__getFunctions ();
echo "<pre>";
var_dump ($functions);
echo "</pre>";
die;
Or call a function right away, like this:
// Run the function
$obj = $client->__soapCall("emailExists", array(
"email" => "test#email.com"
));
echo "<pre>";
var_dump ($obj);
echo "</pre>";
die;
After struggling for a week I was able to find something in this tutorial here on youtube https://www.youtube.com/watch?v=6V_myufS89A and I was able to send requests to the API successifuly, first read my xml format above before continuing with my solution
$options = array('trace'=> true, "exception" => 0);
$client = new \SoapClient('your url to wsdl',$options);
//if you have Authorization parameters in your xml like mine above use SoapVar and SoapHeader as me below
$params = new \stdClass();
$params->alisonOrgId = 'value here';
$params->alisonOrgKey = 'value here';
$authentication_parameters = new \SoapVar($params,SOAP_ENC_OBJECT);
$header = new \SoapHeader('your url to wsdl','credentials',$authentication_parameters);
$client->__setSoapHeaders(array($header));
print_r($client->__soapCall("Function here",'Function parameter here or left it as null if has no parameter'));
//or call your function by
print_r($client->yourFunction($function_parameters));
}
Hope this will help someone out there struggling with soap requests that contains authentication informations

Incorrect SOAP request format for the message body using __soapcall() PHP

I have used __soapcall() to send a soap requsest.
$parameters = array(
'ID' => '0001',
'TimeStamp' => '2016-06-20T17:03:27Z',
'SenderID' => 'B2eww8',
);
$URL = 'https://example.com';
$client = new \SoapClient(null, array(
'location' => $URL,
'uri' => "http://example.com/SOAP/WSDL/abc.xsd",
'trace' => 1
));
$return = $client->__soapCall("TestSoapRequest", $parameters);
var_dump($return);
Then request looks like.
<SOAP-ENV:Body>
<ns1:TestSoapRequest>
<param0 xsi:type="xsd:string">0001</param2>
<param1 xsi:type="xsd:string">2016-06-20T17:03:27Z</param3>
<param2 xsi:type="xsd:string">B2eww8</param4>
</ns1:TestSoapRequest>
</SOAP-ENV:Body>
But desired format is,
<soapenv:Body>
<ns1:TestSoapRequest xmlns:ns1="http://www.caqh.org/SOAP/WSDL/CORERule2.2.0.xsd">
<ID>0001</ID>
<TimeStamp>2014-05-22T17:03:27Z</TimeStamp>
<SenderID>B2eww8</SenderID>
</ns1:TestSoapRequest>
</soapenv:Body>
How can i achive this.
There is no definition for TestSoapRequest in http://www.caqh.org/SOAP/WSDL/CORERule2.2.0.xsd so if you want to prepare request without it then you should use SoapParam class described here http://php.net/manual/en/soapparam.soapparam.php
Similar problem has been described here PHP Soap non-WSDL call: how do you pass parameters?
A few months ago I had the same problem and was using an XML document. I created a generator file which creates an XML document with structure corrected.
$XMLDocument = new DOMDocument('1.0', 'UTF-8');
$Body = $XMLDocument->createElement('soapenv:Body', null);
$Body->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns1', "http://www.caqh.org/SOAP/WSDL/CORERule2.2.0.xsd");
$TestSoapRequest = $XMLDocument->createElement('TestSoapRequest', null);
$element = $XMLDocument->createElement('ID', '0001');
$TestSoapRequest->appendChild($grossWeightElement);
$element = $XMLDocument->createElement('TimeStamp', '2014-05-22T17:03:27Z');
$TestSoapRequest->appendChild($grossWeightElement);
$element = $XMLDocument->createElement('SenderID', 'B2eww8');
$TestSoapRequest->appendChild($grossWeightElement);

Change format of the output XML , generated from PHP SoapClient?

I am trying to create this :
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<AccessKey xmlns="http://eatright/membership" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Value>67a4ef47-9ddf-471f-b6d0-0000c28d57d1</Value>
</AccessKey>
</s:Header>
<s:Body>
<WebUserLogin xmlns="http://eatright/membership">
<loginOrEmail>1083790</loginOrEmail>
<password>thomas</password>
</WebUserLogin>
</s:Body>
</s:Envelope>
I created this PHP code
class ChannelAdvisorAuth
{
public $AccessKey ;
public function __construct($key)
{
$this->AccessKey = $key;
}
}
$AccessKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$url = "http://ws.eatright.org/service/service.svc?wsdl";
$client = new SoapClient($url, array("trace" => 1, "exception" => 0));
$auth = new ChannelAdvisorAuth($AccessKey);
$header = new SoapHeader("AccessKey", "Value", $AccessKey, false);
$client->__setSoapHeaders($header);
$result = $client->ValidateAccessKey();
echo "REQUEST:\n" . $client->__getLastRequest() . "\n";
The output of the above PHP code is :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://eatright/membership" xmlns:ns2="AccessKey">
<SOAP-ENV:Header>
<ns2:Value>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</ns2:Value>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:ValidateAccessKey/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
How to change the PHP code to output the XML as requested by web service provider?
How to replace the "SOAP-ENV" to "S"?
Is there is a way to remove the NS1 and NS2? and also adjust the whole format of the XML to meet the requirements? thanks
You don't need to worry about SOAP-ENV, ns1 or ns2 - they are just prefixes referring to the namespaces. As long as the full namespaces are correct, it's going to be alright.
I think the SOAP header should be made like this:
$access_key = new stdClass();
$access_key->Value = 'XXX';
$hdr = new SoapHeader('http://eatright/membership', 'AccessKey', $access_key);
$client->__setSoapHeaders($hdr);
I don't see a purpose of xmlns:i in the first example - there are no elements having XSI attributes.
I'm not sure what to do with the body. In your first example, there is a call to the WebUserLogin operation, while in your PHP code you are trying to call ValidateAccessKey.
Have you tried reading the WSDL file which is pointed by $url
Ok I found the problem and I will add it here in case someone looking for same issue.
$access_key = new stdClass();
$access_key->Value = 'xxxxxxxxxxxxxxxxxxx';
// Create the SoapClient instance
$url = "http://ws.eatright.org/service/service.svc?wsdl";
$client = new SoapClient($url, array("trace" => 1, "exception" => 0));
$hdr = new SoapHeader('http://eatright/membership', 'AccessKey', $access_key);
$client->__setSoapHeaders($hdr);
$soapParameters = array('loginOrEmail ' => $username, 'password' => $password);
$login = new stdClass();
$login->loginOrEmail='LoginID';
$login->password='Password';
$result = $client->WebUserLogin($login);

PHP sending XML with soap returns unknown rerror

I'm trying to send user registrations with soap to another server. I'm creating an xml with DOMdocument and than after saveXML I'm running the soap which should return an xml with registration id plus all the data I sent in my xml.
But the Soap returns unknown error. exactly this: stdClass Object ( [RegisztracioInsResult] => stdClass Object ( [any] => 5Unknown error ) )
and this is how I send my xml.
/*xml creation with DOMdocument*/
$xml = saveXML();
$url = 'http://mx.biopont.com/services/Vision.asmx?wsdl';
$trace = '1';
$client = new SoapClient($url, array('trace' => $trace, "exceptions" => 0, 'features' => SOAP_SINGLE_ELEMENT_ARRAYS));
$params = $client->RegisztracioIns(array('xml' => $xml));
$print_r($params);
If I click on the description of the RegisztracioIns service at this URL http://mx.biopont.com/services/Vision.asmx it shows me this:
POST /services/Vision.asmx HTTP/1.1
Host: mx.biopont.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://mx.biopont.com/services/RegisztracioIns"
<?xml version="1.0" encoding="utf-8"?>
<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:Body>
<RegisztracioIns xmlns="http://mx.biopont.com/services/">
<xml>string</xml>
</RegisztracioIns>
</soap:Body>
</soap:Envelope>
According to this I think I'm doing the upload correctly but maybe not I don't have much experience with soap.
Is there anything I'm missing? I also tried to save the xml to my server an than get the contents with file_get_contents(). but the result was the same.
You should be able to do something like this:
$res = $client->__soapCall( 'RegisztracioIns', array('xml'=>'my string to send'));
To have the wsdl wrap 'my string to send' in the proper tags.
You are doing something similar, but I dont think the wsdl is actually wrapping the string you are trying to pass, and passing nothing instead, resulting in unknown error.
You can examine the outgoing xml using $client->__getLastRequest();.
(Also, you have a small typo in your code on the last line should be print_r($params);.)
Failing that you could try to write the xml yourself using SoapVar() and setting the type to XSD_ANYXML.
It seems cleaner to me when the wsdl wraps everything for you, but its better than banging your head against the wall until it does.
I made an attempt to do just that with your wsdl. Give this a try:
$wsdl = "http://mx.biopont.com/services/Vision.asmx?wsdl";
$client = new SoapClient($wsdl, array( 'soap_version' => SOAP_1_1,
'trace' => true,
));
try {
$xml = "<RegisztracioIns xmlns='http://mx.biopont.com/services/'>
<xml>string</xml>
</RegisztracioIns>";
$args= array(new SoapVar($xml, XSD_ANYXML));
$res = $client->__soapCall( 'RegisztracioIns', $args );
var_dump($res);
} catch (SoapFault $e) {
echo "Error: {$e}";
}
print_r($client->__getLastRequest());
print_r($client->__getLastResponse());
I can't exactly read the response I am getting with that given that it is Hungarian (I think?). So let me know if that works for you.

Categories