SOAP PHP : how to translate request file to PHP function call - php

I'm beginning with the SOAP lib of PHP and i can't figure out how to execute my request :
The server has a user friendly API which gives me the request to pass but i can't tell how I am supposed to do so.
Here is the point I currently am :
$soap = new SoapClient("https://www.dmc.sfr-sh.fr/DmcWS/1.5.6/MessagesUnitairesWS?wsdl");
$soap->getSingleCallCra();
and the request i should pass :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://servicedata.ws.dmc.sfrbt/">
<soapenv:Header>
<ser:authenticate>
<serviceId>********</serviceId>
<servicePassword>******</servicePassword>
<spaceId>*******</spaceId>
<lang>fr_FR</lang>
</ser:authenticate>
</soapenv:Header>
<soapenv:Body>
<ser:getSingleCallCra>
<beginDate>2017-10-17T00:00:00</beginDate>
</ser:getSingleCallCra>
</soapenv:Body>
</soapenv:Envelope>
The SOAP client does work for other function with no parameter but i get a translated java NPE exception when i call this function.
Can anyone tell me how i can pass the parameters and authentification to the function ?
Thanks.

$soap = new SoapClient("https://www.dmc.sfr-sh.fr/DmcWS/1.5.6/MessagesUnitairesWS?wsdl");
To add headers to a soapcall use the __setSoapHeaders method like this:
$soap->__setSoapHeaders(array(
//(namespace, name, data)
new SoapHeader("http://servicedata.ws.dmc.sfrbt/",'authenticate',array(
'serviceId' => '********',
'servicePassword' => '******',
'spaceId' => '*******',
'lang' => 'fr_FR',
))
));
These parameters will go into the soap body. In PHP you can use objects or associative arrays as input as they are both interpreted into xml as key => value pairs.
$soap_body_parameters = array(
'beginDate' => '2017-10-17T00:00:00',
);
$response = $soap->getSingleCallCra($soap_body_parameters);
print_r($response);
The return value of the soapclient class is always an object, so remember to use the arrow notation '$object->property' to get the relevant data out.
You can also create a class like this, that will deal with the headers, data extraction, etc. in the background for each call
class sfr_soap {
function __construct($serviceId, $servicePassword, $spaceId, $lang = 'fr_FR'){
$url = "https://www.dmc.sfr-sh.fr/DmcWS/1.5.6/MessagesUnitairesWS?wsdl";
$this->client = new SoapClient($url);
$soap->__setSoapHeaders(array(
new SoapHeader("http://servicedata.ws.dmc.sfrbt/",'authenticate',array(
'serviceId' => $serviceId,
'servicePassword' => $servicePassword,
'spaceId' => $spaceId,
'lang' => $lang,
))
));
}
public function __call($name, $args = array()){
$response = $this->client->$name($args);
// do something with the response here, like extract the meaningful parts of the data
return $response;
}
}
init like this
$sfr = new sfr_soap($serviceId, $servicePassword, $spaceId);
or like this if you want to specify the language
$sfr = new sfr_soap($serviceId, $servicePassword, $spaceId, $lang);
use like this
$data = $sfr->getSingleCallCra(array(
'beginDate' => '2017-10-17T00:00:00'
));

You can pass arguments to a SOAP function call multiple ways as it is stated in the documentation: SoapClient::__soapCall
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 in your case, the call should be:
$soap->getSingleCallCra(array(
'beginDate' => '2017-10-17T00:00:00',
));
I hope, I could be of any help.

Related

soap client calling function with parameters and type

I have soap request with body
<soap:Body>
<ProcessRequest>
<request xsi:type="GetNotification">
<node1>val1</node2>
<node2>val2</node2>
</request>
</ProcessRequest>
</soap:Body>
I am trying to pass request type GetNotification in soap client call in PHP but its not working.
in $args I am passing
$args = ['node1' => 'val1','node2'=>'node2'];
$response = $client->ProcessRequest(['request'=>$args])
how do i pass type GetNotification
You can use this tool:
https://github.com/wsdl2phpgenerator/wsdl2phpgenerator
You can give it the soap url and it will generate full list of classes for the service.
Using these classes it will be much easier to understand what and how you need to send you request.
I think this should be something like this. Because the xsi:type indicates that request value should be GetNotification. Keep in mind that i didn't test it and it's just a thought.
<?php
//using arrays
$response = $client->ProcessRequest(['request' => [
'GetNotification' => ['node1' => 'val1', 'node2' => 'val2']
]);
//using objects
class request
{
private $GetNotification;
public function __construct(GetNotification $getNotification)
{
$this->GetNotification = $getNotification;
}
}
class GetNotification
{
private $node1;
private $node2;
public function __construct(string $node1, string $node2)
{
$this->node1 = $node1;
$this->node2 = $node2;
}
}
$response = $client->ProcessRequest(
new request(new GetNotification('val1', 'val2'))
);

SOAP client method with parameter returns null eventhough there is data

The SOAP server I call has two functions
GetUserInfo and
GetAllUserInfo
the client calls the second function perfectly and displays all users. But the first function returns a null. I know it has something to do with the parameters, so I have tried a number of possible ways, still nothing. Here is the xml of soap and the function I used.
1.GetUserInfo
Request Xml:
<GetUserInfo>
<ArgComKey xsi:type="xsd:integer”>ComKey</ArgComKey>
<Arg>
<PIN xsi:type="xsd:integer”>Job Number</PIN>
</Arg>
</GetUserInfo>
Response Xml:
<GetUserInfoResponse>
<Row>
<PIN>XXXXX</PIN>
<Name>XXXX</Name>
<Password>XXX</Password>
2.GetAllUserInfo *
Request Xml:
<GetAllUserInfo>
<ArgComKey xsi:type="xsd:integer”>ComKey</ArgComKey>
</GetAllUserInfo>
Response Xml:
<GetAllUserInfoResponse>
<Row>
<PIN>XXXXX</PIN>
<Name>XXXX</Name>
<Password>XXX</Password>
< Group>X</ Group>
And here is the code I wrote for the client I use to get a specific user and all users.
try {
$opts = array('location' => 'http://192.168.0.201/iWsService',
'uri' => 'iWsService');
$client = new \SOAPClient(null, $opts);
$attendance = $client->__soapCall('GetUserInfo', array('PIN'=> 2));
var_dump($attendance);
} catch (SOAPFault $exception) {
print $exception;
}
When I called GetAllUserInfo with a parameter of empty array() it returns all the users. Including a user with PIN = 2. But the GetUserInfo method returns null. Am I missing something when I called the GetUserInfo method?
In order to make the correct SOAP call for GetUserInfo, you will need to know 2 parameters: the ComKey and the PIN. In my SOAP call below, I assume that the ComKey is 12345, but you will have to replace this value with something meaningful. Try using the following code:
try {
$opts = array('location' => 'http://192.168.0.201/iWsService',
'uri' => 'iWsService');
$client = new \SOAPClient(null, $opts);
$attendance = $client->__soapCall('GetUserInfo', array('ArgComKey'=>12345,
'Arg' => array('PIN' => 2)));
var_dump($attendance);
} catch (SOAPFault $exception) {
print $exception;
}
Here I am trying to match the XML format for the GetUserInfo request as closely as possible.

PHP SOAP client namespacing

I have a SoapClient instance and I'm trying to make a request (duh!). I am able to pass an array of parameters as key => value in the first level like, securityToken. But I can't send to the second namespace (I think that's what it is) stap. The following is a simplified version of what the inside of my ENV should look like. I know the Envelope should contain a reference to xmlns:stap but I can't work out how to get SoapClient to do that.
<soapenv:Body>
<ns:PlaceOrder>
<ns:securityToken></ns:securityToken>
<ns:orderRequest>
<stap:Headers>
<stap:OrderRequestHeader>
<stap:Lines>
<stap:OrderRequestLine>
<stap:QuantityRequested></stap:QuantityRequested>
<stap:StockCode></stap:StockCode>
</stap:OrderRequestLine>
</stap:Lines>
</stap:OrderRequestHeader>
</stap:Headers>
</ns:orderRequest>
</ns:PlaceOrder>
And here's my _soap function
protected function _soap($request, $parameters = array(), $service = null, $options = array()) {
$client = new SoapClient($service, $options);
$response = $client->{$request}($parameters);
return $response;
}

Calling WCF with PHP -- variable structures

I have a WCF Service written in .Net 4.0 that accepts two parameters. One is a complex type consisting of User, MerchantName, and Password, the second variable is an int. The service returns a third complex type.
It's structure looks like the following:
//*C# Code *
public sub AccountData Log(Login LoginData, int AccountID)
{
//do stuff here
}
Using SoapClient and removing the int AccountID from the C# service, I can pass the complex data in and parse through the complex data output succesfully. Adding the AccountID parameter, breaks the soap call. I can't seem to compound the variables into one array in a fashion that WCF will accept.
The question is how to form the array to pass in the call?
I have tried the following:
//****Attempt one *******
$login = array('MerchantName' => 'merchantA',
'User' => 'userA',
'password' => 'passwordA');
$account = '68115'; //(also tried $account = 68115; and $account = (int)68115;)
$params = array('LoginData' => $login, 'AccountID' => $account);
$send = (object)$params; //Have tried sending as an object and not.
$client = new SoapClient($wsdl);
$client->soap_defencoding = 'UTF-8';
$result = $client->__soapCall('Log', array($send);
var_dump($send);
echo("<pre>");
var_dump($result);
The latest attempt was to class the variables but I got stuck when tring to form into the $client call.
class LogVar
{
public $MerchantName;
public $User;
public $Password;
}
class AccountID
{
public $AccountID;
}
$classLogin = new LogVar();
$classLogin->MerchantName = 'merchantA';
$classLogin->User = 'userA';
$classLogin->Password = 'passwordA';
$classAccount = new AccountID();
$classAccount->AccountID = '68115';
//How to get to $client->__soapCall('Log', ???????);
P.S. I'm a .Net coder, please be kind with the PHP explanations... Also NuSoap didn't seem much better, however it may have undiscovered ways of dealing with complex types.
This worked for me with standard SoapClient:
$client = new SoapClient($wsdl, array('trace' => true));
$data = $client->Log(array('AccountID' => 23, 'LoginData' => array('User' => '123', 'Password' => '123', 'MerchantName' => '123')));
// echo $client->__getLastRequest();
var_dump($data);
You can display the last request XML and compare it with what a WCF client is generating. This is how I figured it out: I generated a WCF client, inspected the XML message generated by it and compared to $client->__getLastRequest.
Note: You can call the method by its name on a SoapClient rather than use $client->__soapCall('operationName')

SOAP request with attribute

I can not seem to find out how to set an attribute to a SOAP request without using the XSD_ANYXML encoding.
The request parameter should look as follows
<request
xmlns:ns="/some/ns">
...
<ns:parameter attr="some attribute">
value
</ns:parameter>
...
</request>
Of course the following code works, but it's rather ugly (ugly, because it uses string concatenation where it should use the SOAP_Client API and because it does not use the general namespace)
$param = new SoapVar(
'<ns_xxx:parameter xmlns:ns_xxx="/some/ns" attr="some attribute">
value
</ns_xxx:parameter>',
XSD_ANYXML
);
Is there a better way to create a SOAP request parameter with a namespace and an attribute?
I am looking for s.th. like the following (this is just some pseudo code using the SoapVar API):
$param = new SoapVar(
array(
'_' => 'value',
'attr' => 'some attribute'
),
SOME_ENCODING,
null,
null,
null,
'/some/ns'
);
For this, you need to derived the class from SoapClient and Override the method __doRequest():
class ABRSoapClient extends SoapClient {
// return xml request
function __doRequest($request, $location, $action, $version) {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$xml= $dom->loadXML($request);
// Goto request Node and Set the attribute
$attr_ns = $dom->createAttributeNS('xmlns:ns', '' ); // instead of xmlns:ns use Namespace URL
$attr_ns->value = '/some/ns';
// add atribute in businessReport node
$dom->getElementsByTagName($report_type)->item(0)->appendChild( $attr_ns );
$request = $dom->saveXML();
return parent::__doRequest($request, $location, $action, $version);
}
}
$client = new ABRSoapClient(.....);
$save_result = $client->request($param);
// You can check the form request using function
$client->__getLastRequest();
I hope this will resolve your problem.
SOAP does not support attributes, may be you should use REST instead!
EDIT:
Please check the body style w3c:"4.3 SOAP Body" and remember that
you need to encode your message with "soap-envelope" namespace and describe
your XML types thats why, you can't use attributes to describe your message data.
But if you ask me, it can be made possible! You can use a custom SoapClient parser or something like that and convert your message as you like it.
A example of that may be RSS over SOAP http://www.ibm.com/developerworks/webservices/library/ws-soaprdf.
But, the problem would be that you would miss the descriptive information about your message data/types and other clients could not easy understand your messages!
My best practice for you would be to use elements instead of attributes,
i know you need to fix your XML schema but thats the way it goes or switch to a other technology.
SOAP 1 does support attributes. Here is an example of Perl code using both attributes and values (from a client):
$som = $client->call(
'tran:getContent',
SOAP::Header->name('cred:credentials')->attr({
'username' => $username,
'password' => 'xxx',
'customerID' => 'xxx'}
),
SOAP::Data->name('contentID')->value('9999')
)

Categories