Finishing up the NuSOAP server

In the last post, the NuSOAP server introduced a new level of complexity, and had a multidimensional array, along with creating a more robust status message for the client to help determine the success or failure of the request. This post will examine the remaining functions in the NuSOAP server, and making the rest of the functions work, and more complex types including third and fourth level complexities along with a non-named (integer) array for the result.

We will be building on the code we used last time, so as a refresher, the code from the last post can be located at:
http://www.hirdweb.com/webservice/20100710_server.txt
http://www.hirdweb.com/webservice/20100710.txt

First off, here are the remaining functions to be exposed in the service:

  • showGroupItems – Shows what is needed based on a set event, all items are made up of course
  • showMadLib – Returns a mad lib based on passing in a number of different items
  • showNumbers – Returns a non associative array of numbers, requires an ID, and returns the numbers associated with that ID

Each of these webservices will require a little different structure.
showGroupItems will have a third level of complexity
showMadLib will require a long list of strings as the input
showNumbers will return an array of string elements, integer value for the element, instead of a named element

Make sure to save the file with a different name. I named it “20100720_server.php”. You will need to also update the WSDL call, otherwise it will still reference an older file name. Here is what I updated my location with:

$wsdl_addr = 'http://www.hirdweb.com/webservice/20100720_server.php';

Let’s first tackle the showGroupItems. The idea behind this is to show an example of a complex type for the returned result. We already have a complex type, but we need to make a returned result with a multidimensional array. The required input is:
id = a specific event name, in this case, we limit it to: movie, picnic, drive, shopping

For each of these events, we have a set of items that will go with it:
food – this is an array of items for food, drink and treat
equip – string containing a comma separated list of equipment needed
transport – string for the transport to/of the event
people – string of a comma separated list (or a single item) of people involved in the activity

First we need the list, so add this to the file named items.php that contains all the data for the services. We created this file in a previous post. (Just a note: in a “real world” application, these would likely be stored in a database and you would need to query the information you need in order to build the array).

$items = array(
	'movie' => array(
		'food'	=> array('food' => 'nachos with jalepenos and cheese', 'drink' => 'soda', 'treat' => 'candy'),
		'equip' => 'ticket, 3D glasses',
		'transport' => 'car',
		'people' => 'friend A, friend B, friend C',
	),
	'picnic' => array(
		'food'	=> array('food' => 'sandwiches', 'drink' => 'sports drinks', 'treat' => 'granola bars'),
		'equip' => 'basket, blanket',
		'transport' => 'van',
		'people' => 'Mom, Dad, brothers, sisters, cousins',
	),
	'drive' => array(
		'food'	=> array('food' => 'snacks', 'drink' => 'water', 'treat' => 'crackers'),
		'equip' => 'gloves, sunglasses',
		'transport' => 'sports car',
		'people' => 'myself, hot date',
	),
	'shopping' => array(
		'food'	=> array('food' => 'grapes', 'drink' => 'juice box', 'treat' => 'candybar'),
		'equip' => 'cart, list',
		'transport' => 'moving van',
	)
);

We will create a function with some very basic error checking, and be able to use our error function if there are errors.

function showGroupItems($id){
	require('items.php');	 
	// Make sure the id is lowercase
	$id = strtolower($id);	
	if ( $id == '' ){
		return error('empty');
	}	
	// make sure the item is in the array keys
	if ( !array_key_exists($id, $items) ){
		// return an error		
		return error("not_found");
	}	
	$final = $items[$id];
	
	$fin_data['status'] = array('status'=>'ok','message'=>'');
	$fin_data['group_items'] = $final;
	
	return $fin_data;
}

Now we need to tackle the registration of this function to the webservice. This is where we really need to think about what we need to return. First, we need to set the registration and the base complex type:

// ================================================
// === showGroupItems register
$in = array(
	'id' => 'xsd:string',
);
$out = array('return' => 'tns:showGroupItems');
$namespace = 'uri:hirdwebservice';
$soapaction = 'uri:hirdwebservice/showGroupItems';
$doc = 'Simplistic service to return an array of items for a specific event, the ones available are: movie, picnic, drive, shopping';
$server->register('showGroupItems', $in, $out, $namespace, $soapaction, 'rpc', 'encoded', $doc);
// ================================================

In this registration, we defined a “showGroupItems” type. So we need to create that to pull in the status type, and start building the showGroupItems result

// showGroupItems types ++++++++++++++++++++++++++++++++++++++++++
$server->wsdl->addComplexType('showGroupItems','complexType','struct','all','',
    array(
        'status' => array('name' => 'head', 'type' => 'tns:status_mssg'),
        'group_items' => array('name' => 'message', 'type' => 'tns:groupItems')
    )
);
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

In this one, we set the next level complex type to “groupItems”. In this type, we will split it into two parts. One will be for the string portions of the result, the other into the next level complex type. Add the following to the showGroupItems types code block.

$server->wsdl->addComplexType(
	'all_food','complexType','struct','all','',
	array(
		'food' => array('name' => 'food', 'type' => 'xsd:string', 'minOccurs' => "0"),				
		'drink' => array('name' => 'drink', 'type' => 'xsd:string', 'minOccurs' => "0"),
		'treat' => array('name' => 'treat', 'type' => 'xsd:string', 'minOccurs' => "0"),
	)
);


$server->wsdl->addComplexType(
	'groupItems','complexType','struct','all','',
	array(
		'food' => array('name' => 'food', 'type' => 'tns:all_food'),
		'equip' => array('name' => 'equip', 'type' => 'xsd:string', 'minOccurs' => "0"),
		'transport' => array('name' => 'transport', 'type' => 'xsd:string', 'minOccurs' => "0"),
		'people' => array('name' => 'people', 'type' => 'xsd:string', 'minOccurs' => "0"),
	)
);

This definition splits the returned result. The first element item in the array is defined by a separate complex type “all_food”. The rest are regular strings. We can update all of these to be arrays as well, and that is something to try on your own. The “minOccurs” definition tells the webservice that the item is optional and may not appear. Which is ok if it does not, then the service will ignore the empty space and go on. In our data list, the last item, “shopping” does not have a ‘people’ element. So by defining the minOccurs as 0, it will not be required to print out. Now add the following to the client:

$try4 = $proxy->showGroupItems('Picnic');
$try5 = $proxy->showGroupItems('Shopping');

And print those out you should receive something similar to the following:

showGroupItems
"Picnic"
Array
(
    [status] => 
    [group_items] => Array
        (
            [food] => Array
                (
                    [food] => sandwiches
                    [drink] => sports drinks
                    [treat] => granola bars
                )

            [equip] => basket, blanket
            [transport] => van
            [people] => Mom, Dad, brothers, sisters, cousins
        )
)
"Shopping"
Array
(
    [status] => 
    [group_items] => Array
        (
            [food] => Array
                (
                    [food] => grapes
                    [drink] => juice box
                    [treat] => candybar
                )

            [equip] => cart, list
            [transport] => moving van
        )
)

Now we are completed with this function, now on to the last two. These are pretty simple. I will give you the functions, and the registration for this one, and the complexTypes definitions should be easy by now, so I will skip posting this one. Since this one is a mad lib, there is nothing to add in, we can just insert the input elements into the string.

function showMadLib($adj1, $adj2, $adj3, $adj4, $adverb1, $adverb2, $noun1, $noun2, $noun3, $verb1, $verb_past1, $verb_past2 ){
	// Put all validation code here
	if ( ($adj1 == '')||($noun1 == '')||($verb_past1 == '')||($adverb1 == '')||($adj2 == '')||($noun2 == '')|| 
			($noun3 == '')||($adj3 == '')||($verb1 == '')||($adverb2 == '')||($verb_past2 == '')||($adj4 == '') ){
		return error("partial");
	}
	
	// example submission:
	// "disagreeable","dilapidated","evanescent","ubiquitous","obediently","painfully","monkey","spaceship","boogey man","answer","photographed", "knitted"
	
	$mad_lib = "Today I went to the zoo. I saw a $adj1 $noun1 jumping up and down in its tree. He $verb_past1 $adverb1 through the " . 
			"large tunnel that led to its $adj2 $noun2. I got some peanuts and passed them through the cage to a gigantic gray $noun3 " . 
			"towering above my head. Feeding that animal made me hungry. I went to get a $adj3 scoop of ice cream. It filled my stomach." .  
			"Afterwards I had to $verb1 $adverb2 to catch our bus. When I got home I $verb_past2 my mom for a $adj4 day at the zoo.";
	
	$fin_data['status'] = array('status'=>'ok','message'=>'');
	$fin_data['mad_lib'] = $mad_lib;
	
	return $fin_data; 
}

The function is in place, and as you can see, it takes a lot of different input. It returns only a string as the final message, so when you build the complexType, remember that. Here is the registration:

// ================================================
// === showMadLib register
$in = array(
	'adj1' => 'xsd:string',
	'adj2' => 'xsd:string',
	'adj3' => 'xsd:string',
	'adj4' => 'xsd:string',
	'adverb1' => 'xsd:string',
	'adverb2' => 'xsd:string',
	'noun1' => 'xsd:string',			
	'noun2' => 'xsd:string',
	'noun3' => 'xsd:string',
	'verb1' => 'xsd:string',
	'verb_past1' => 'xsd:string',
	'verb_past2' => 'xsd:string',
);
$out = array('return' => 'tns:showMadLib');
$namespace = 'uri:hirdwebservice';
$soapaction = 'uri:hirdwebservice/showMadLib';
$doc = 'Returns a mad lib based on the data it is sent. Need 4 Adjectives, 2 Adverbs, 3 Nouns, 1 Verb, and 2 Verb: Past Tense. It replies with a string that contains the madlib. ';
$server->register('showMadLib', $in, $out, $namespace, $soapaction, 'rpc', 'encoded', $doc);
// ================================================

Now after you build the complexType add the following to the client:

$try6 = $proxy->showMadLib("disagreeable","dilapidated","evanescent","ubiquitous","obediently","painfully","monkey","spaceship","boogey man","answer","photographed", "knitted");
$try7 = $proxy->showMadLib("hello","","","","","","",""," ","","", "");

And now you can test the function.

Lastly, we need to do a service that returns an array of data (numbers for this example) that has no associative type array. Add this to the “items.php” file:

$various = array (
	array(
		array('number' => "01"),
		array('number' => "02"),
		array('number' => "03"),
		array('number' => "04"),
		array('number' => "05"),
		array('number' => "06"),
		array('number' => "07"),
		array('number' => "08"),
		array('number' => "09"),
	),
	array(
		array('number' => "121654898"),
		array('number' => "11151"),
		array('number' => "18852"),
		array('number' => "1114885111111"),
		array('number' => "11111111111111111111"),
	),	
	array(
		array('number' => "2654846354684654"),
	),
	array(
		array('number' => "303030303030303030"),
		array('number' => "32323232323232323232323232"),
	),
	array(
		array('number' => "44444444"),
		array('number' => "41414141"),
		array('number' => "42424242"),
		array('number' => "43434343"),
		array('number' => "40404040"),
	),
	array(
		array('number' => "5"),
		array('number' => "10"),
		array('number' => "15"),
		array('number' => "20"),
		array('number' => "25"),
	),
);

As you can see, it is just a regular array with numbers and nothing else. Based on the number they send to the service, it will return the string of numbers. After planning, build out the function.

function showNumbers($id){
	require("items.php");
	
	// place all validation code here
	if ( ($id < 0) || ($id > 5)  ){
		// return an error
		return error("custom", "numbers", "You need to enter an ID that is 0, 1, 2, 3, 4, or 5.");
	}
	
	$numbers = $various[$id];
	
	$fin_data['head'] = array('status'=>'ok','message'=>'');
	$fin_data['numbers'] = $numbers;
	
	return $fin_data;
}

We only want numbers from 0 to 5, so if there is anything else, then return an error. Now we need to register the function.

// ================================================
// === showNumbers register
$in = array(
	'id' => 'xsd:string',
);
$out = array('return' => 'tns:showNumbers');
$namespace = 'uri:hirdwebservice';
$soapaction = 'uri:hirdwebservice/showNumbers';
$doc = 'Returns an array of numbers in a non-assoc array key format';
$server->register('showNumbers', $in, $out, $namespace, $soapaction, 'rpc', 'encoded', $doc);
// ================================================

This is basically the same, we need an ID, and we will return a formatted complexType named “showNumbers”. But now we need to actually format those. Since we are using a non associative array for the return, we need to format a new type. This is still a complexType, but it is not a struct, but an “array”. Start with the base complexType to build off of, then add the array type which references a regular struct complexType. This is done as follows:

// showNumbers types ++++++++++++++++++++++++++++++++++++++++++
$server->wsdl->addComplexType(
	'number','complexType','struct','all','',
	array(
		'number' => array('name' => 'number', 'type' => 'xsd:string'),		
	)
);

$server->wsdl->addComplexType(
	'numbers','complexType','array','',
	'SOAP-ENC:Array',
	array(),
	array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'tns:number[]')), 'tns:number'
);

$server->wsdl->addComplexType('showNumbers','complexType','struct','all','',
    array(
        'status' => array('name' => 'head', 'type' => 'tns:status_mssg'),
        'numbers' => array('name' => 'message', 'type' => 'tns:numbers')
    )
);
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

The new array type will have different items we need to declare. The first being is that it is an array not a struct, we do not want all, so will do “” instead of passing “all”. Next, we need to tell the class that we want a SOAP-ENC:Array. Since we are not defining a struct, the normal position where we would put our return data is now a blank array. Finally we need to build the array type, reference in the SOAP-ENC:arrayType. Add the wsdl def to it, then make sure you add the square brackets to the named array. Now build the regular struct we already know. Add the following to the client to test the service:

$try8 = $proxy->showNumbers(2);
$try9 = $proxy->showNumbers(5);

And you should get something similar to:

showNumbers
"2"
Array
(
    [status] => 
    [numbers] => Array
        (
            [0] => Array
                (
                    [number] => 2654846354684654
                )
        )
)
"5"
Array
(
    [status] => 
    [numbers] => Array
        (
            [0] => Array
                (
                    [number] => 5
                )
            [1] => Array
                (
                    [number] => 10
                )
            [2] => Array
                (
                    [number] => 15
                )
            [3] => Array
                (
                    [number] => 20
                )
            [4] => Array
                (
                    [number] => 25
                )
        )
)

and that will complete the server side of this. These are very simplistic examples, but can be used to build on for more applicable items. For example, You may have a database full of authors and the books they wrote, along with prices, ISBN numbers, publishers, and excerpts from the book. By using a combo of the previous examples, you can create a service that allows others to query the DB based on author, that will return each book, publisher, price with tax for the state they are requesting along with an excerpt of each book.

The server for this post is located at http://www.hirdweb.com/webservice/20100720_server.php
The client for this post is located at http://www.hirdweb.com/webservice/20100720.php

Code for this post is located at:
server: http://www.hirdweb.com/webservice/20100720_server.txt
client: http://www.hirdweb.com/webservice/20100720.txt
items: http://www.hirdweb.com/webservice/items.txt

Next we will test these in ASP.NET and figure out how to really use these.

One thought on “Finishing up the NuSOAP server”

Comments are closed.