I would like to query some stuffs via SOAP by generating WSDL with NuSOAP.
I know there are lots of questions related to the topic, but I didn't have success to adapt the codes to my particular problem.
I was successful in generating WSDL code which returns just an array of structs (associative array), BUT I would rather like to return an object (struct) which contains an integer variable, a string variable AND an array of structs.
So, this is the code that works for returning an array of structs:
<?php
function getStuffs( $user='', $pass='' ) {
// here we can check user and pass and do whatever (if it isn't alright, we can throw exception or return NULL or sg. similar)
// .......
$stuff_array = array();
$stuff_array[] = array( 'id'=>122, 'name'=>'One stuff');
$stuff_array[] = array( 'id'=>213, 'name'=>'Another stuff');
$stuff_array[] = array( 'id'=>435, 'name'=>'Whatever stuff');
$stuff_array[] = array( 'id'=>65, 'name'=>'Cool Stuff');
$stuff_array[] = array( 'id'=>92, 'name'=>'Wow, what a stuff');
return $stuff_array;
}
require_once 'nusoap/lib/nusoap.php';
$server = new soap_server;
// $myNamespace = $_SERVER['SCRIPT_URI'];
$myNamespace = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];
$server->configureWSDL('MyStuffService', 'urn:' . $myNamespace);
// $server->wsdl->schemaTargetNamespace = 'http://soapinterop.org/xsd/';
$server->wsdl->addComplexType(
// name
'Stuffs',
// typeClass (complexType|simpleType|attribute)
'complexType',
// phpType: currently supported are array and struct (php assoc array)
'struct',
// compositor (all|sequence|choice)
'all',
// restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
'',
// elements = array ( name = array(name=>'',type=>'') )
array(
'id' => array(
'name' => 'id',
'type' => 'xsd:int'
),
'name' => array(
'name' => 'name',
'type' => 'xsd:string'
)
)
);
$server->wsdl->addComplexType(
// name
'StuffsArray',
// typeClass (complexType|simpleType|attribute)
'complexType',
// phpType: currently supported are array and struct (php assoc array)
'array',
// compositor (all|sequence|choice)
'',
// restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
'SOAP-ENC:Array',
// elements = array ( name = array(name=>'',type=>'') )
array(),
// attrs
array(
array(
'ref' => 'SOAP-ENC:arrayType',
'wsdl:arrayType' => 'tns:Stuffs[]'
)
),
// arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
'tns:Stuffs'
);
$server->register(
// string $name the name of the PHP function, class.method or class..method
'getStuffs',
// array $in assoc array of input values: key = param name, value = param type
array(
'user' => 'xsd:string',
'pass' => 'xsd:string'
),
// array $out assoc array of output values: key = param name, value = param type
array(
'return' => 'tns:StuffsArray'
),
// mixed $namespace the element namespace for the method or false
'urn:' . $myNamespace,
// mixed $soapaction the soapaction for the method or false
'urn:' . $myNamespace . "#getStuffs",
// mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
'rpc',
// mixed $use optional (encoded|literal) or false
'encoded',
// string $documentation optional Description to include in WSDL
'Fetch array of Stuffs ("id", "name").' // documentation
);
#$server->wsdl->schemaTargetNamespace = $myNamespace;
$server->service(isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '');
exit();
?>
In a C# Console Application, after adding a Web Reference called "StuffService" with the "?wsdl" appended to the appropriate URL where this PHP-file can be found, this code works, I can perfectly query the stuff_array values like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WebServiceTest
{
class Program
{
static void Main(string[] args)
{
StuffService.MyStuffService myService = new StuffService.MyStuffService();
StuffService.Stuffs[] stuffs = myService.getStuffs("someone", "1234");
foreach (var stuff in stuffs)
{
Console.WriteLine(stuff.id+".: "+stuff.name);
}
Console.WriteLine();
Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}
That's cool, BUT I'd like to develop this code to give back an object like this:
class ResponseObject {
public $responseCode = 0;
public $responseMessage = '';
public $stuffArray = NULL;
}
$responseObject = NULL;
function getStuffs( $user='', $pass='' ) {
global $responseObject;
$responseObject = new ResponseObject();
// check stuffs in a simple way now
if($user != 'someone' && $pass != '1234'){
$responseObject->responseCode = 2;
$responseObject->responseMessage = 'Authentication failed';
return $responseObject;
}
$responseObject->stuffArray = array();
$responseObject->stuffArray[] = array( 'id'=>122, 'name'=>'One stuff');
$responseObject->stuffArray[] = array( 'id'=>213, 'name'=>'Another stuff');
$responseObject->stuffArray[] = array( 'id'=>435, 'name'=>'Whatever stuff');
$responseObject->stuffArray[] = array( 'id'=>65, 'name'=>'Cool Stuff');
$responseObject->stuffArray[] = array( 'id'=>92, 'name'=>'Wow, what a stuff');
$responseObject->responseCode = 1;
$responseObject->responseMessage = 'Successful!';
return $responseObject;
}
What's the appropriate NuSOAP code for that?
Thanks!! :)
I hope I could clarify what I would like to achieve: returning a struct which contains an int, a string and an array of structs, but don't know how to write the appropriate NuSOAP-code for that. This way I could firstly check the responseCode, and handle it with the appropriate error messages OR outputting the stuffArray, etc.
After long hours of experimentation, I found the solution!
So giving back a structure containing three members - an int responseCode, a string responseMessage and an array of structs called stuffArray in the example - via SOAP with NuSOAP (PHP) looks like this below, I put some comments in the code to make it more unambiguous:
<?php
class ResponseObject {
public $responseCode = 0;
public $responseMessage = 'Unknown error!';
public $stuffArray = NULL;
}
/**
* @return object
*/
function getStuffs( $user='', $pass='' ) {
$responseObject = new ResponseObject();
// check stuffs in a simple way now
if( !($user == 'someone' and $pass == '1234') ){
$responseObject->responseCode = 2;
$responseObject->responseMessage = 'Authentication failed!';
return $responseObject;
}
$responseObject->stuffArray = array();
$responseObject->stuffArray[] = array( 'id'=>122, 'name'=>'One stuff');
$responseObject->stuffArray[] = array( 'id'=>213, 'name'=>'Another stuff');
$responseObject->stuffArray[] = array( 'id'=>435, 'name'=>'Whatever stuff');
$responseObject->stuffArray[] = array( 'id'=>65, 'name'=>'Cool Stuff');
$responseObject->stuffArray[] = array( 'id'=>92, 'name'=>'Wow, what a stuff');
$responseObject->responseCode = 1;
$responseObject->responseMessage = 'Successful!';
return $responseObject;
}
require_once 'nusoap/lib/nusoap.php';
$server = new soap_server;
// $myNamespace = $_SERVER['SCRIPT_URI'];
$myNamespace = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];
$server->configureWSDL(
// string $serviceName, name of the service
'MyStuffService',
// mixed $namespace optional 'tns' service namespace or false
// 'urn:' . $myNamespace
$myNamespace
);
// $server->wsdl->schemaTargetNamespace = 'http://soapinterop.org/xsd/';
$server->wsdl->schemaTargetNamespace = $myNamespace;
$server->wsdl->addComplexType(
// name
'Stuffs',
// typeClass (complexType|simpleType|attribute)
'complexType',
// phpType: currently supported are array and struct (php assoc array)
'struct',
// compositor (all|sequence|choice)
'all',
// restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
'',
// elements = array ( name = array(name=>'',type=>'') )
array(
'id' => array(
'name' => 'id',
'type' => 'xsd:int'
),
'name' => array(
'name' => 'name',
'type' => 'xsd:string'
)
)
);
$server->wsdl->addComplexType(
// name
'StuffsArray',
// typeClass (complexType|simpleType|attribute)
'complexType',
// phpType: currently supported are array and struct (php assoc array)
'array',
// compositor (all|sequence|choice)
'',
// restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
'SOAP-ENC:Array',
// elements = array ( name = array(name=>'',type=>'') )
array(),
// attrs
array(
array(
'ref' => 'SOAP-ENC:arrayType',
'wsdl:arrayType' => 'tns:Stuffs[]'
)
),
// arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
'tns:Stuffs'
);
$server->wsdl->addComplexType(
// name
'ResponseObject',
// typeClass (complexType|simpleType|attribute)
'complexType',
// phpType: currently supported are array and struct (php assoc array)
'struct',
// compositor (all|sequence|choice)
'all',
// restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
'',
// elements = array ( name = array(name=>'',type=>'') )
array
(
'responseCode' => array( 'type' => 'xsd:int'),
'responseMessage' => array( 'type' => 'xsd:string'),
'stuffArray' => array( 'type' => 'tns:StuffsArray'
// DON'T UNCOMMENT THE FOLLOWING COMMENTED LINES, BECAUSE THIS WAY IT DOESN'T WORK!!! - Left it in the code not to forget it....
// ,
// 'minOccurs' => '0',
// 'maxOccurs' => 'unbounded'
)
)
);
$server->register(
// string $name the name of the PHP function, class.method or class..method
'getStuffs',
// array $in assoc array of input values: key = param name, value = param type
array(
'user' => 'xsd:string',
'pass' => 'xsd:string'
),
// array $out assoc array of output values: key = param name, value = param type
array(
'return' => 'tns:ResponseObject'
),
// mixed $namespace the element namespace for the method or false
// 'urn:' . $myNamespace,
$myNamespace,
// mixed $soapaction the soapaction for the method or false
// 'urn:' . $myNamespace . "#getStuffs",
$myNamespace . "#getStuffs",
// mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
'rpc',
// mixed $use optional (encoded|literal) or false
'encoded',
// string $documentation optional Description to include in WSDL
'Fetch array of Stuffs ("id", "name").' // documentation
);
// $server->wsdl->schemaTargetNamespace = $myNamespace;
// function def.: nusoap/lib/class.soap_server.php (236)
$server->service(isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '');
// DON'T UNCOMMENT THE FOLLOWING LINES!! - Don't call these headers explicitly,
// everything will be handled in function service() appropriately - I know it by experience that it's not a good choice...
// output:wsdl
// header('Content-Type: text/xml;charset=utf-8');
// header('Content-Type: text/xml');
// echo $server->wsdl->serialize();
exit(0);
Give this file a name, for example getStuffComplex.php
, and then copy this file somewhere on your webserver, and remember its path.
For example one domain name on my local webserver is http://soap.local
, and the above mentioned PHP-code can be reached at http://soap.local/getStuffComplex.php
.
Let's say you want to call getStuffs() function in a C# code via a SOAP client, from a Console Application under Visual Studio 2010. In this case you have to do the following steps:
http://soap.local/getStuffComplex.php?wsdl
The C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Services.Protocols;
namespace WebServiceTestComplex
{
class Program
{
static void Main(string[] args)
{
try
{
StuffServiceComplex.MyStuffService myService = new StuffServiceComplex.MyStuffService();
StuffServiceComplex.ResponseObject myRespObject = myService.getStuffs("someone", "1234");
switch (myRespObject.responseCode)
{
// Everything was OK, results can be output
case 1:
Console.WriteLine("Everything's OK, let's write the results to the standard output:");
foreach (var stuff in myRespObject.stuffArray)
{
Console.WriteLine("\t"+stuff.id + ".:\t" + stuff.name);
}
break;
// Authentication failed
case 2:
// Unknown error
case 0:
default:
Console.WriteLine("Error:");
Console.WriteLine("\tError code: "+myRespObject.responseCode);
Console.WriteLine("\tError message: " + myRespObject.responseMessage);
break;
}
}
catch (SoapException e)
{
Console.WriteLine("=== SOAP EXCEPTION!! ===");
Console.WriteLine(e);
}
catch (Exception e)
{
Console.WriteLine("=== OTHER EXCEPTION!! ===");
Console.WriteLine(e.ToString());
}
Console.WriteLine();
Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}
The output:
Everything's OK, let's write the results to the standard output:
122.: One stuff
213.: Another stuff
435.: Whatever stuff
65.: Cool Stuff
92.: Wow, what a stuff
Press a key...
I hope this helps someone who struggled with bringing PHP and SOAP and .NET together.
(Note: take care about character coding when using accents or any special letters. By default, ANSI could be used (but character encodings have to be the same).)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With