How to make SoapClient request with complex parameters? - php

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));

Related

Sending SOAP request via PHP?

I need to send the following SOAP request using PHP:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sup="url">
<soapenv:Header/>
<soapenv:Body>
<ControlledEnquiryRequest xmlns="url">
<Authentication>
<SubscriberDetails>
<Username>*****</Username>
<Password>***</Password>
</SubscriberDetails>
</Authentication>
<ControlledRequest>
<Asset>
<Name>Tranquility</Name>
</Asset>
<ParameterList/>
<PrimaryProduct>
<Code>Platinum</Code>
</PrimaryProduct>
</ControlledRequest>
</ControlledEnquiryRequest>
</soapenv:Body>
</soapenv:Envelope>
At the moment I've got:
$requestParams = array(
'CustomerCode' => '*****',
'Password' => '***'
);
$client = new SoapClient('url?wsdl');
$response = $client->ControlledEnquiryRequest($requestParams);
print_r($response);
However I'm getting a 500 error. I'm also unsure on how I pass the other Name and product codes through?
Obviously I'm using the actual URL instead of a 'url'. In Soap UI it's getting a result, but I can't get my head around adapting it to PHP.

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 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.

PHP SoapClient sends escaped XML characters

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.

How to use nuSOAP for messages with multiple namespaces

I'm trying to access a WebService using nuSOAP (because I'm bound to PHP4 here) that uses more than 1 namespace in a message. Is that possible?
An example request message would look like this:
<soapenv:Envelope ...
xmlns:ns1="http://domain.tld/namespace1"
xmlns:ns2="http://domain.tld/namespace2">
<soapenv:Header/>
<soapenv:Body>
<ns1:myOperation>
<ns2:Person>
<ns2:Firstname>..</ns2:Firstname>
..
</ns2:Person>
<ns1:Attribute>..</ns1:Attribute>
</ns1:myOperation>
</soapenv:Body>
</soapenv:Envelope>
I tried to following:
$client = new nusoap_client("my.wsdl", true);
$params = array(
'Person' => array(
'FirstName' => 'Thomas',
..
),
'Attribute' => 'foo'
);
$result = $client->call('myOperation', $params, '', 'soapAction');
in the hope that nuSOAP would try to match these names to the correct namespaces and nodes. Then I tried to use soapval() to generate the elements and their namespace - but if I call an operation, nuSOAP creates the following request:
<SOAP-ENV:Envelope ...>
<SOAP-ENV:Body>
<queryCCApplicationDataRequest xmlns="http://domain.tld/namespace1"/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
So something goes wrong during the "matching" phase.
After trying around with the matching, I found two possible solutions:
1) Don't use the WSDL to create the nusoap_client and soapval() to create the message
This has the disadvantage that the message contains a lot of overhead (the namespace is defined in each element). Not so fine.
2) Instead of relying on the matching of parameters, construct your reply in xml and put all the definition for prefixes in the first element - e.g.
$params = "<ns1:myOperation xmlns:ns1="..." xmlns:ns2="...">
<ns2:Person>
<ns2:Firstname>..</ns2:Firstname>
..
</ns2:Person>
<ns1:Attribute>..</ns1:Attribute>
</ns1:myOperation>";
Still not a very nice solution, but it works :-)
Building on Irwin's post, I created the xml manually and had nusoap do the rest. My webhost does not have the php soap extension, so I had to go with nusoap, and the web service I'm trying to consume required the namespaces on each tag (e.g. on username and password in my example here).
require_once('lib/nusoap.php');
$client = new nusoap_client('https://service.somesite.com/ClientService.asmx');
$client->soap_defencoding = 'utf-8';
$client->useHTTPPersistentConnection(); // Uses http 1.1 instead of 1.0
$soapaction = "https://service.somesite.com/GetFoods";
$request_xml = '<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<n1:GetFoods xmlns:n1="https://service.somesite.com">
<n1:username>banjer</n1:username>
<n1:password>theleftorium</n1:password>
</n1:GetFoods>
</env:Body>
</env:Envelope>
';
$response = $client->send($request_xml, $soapaction, '');
echo '<h2>Request</h2><pre>' . htmlspecialchars($client->request, ENT_QUOTES) . '</pre>';
echo '<h2>Response</h2><pre>' . htmlspecialchars($client->response, ENT_QUOTES) . '</pre>';
echo '<h2>Debug</h2><pre>' . htmlspecialchars($client->getDebug(), ENT_QUOTES) . '</pre>';
Then I had an error that said:
Notice: Undefined property: nusoap_client::$operation in ./lib/nusoap.php on line 7674
So I went the lazy route and went into nusoap.php and added this code before line 7674 to make it happy:
if(empty($this->operation)) {
$this->operation = "";
}
Another bypass this issue would be a modification to nusoap_client::call() function. Next to the this line (7359 in version 1.123) in nusoap.php:
$nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
I added this one:
$nsPrefix = $this->wsdl->getPrefixFromNamespace("other_ns_name");
And it worked! Since I only needed this library for one project, it was OK for me to hardcode this hack. Otherwise, I would dig more and modify the function to accept array instead of string for a namespace parameter.
Yeah, i've been having this same problem (found your q via google!) and i've come across this:
http://www.heidisoft.com/blog/using-nusoap-consume-net-web-service-10-min
Here, the dev creates the xml body of the message in coe and then uses nusoap to submit.

Categories