PHP SoapClient sends escaped XML characters - php

I create an XML document using PHP's XMLWriter.
$xmlWriter = new \XMLWriter();
$xmlWriter->openMemory(); //generate XML in-memory, not on disk
//Please keep the indentation intact to preserve everybody's sanity!
$xmlWriter->startElement('RootElement');
// ...
$xmlWriter->endElement();
$myXml = $xmlWriter->outputMemory(true);
Now I connect to a SOAP service in non-WSDL mode.
$soapClient = new \SoapClient(null, array(
"location" => "https://theservice.com:1234/soap/",
"uri" => "http://www.namespace.com",
"trace" => 1
)
);
$params = array(
new \SoapParam($myXml, 'param')
);
$result = $soapClient->__soapCall('method', $params);
The problem is that the SOAP message received by the SOAP service contains my data as escaped XML characters. (Warning : dummy SOAP message ahead!)
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope" ...>
<SOAP-ENV:Header>
...
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:method>
<Request xsi:type="xsd:string">
<Root>
(escaped data)
</Root>
</Request>
</ns1:method>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The SOAP service will not work with escaped data but, on the other hand, I don't escape my data, the SoapClient does so. How do I make my SoapClient send unescaped data?

Escaping characters is the expected behavior of the XmlWriter::writeElement method.
It took me a while to figure it out but the answer was simple : put a SoapVar with the XSD_ANYXML parameter into the SoapParam :
$params = array(
new \SoapParam(new \SoapVar($myXml, XSD_ANYXML), 'param')
);
The SoapVar documentation mentions that its second parameter (encoding) can be "one of the XSD_... constants" which are not documented in the PHP docs.

Related

PHP soap xml vs SoapUI xml

I have a working SOAP UI xml, and I have my SOAP request XML and they are almost identical. SOAP UI works, mine gets a null response. Here's the SOAPUI XML first
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:get="http://www.cornerstoneondemand.com/Webservices/GetDetailsWebService">
<soapenv:Header>
<get:AuthHeader>
<get:CorpName>corp</get:CorpName>
<get:UserId>1234</get:UserId>
<get:Signature>ABC123</get:Signature>
</get:AuthHeader>
</soapenv:Header>
<soapenv:Body>
<get:GetDetails xmlns:get="http://www.cornerstoneondemand.com/Webservices/GetDetailsWebService">
<get:object_id>qwerty-123</get:object_id>
</get:GetDetails>
</soapenv:Body>
</soapenv:Envelope>
and here is my PHP code and the request.
$client=new SoapClient($wsdl,array('trace' => 1, 'exception' => 0));
$auth = array(
'CorpName' => $CorpName,
'UserId' => $username,
'Signature' => $Signature
);
$header = new SoapHeader('http://www.cornerstoneondemand.com/Webservices/GetDetailsWebService','AuthHeader',$auth,false);
$client->__setSoapHeaders($header);
$parm[] = new SoapVar($LOid, XSD_STRING, null, null, 'object_id' );
var_dump($client->GetDetails( new SoapVar($parm, SOAP_ENC_OBJECT) )); //output is NULL
//and the PHP request:
print_r($client->__getLastRequest());
output is
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.cornerstoneondemand.com/Webservices/GetDetailsWebService">
<SOAP-ENV:Header>
<ns1:AuthHeader>
<ns1:CorpName>corp</ns1:CorpName>
<ns1:UserId>1234</ns1:UserId>
<ns1:Signature>ABC123</ns1:Signature>
</ns1:AuthHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:GetDetails>
<object_id>qwerty-123</object_id>
</ns1:GetDetails>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I can't tell if I'm close to creating a good request, or miles off. I'm working to make the PHP request match SOAPUI's since it works and mine doesn't.
The contain the nearly same information. Namespace prefixes for element nodes are exchangeable and optional. So all these 3 variants are resolved to and element node with the local name Envelope in the namespace http://schemas.xmlsoap.org/soap/envelope/.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"/>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"/>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"/>
You can read the element name as {http://schemas.xmlsoap.org/soap/envelope/}:Envelope.
The same goes for the get vs ns1 namespaces prefixes. They both resolve to the same actual namespace.
But the element object_id has no namespace in your XML. The sixth argument of the SoapVar constructor is the node namespace so you might want to try:
$namespace = 'http://www.cornerstoneondemand.com/Webservices/GetDetailsWebService';
$parm[] = new SoapVar($LOid, XSD_STRING, null, null, 'object_id', $namespace);

PHP SoapClient always returns NULL reponse even if I get a valid XML debug

I've been trying to get data from a SOAP service using PHP SoapClient.
If I point to http://my-service?WSDL (obviously I can't expose the correct one) away from PHP on a well know tool called SOAP UI with the right parameters I get a proper response. But In PHP I always get NULL in my response even with the right params and stuff.
This is my code:
<?php
$client = new SoapClient('http://my-service?WSDL',
array('trace'=>true, 'exceptions'=>true));
$params= ['EBMHeader'=> ['EBMID'=> 'anyvalue'],
'DataArea'=> ['Ip'=> '127.0.0.1', 'RequestId'=> '12345']];
try {
$response= $client->MySoapFunction($params);
var_dump($response); // returns always NULL
echo $client->__getLastResponse(); //Returns a valid XML with the proper data
} catch (Exception $exception) {
echo $exception;
}
I already tried these:
Double checked the wsdl and parameters
Changed soap options: trace and exceptions
Changed params keys to see if it fails, it does and SOAP server responds that the param is misssing or whatever the error is, for instance I can say that I'm getting a response from SOAP server.
Tried to debug using __getLastResponse(), it shows a valid XML with the expected data, also I validated the XML to see if it's ok.
Tried to use $client->__soapCall('MySoapFunction', $params, null);
and the result is the same.
I also tried to use the __getLastResponse() output and parse it to var so them i could convert it to an array but the result is an empty object:
$xml= $client->__getLastResponse();
$response = simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA);
This is my XML request from PHP with $client->__getLastRequest():
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://url.com/EnterpriseObjects/Common/EBM/Ebm/V1/" xmlns:ns2="http://url.com/EnterpriseObjects/Autogestion/Producto/ABM/V1/">
<SOAP-ENV:Body>
<ns2:ConsultarProductoABM>
<ns1:EBMHeader>
<ns1:EBMID>whatever</ns1:EBMID>
</ns1:EBMHeader>
<ns2:DataArea>
<ns2:Ip>127.0.0.1</ns2:Ip>
<ns2:RequestId>12345</ns2:RequestId>
</ns2:DataArea>
</ns2:ConsultarProductoABM>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
And this is my response $client->__getLastResponse():
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Body xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<v1:ConsultarProductoResponseEBM xmlns:v1="http://url.com/EnterpriseObjects/Core/EBM/Producto/V1/">
<v11:EBMHeader xmlns:v11="http://url.com/EnterpriseObjects/Common/EBM/Ebm/V1/">
<v11:EBMID>anything</v11:EBMID>
<v11:CreationDateTime>2015-10-16T19:09:54.962-03:00</v11:CreationDateTime>
<v11:Sender>ATG</v11:Sender>
<v11:Target>SMFLX</v11:Target>
<v11:BusinessScope>SIN_CONFIGURACION</v11:BusinessScope>
<v11:BusinessScope>SIN_CONFIGURACION</v11:BusinessScope>
<v11:BusinessScope>SIN_CONFIGURACION</v11:BusinessScope>
<v11:BusinessScope>e6e00a83-ce05-4e2a-bdb6-d7bfbd14d84a</v11:BusinessScope>
<v11:EBMTracking>ConsultarProductoAutogestionReqABCS (V1).ConsultarProducto_+++_ConsultarProductoProvisioningProvABCS (V1).ConsultarProducto_+++_ValidarRespuestaEBS (V1).ValidarRespuesta_+++_ConsultarProductoOpenSmartFlexProvABCS (V1).ConsultarProducto_+++_ValidarRespuestaEBS (V1).ValidarRespuesta</v11:EBMTracking>
<v11:FaultNotification>
<ns1:FaultMessage xmlns:ns2="http://url.com/EnterpriseObjects/Autogestion/Producto/ABM/V1/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://url.com/EnterpriseObjects/Common/EBM/Ebm/V1/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"/>
<ns1:FaultingService xmlns:ns2="http://url.com/EnterpriseObjects/Autogestion/Producto/ABM/V1/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://url.com/EnterpriseObjects/Common/EBM/Ebm/V1/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"/>
<ns1:Destination xmlns:ns2="http://url.com/EnterpriseObjects/Autogestion/Producto/ABM/V1/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://url.com/EnterpriseObjects/Common/EBM/Ebm/V1/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"/>
</v11:FaultNotification>
</v11:EBMHeader>
<v11:ReturnCode xmlns:v11="http://url.com/EnterpriseObjects/Common/EBM/Ebm/V1/">0</v11:ReturnCode>
<v11:ReturnMessage xmlns:v11="http://url.com/EnterpriseObjects/Common/EBM/Ebm/V1/"/>
<v11:ErrorCode xmlns:v11="http://url.com/EnterpriseObjects/Common/EBM/Ebm/V1/">0</v11:ErrorCode>
<v1:DataArea>
<v1:InstalledProduct>
<v11:Hub xmlns:v11="http://url.com/EnterpriseObjects/Core/EBO/InstalledProduct/V1/">BON</v11:Hub>
<v11:Node xmlns:v11="http://url.com/EnterpriseObjects/Core/EBO/InstalledProduct/V1/">BON076A</v11:Node>
<v11:CustomerParty xmlns:v11="http://url.com/EnterpriseObjects/Core/EBO/InstalledProduct/V1/">
<v12:Id xmlns:v12="http://url.com/EnterpriseObjects/Core/EBO/CustomerParty/V1/">7889679</v12:Id>
<v12:PartyHuman xmlns:v12="http://url.com/EnterpriseObjects/Core/EBO/CustomerParty/V1/">
<v13:FirstName xmlns:v13="http://url.com/EnterpriseObjects/Core/EBO/Common/V1/">JOHN</v13:FirstName>
<v13:LastName xmlns:v13="http://url.com/EnterpriseObjects/Core/EBO/Common/V1/">DOE</v13:LastName>
</v12:PartyHuman>
<v12:Location xmlns:v12="http://url.com/EnterpriseObjects/Core/EBO/CustomerParty/V1/">
<v13:Address xmlns:v13="http://url.com/EnterpriseObjects/Core/EBO/Common/V1/">
<v13:ProvinceName>LACATION</v13:ProvinceName>
<v13:StateName>LACATION</v13:StateName>
<v13:CityName>LACATION</v13:CityName>
</v13:Address>
</v12:Location>
</v11:CustomerParty>
<v11:ContractID xmlns:v11="http://url.com/EnterpriseObjects/Core/EBO/InstalledProduct/V1/">123456</v11:ContractID>
<v11:Service xmlns:v11="http://url.com/EnterpriseObjects/Core/EBO/InstalledProduct/V1/">
<v11:ClassId>52</v11:ClassId>
</v11:Service>
</v1:InstalledProduct>
</v1:DataArea>
</v1:ConsultarProductoResponseEBM>
</soap-env:Body>
</soapenv:Envelope>
The problem is that i don't know what else to do to get a valid php response if i do:
$response= $client->MySoapFunction($params);
var_dump($response);

PHP SOAPCall null result

I have deployed a SOAP WS with AXIS. I use SOAPClient library and PHP 5.5.9 on Ubuntu 14.04 to call the exposed operations, but when I make the "__SoapCall" it returns nothing. I have also tried with "NuSOAP" library, but I obtain the same result.
But when I call "__getLastResponse", It returns the good response (although duplicated), as you can see:
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<idXMLReturn xmlns="http://aemetproyecto">PD94bWwgdm...</idXMLReturn>
</soapenv:Body>
</soapenv:Envelope>
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<idXMLReturn xmlns="http://aemetproyecto">PD94bWwgdm...</idXMLReturn>
</soapenv:Body>
</soapenv:Envelope>
Here is the WSDL
My PHP code:
<?php
function generarTabla($id, $formato){
try{
// Create the SoapClient instance
$url = "http://localhost:8080/axis/services/AemetProyect?wsdl";
$client = new SoapClient($url, array("trace" => 1, "exception" => 1));
// Creo XML
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
$xml .= "<!DOCTYPE id [\n";
$xml .= "<!ELEMENT id (#PCDATA)>\n";
$xml .= "]>\n";
$xml .= "<id>".$id."</id>";
// Codifico XML en Base64
$Base64xml = base64_encode($xml);
// Llamada SOAP a DescargarInfoTiempo
$resultado = $client -> __soapCall("DescargarInfoTiempo",
array($Base64xml));
//$resultado = $client -> __getLastResponse();
//echo $resultado;
//$resultado = $client -> __getLastRequest();
//echo $resultado;
echo $resultado;
} catch (SoapFault $ex){
$error = "SOAP Fault: (faultcode: {$ex->faultcode}\n"
."faultstring: {$ex->faultstring})";
echo $error;
} catch (Exception $e){
$error = "Exception: {$e->faultstring}";
echo $error;
}
}
?>
I've noticed that the name of return element ("idXMLReturn") is not the same that the name described in WSDL ("DescargarInfoTiempoReturn").
Can this be the problem?
Update
I've tried to do:
$argumens['idXML'] = $Base64xml;
$resultado = $client -> __soapCall("DescargarInfoTiempo",
array($arguments));
and
$arguments['idXML'] = $Base64xml;
$resultado = $client ->DescargarInfoTiempo($arguments);
But then I get "Notice: Array to string conversion" when I do the call.
Your error is in a little detail.
For a call using the SoapClient, you must send the arguments in a array of arguments, instead to send a xml (why you need to do a codification base64? Is some rule of your WebService?). See the PHP docs for get the right way
arguments
An array of the arguments to pass to the function. This can be either an ordered or an associative array. Note that most SOAP servers require parameter names to be provided, in which case this must be an associative array.
So, your array arguments* must to be something like this:
$arguments['id'] = $id// if you need base 64, use base64_encode($id) or something
$resultado = $client -> __soapCall("DescargarInfoTiempo",array($arguments));
Other option to do the same thing, is call the function directly:
$arguments['id'] = $id// if you need base 64, use base64_encode($id) or something
$resultado = $client ->DescargarInfoTiempo($arguments);// note that we don't need array($arguments), just $arguments
See other examples in the comments on the doc page.
Finally, if you still want to create your XML to send, I advise you to do it with other methos like file_get_contents or CURL and don't forget to create your XML with a soap envelop, is needed for the Soap Protocol
*Some old WebServices needs a array with name "parameters"
UPDATE
Look, you're trying to send your $arguments in a array that contains only one element: The XML in your $Base64xml var. I think that The problem still here.
According to PHP manual, you can't send XML in your SoapCall. The var must be a associative array with your vars, so try to do something like this:
$arguments['id'] = $id// this $id var is your function argument(int, string or somenthing else), forget the created XML
$resultado = $client -> __soapCall("DescargarInfoTiempo",array($arguments));
About the base64 that you need, I never needed it before, but see the marcovtwout comment in this page of PHP manual
If your WSDL file containts a parameter with a base64Binary type, you should not use base64_encode() when passing along your soap vars. When doing the request, the SOAP library automatically base64 encodes your data, so otherwise you'll be encoding it twice.
So, I belive that you don't need to encode your vars.
In short, forget the XML and send only your vars. The PHP SoapClient create the Soap envelop with the corrected encodes and all these things.
If you still with problems doing this, try to enclose your var with some SoapVars. Maybe your WSDL configuration needs this treatment.
I hope this helps
The solution is to specify the document literal style as it is explained in
Creating a SOAP call using PHP with an XML body
As I said above, "I've noticed that the name of return element ("idXMLReturn") is not the same that the name described in WSDL ("DescargarInfoTiempoReturn")"
So, that's the problem: AXIS doesn't generate a Envelope that fits to the WSDL schema. It seems java:RPC provider doesn't worry about the return names of operations.
For now, I've solved it by renaming the parameter from "idXML" to "DescargarInfoTiempo", so SOAP response will be "DescargarInfoTiempoReturn" and it will fit with the WSDL schema and PHP will map the response correctly.

How to make SoapClient request with complex parameters?

This is the first time I work with SoapClient on PHP. My task is to create a script to automatically send a Soap request to the server. The correct request in SOAP UI is:
Soap URL: http://example.com:8181/inventory/soap/inventory-api?wsdl
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:inv="http://example.com:8181/inventory-api/">
<soapenv:Header/>
<soapenv:Body>
<inv:searchStockItemRequest>
<inv:filter>
<inv:ItemID>100</inv:ItemID>
<inv:ItemID>101</inv:ItemID>
<inv:ItemID>102</inv:ItemID>
</inv:filter>
</inv:searchStockItemRequest>
</soapenv:Body>
</soapenv:Envelope>
It means: Search the StockItem with ID is 100 or 101 or 102.
This is my current code.
$xml = '<inv:filter>
<inv:ItemID>100</inv:ItemID>
<inv:ItemID>101</inv:ItemID>
<inv:ItemID>102</inv:ItemID>
</inv:filter>';
$client = new SoapClient(null, array(
'location' => 'http://example.com:8181/inventory/soap/inventory-api?wsdl',
'uri' => MCA_INVENTORY_WSDL)
);
$result = $client->searchItem(htmlspecialchars($xml));
And the result is fault with a message:
"Missing required element {http://example.com:8181/inventory-api/}filter"
I think that the server cannot detect the above filter element. Anyone please help!!!
Soap URL: http://example.com:8181/inventory/soap/inventory-api?wsdl
This is your wsdl address, not your service end point url.
Open above url in web browser. Find this tag <soap:address location> in your wsdl. It should be within <service> tag at end of wsdl. Now replace wsdl url with this end point url.
It should work.
$client = new SoapClient(null, array(
'location' => 'http://example.com:8181/inventory/soap/inventory-api?wsdl',
'uri' => MCA_INVENTORY_WSDL)
I think you have to use a correct data format instead of a XML string. Based on your correct request using SOAP UI, the code using SoapClient should be like the below:
$client = new SoapClient('http://example.com:8181/inventory/soap/inventory-api?wsdl');
$result = $client->searchStockItemRequest(array(
'filter' => array(
'ItemID' => array(100, 101, 102)
)
));
I am not sure the ItemID array is a correct format. You can print the request to check:
echo $client->__getLastRequest();
Don't forget to enable the tracing option:
$client = new SoapClient('...', array('trace' => true));

Subclassing PHP SoapClient _soapCall to handle XML instead of an array for arguments

Here is my problem:
I need a way to send the arguments($xmlNusoap) in my _soapCall as a string, then somehow subclass _soapCall or _doRequest to handle the XML string instead of an associative array. If someone knows how to do this you would be a life saver!
// Init SoapClient
$soapClient = new SoapClient("http://xxxx:8080/TransactionServices/TransactionServices6.asmx?WSDL", array("trace" => 1));
// My request as string
$xmlNusoap='<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.micros.com/pos/les/TransactionServices"><SOAP-ENV:Body><ns1:PostTransaction/></SOAP-ENV:Body></SOAP-ENV:Envelope><ns1:PostTransaction>
<ns1:REQ>
<ns1:RequestHeader>
<ns1:InterfaceVersion>3.0.7</ns1:InterfaceVersion>
<ns1:ClientName>TRANS_SERVICES</ns1:ClientName>
</ns1:RequestHeader>
<ns1:CheckDetailEntries>
<ns1:MenuItem>
<ns1:ReferenceEntry>Pizza4</ns1:ReferenceEntry>
<ns1:Count>1</ns1:Count>
<ns1:Price>10.00</ns1:Price>
<ns1:ItemNumber>112001</ns1:ItemNumber>
<ns1:PriceLevel>1</ns1:PriceLevel>
<ns1:Seat xsi:nil="true"/>
</ns1:MenuItem>
</ns1:CheckDetailEntries>
<ns1:CheckHeaderRequest>
<ns1:CheckId>03:21:05.050505</ns1:CheckId>
<ns1:GuestCount>1</ns1:GuestCount>
<ns1:GuestInformation>
<ns1:ID>001</ns1:ID>
<ns1:FirstName>xxx</ns1:FirstName>
<ns1:LastName>xxx</ns1:LastName>
<ns1:Address1>xxx Rd</ns1:Address1>
<ns1:Address2>xx</ns1:Address2>
<ns1:Address3>xx</ns1:Address3>
<ns1:PhoneNum>xx</ns1:PhoneNum>
<ns1:UserText1>None</ns1:UserText1>
<ns1:UserText2>None</ns1:UserText2>
<ns1:UserText3>None</ns1:UserText3>
<ns1:GUID></ns1:GUID></ns1:GuestInformation>
</ns1:CheckHeaderRequest>
<ns1:OrderTypeNumber>1</ns1:OrderTypeNumber>
</ns1:REQ>
</ns1:PostTransaction>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
$PostTransaction = $soapClient->__soapCall("PostTransaction", array($xmlNusoap));
Normally, I build the $args to be an associative array. However, this will not work because I need to maintain a certain order of children elements.

Categories