Passing Arrays as Parameters to Soap Webservice in non-WSDL mode - php

I am using Zend_Soap_Client to query data from a webservice provided by SAP. Since the auto-generated WSDL file has a few flaws, I use the non-WSDL mode of the client.
I managed to successfully call a webservice which only requires simple parameters, like strings. Example:
This is what SAP expects:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:sap-com:document:sap:soap:functions:mc-style">
<soapenv:Header/>
<soapenv:Body>
<urn:Ze12RfcGetCustHistoryNew>
<PiDateHigh>2011-12-31</PiDateHigh>
<PiDateLow>1970-01-01</PiDateLow>
<PiKunnr>1</PiKunnr>
</urn:Ze12RfcGetCustHistoryNew>
</soapenv:Body>
</soapenv:Envelope>
This is my (working) code in PHP (with $soapClient already initialized in non-WSDL mode):
$soapClient->Ze12RfcGetCustHistoryNew(
new SoapParam(date('Y-m-d'), 'PiDateHigh'),
new SoapParam('1970-01-01', 'PiDateLow'),
new SoapParam('1', 'PiKunnr')
);
But as soon as I have to pass more complex parameters to the service, it does not work. Again, an example:
This is what SAP expects:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:sap-com:document:sap:soap:functions:mc-style">
<soapenv:Header/>
<soapenv:Body>
<urn:Ze12RfcGetCustHistoryNew>
<PiDateHigh>2011-12-31</PiDateHigh>
<PiDateLow>1970-01-01</PiDateLow>
<PiKunnr>1</PiKunnr>
<PiTBelegart>
<item>
<BelegartTyp>FAKTURA</BelegartTyp>
<Belegart>ZF2</Belegart>
</item>
</PiTBelegart>
</urn:Ze12RfcGetCustHistoryNew>
</soapenv:Body>
</soapenv:Envelope>
I have tried to use a multi-dimensional array containing SoapParams, but that did not work. In WSDL mode, I could pass the params as an array, without the need of using SoapParams. How can I do this in non-WSDL mode?

just a "quick-hit" ... I'm working in a different environment, but initially had my soap-value troubles too.
One solution for a particular problem was to pass complex arrays this way:
$data = (object)$complexArray;
$result = $webserviceClient->getResult($data);
"Casting" to object results in a StdClass object ... which often works fine for webservices.
Good Luck!

I have not come up with a nice solution for this yet - currently I pass the parameters to the client object as raw xml. That works, but does not seem to be the best way to do this. This is my code now:
$params = '
<PiDateHigh>2011-12-31</PiDateHigh>
<PiDateLow>1970-01-01</PiDateLow>
<PiKunnr>1</PiKunnr>
<PiTBelegart>
<item>
<BelegartTyp>FAKTURA</BelegartTyp>
<Belegart>ZF2</Belegart>
</item>
</PiTBelegart>
';
$result = $this->_client->Ze12RfcGetCustHistoryNew(new SoapVar($params,XSD_ANYXML));

Related

PHP - How to set footer in SoapClient

With PHP, I need to send a SOAP request with a parameter (Hash) in the footer. I'm using SoapClient but I can not figure out how to do this, neither in internet searches, nor in documentation.
This is the envelope I used in the SoapUI tool to test:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:abc="" xmlns:abc1="" xmlns:abc2="">
<soapenv:Header/>
<soapenv:Body>
<abc:Method>
<!--Optional:-->
<abc:request>
<abc1:Header>
<abc1:Username>Username</abc1:Username>
<abc1:Password>Password</abc1:Password>
<!--Optional:-->
<abc1:PublicKeyUid></abc1:PublicKeyUid>
</abc1:Header>
<abc1:Body>
<abc2:Id></abc2:Id>
</abc1:Body>
<abc1:Footer>
<abc1:Hash></abc1:Hash>
</abc1:Footer>
</abc:request>
</abc:Method>
</soapenv:Body>
</soapenv:Envelope>
There is The SoapHeader class e the SoapClient::__setSoapHeaders method but I find nothing related to the footer.
I do not have access to the server and should follow this structure mentioned above.
What I need to know is how to send the HASH parameter that is inside the footer with SoapClient.
Thanks in advance for any help or suggestion.
As IMSoP and axiac have commented, the footer (<abc1: Footer>) is just a sub-structure of the envelop body and not a standard XML envelop element. I had not realized this and so I did not find it in the documentation.
So to send HASH to the requested structure, I need to pass a multidimensional array on the body element. The code looks like this:
$arrParameters = [
'request'=>[
'Header'=>[
'Username'=>$strUsername,
'Password'=>$strPassword
],
'Body'=>[
...
],
'Footer'=>[
'Hash'=>$strHash
]
]
];
$SoapClient = new SoapClient(<WSDL URL>);
$resp = $SoapClient->method($arrParameters);
Thank you IMSoP and axiac!

Calling multiple methods with same name using SoapClient

I have a SOAP webservice and in SOAP UI I see that there are methods with the same name. So, for example, there are 2 CreateNewContact methods, one of which takes 3 parameters and the other 4. Below are the stubs generated by SOAP UI
Method 1 Stub:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:rfp="http://test.com/testWebservice/">
<soapenv:Header/>
<soapenv:Body>
<rfp:CreateNewContact_FullName>
<!--Optional:-->
<rfp:fullName>?</rfp:fullName>
<!--Optional:-->
<rfp:email>?</rfp:email>
<!--Optional:-->
<rfp:telNo>?</rfp:telNo>
</rfp:CreateNewContact_FullName>
</soapenv:Body>
</soapenv:Envelope>
Method 2 Stub:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:rfp="http://test.com/testWebservice/">
<soapenv:Header/>
<soapenv:Body>
<rfp:CreateNewContact_FirstLastName>
<!--Optional:-->
<rfp:firstName>?</rfp:firstName>
<!--Optional:-->
<rfp:lastName>?</rfp:lastName>
<!--Optional:-->
<rfp:email>?</rfp:email>
<!--Optional:-->
<rfp:telNo>?</rfp:telNo>
</rfp:CreateNewContact_FirstLastName>
</soapenv:Body>
</soapenv:Envelope>
When I call the CreateNewContact method with 4 parameters using PHP SoapClient, it looks like I'm getting the response from the other method.
How can I specify which method to use using SoapClient?
Thanks,
As you can read here:
If you are using WSDL based SOAP requests and you have more than one
operation in your binding (with the same parameters), make sure the
style is set to rpc, NOT body! When you specify
'body' here, all that will be transmitted in the request is the
parameters for the function call, and SoapServer->handle() will use
the first function it finds with the same parameter-makeup to handle
the call. The actual method to call will only be included in the
request when your type is set to 'rpc', resulting in the expected
behavior
Therefore, you should check in your WSDL the operation element, which provides binding information from the abstract operation to the concrete SOAP operation.
For example:
<definitions ....>;
<binding .... >;
<operation .... >;
<soap12:operation soapAction="xs:anyURI" ?
soapActionRequired="xs:boolean" ?
style="rpc|document" ?
wsdl:required="xs:boolean" ? /> ?
</soap12:operation>
</binding>;
</definitions>
The style attribute value, if present, is a string that specifies the style for the operation. The style attribute indicates whether the operation is RPC-oriented (a messages containing parameters and return values) or document-oriented (a message containing documents). If the style attribute is omitted from the soap12:operation element, then the operation inherits the style specified or implied by the soap12:binding element in the containing wsdl:binding element.
So, in short, to solve your problem you should change the operation style from "document" to "rpc" in your WSDL.
As a further reference: https://bugs.php.net/bug.php?id=49169
I have faced the same with travelport universal API, I ended up modifying my local wsdl file to use different name for each method and it worked perfectly.

Generating schema with soap PHP client

How can I generate this schema using php soap client cant seem to get a way
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:loc="http://www.csapi.org/schema/parlayx/subscribe/manage/v1_0/local">
<soapenv:Header>
<tns:RequestSOAPHeader xmlns:tns="http://www.namesapace.com/common/v2_1">
<tns:one>***</tns:one>
<tns:two>***</tns:three>
<oauth_token>***</oauth_token>
</tns:RequestSOAPHeader>
</soapenv:Header>
<soapenv:Body>
<loc:ProductRequest>
<subInfo>
<productID>***</productID>
<isAutoExtend>0</isAutoExtend>
</subInfo>
</loc:ProductRequest>
</soapenv:Body>
</soapenv:Envelope>
On top of using the client, sometimes it may not bind to the namespace especially if using different soap versions or other strange reasons. Solved it by overriding the soap class and replacing the ns1 and ns2 values as described here http://php.net/manual/en/soapclient.dorequest.php#74123
IF no success with php soapclient, just post the request with curl.
Example in this answer - SOAP request in PHP with CURL

Undefined property while attempting to get values using SOAP

I'm having an adventure with Openmeetings' SOAP API. This is my first rodeo with SOAP so don't worry if the solution here seems obvious.
Anyway, I'm attempting to retrieve the session id with the following script.
<?php
$wsdl = "http://localhost:5080/openmeetings/services/UserService?wsdl";
$session = new SoapClient($wsdl, array("trace" =>1, "exceptions"=>0));
$value = $session->getSession();
$xml = $value->getSessionResponse;
$ssid = $xml->session_id;
print "<br/>\n SSID: $ssid";
?>
But I'm getting the following errors:
Notice: Undefined property: stdClass::$getSessionResponse in /home/sam/www/soap.php on line 5
Notice: Trying to get property of non-object in /home/sam/www/soap.php on line 6
Using soapUI, I can see that the following is sent:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.axis.openmeetings.apache.org">
<soapenv:Header/>
<soapenv:Body>
<ser:getSession/>
</soapenv:Body>
</soapenv:Envelope>
When I carry it out on soapUI, the following is returned (which contains everything I want and more):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:getSessionResponse xmlns:ns="http://services.axis.openmeetings.apache.org">
<ns:return xsi:type="ax22:Sessiondata" xmlns:ax27="http://asterisk.sip.beans.persistence.openmeetings.apache.org/xsd" xmlns:ax213="http://basic.beans.data.openmeetings.apache.org/xsd" xmlns:ax24="http://domain.beans.persistence.openmeetings.apache.org/xsd" xmlns:ax21="http://user.beans.persistence.openmeetings.apache.org/xsd" xmlns:ax22="http://basic.beans.persistence.openmeetings.apache.org/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ax22:id>14</ax22:id>
<ax22:language_id xsi:nil="true"/>
<ax22:organization_id xsi:nil="true"/>
<ax22:refresh_time>2013-09-26</ax22:refresh_time>
<ax22:sessionXml xsi:nil="true"/>
<ax22:session_id>90a4d3dc876460e119d068969def236c</ax22:session_id>
<ax22:starttermin_time>2013-09-26</ax22:starttermin_time>
<ax22:storePermanent xsi:nil="true"/>
<ax22:user_id xsi:nil="true"/>
</ns:return>
</ns:getSessionResponse>
</soapenv:Body>
</soapenv:Envelope
Since soapUI works on it, I'm certain the url I'm using is correct and that the API is solid. Can anyone find where I'm amiss in my php?
For reference, the SOAP API documentation for Openmeetings can be found here. In case anyone might find that useful or interesting.
MANY thanks in advance to anyone able to detect the error...or to anyone who gives it a shot for that matter.
A little embarrassment here. As I was editing the grammar in the post, I noticed that I should be using "return" instead of "getSessionResponse." I merely replaced
$xml = $value->getSessionResponse;
with
$xml = $value->return;
and it worked like a charm.
Sorry to waste server space :-P

JiBX/PiBX SOAP binding example

I was trying to find some examples how to write binding.xml with JiBX/PiBX for following SOAP response but with no luck. Does anyone know how to do this?
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns6:getDataResp xmlns:ns6="http://domain.com/response/data/">
<s3:requestId xmlns:s3="http://domain.com/entity/">12</s3:requestId>
<s4:errorCode xmlns:s3="http://domain.com/entity1/">0</s4:errorCode>
<ns6:dataResp>
<ns5:Data>Some string data</ns5:Data>
</ns6:dataResp>
</ns6:getDataResp>
</soapenv:Body>
</soapenv:Envelope>
If you are using JiBX you're in luck. You have a couple of options:
The apache cxf project has a databinding module for JiBX. You can use one of the open-source web servers such as servicemix to do your SOAP handling. This means you only have to bind the message schema (getDataResp in your example) with JiBX. You can find a nice example, here.
JiBX has it's own web server called JiBX/WS. It will also do all the SOAP handling for you.
I hope this helps!
Don
JiBX contributor

Categories