Building on the NuSOAP Server

The NuSOAP server consists of a function that will take an ID and a name/phrase and inserts that into the Shakespeare quote. The function returns a semi-complex type which consists of 2 array elements, a status message and the phrase. However, the webservice needs to expand, add a new function, and make error messages dynamic. So the first thing we will tackle is getting the status message into a more dynamic format.

First, plan it out, always plan it out. The status message will need to accept an “error code”, a string for the struct name, and a variable for a custom message if it does fit in within the specified error codes. Now, just as a disclaimer here, this is just an example of how it can be planned. Some services may not need this extent, some may need a lot more. Remember that it needs to be able to handle the requirements of your application.

With that planned out, here is a possible solution to the function.

function error($err, $struct = 'phrase', $message = ''){
	$error['status']['status']='error';
	$error[$struct]= '';
	switch ($err) {
		case "empty":
			$error['status']['message']='There was nothing passed to the webservice, so nothing can be returned.';		
			break;
		case "partial":
			$error['status']['message']="Not all required parameters were passed, so the webservice can not return any information.";
			break;
		case "not_found":
			$error['status']['message']='The parameter you passed was not found, please try again.';
			break;
		case "bounds":
			$error['status']['message']='The ID that was passed is out of the allowable boundaires. Please try your request again.';
			break;
		case "less_than_zero":
			$error['status']['message']='The number recieved is less than zero, and will not work with this service. Please double check the number and try again.';
			break;
		case 'custom':
			$error['status']['message'] = $message;
			break;
		default:
			$error['status']['message'] = 'There was a problem with your request, please try again.';
			break;
	}
	return $error;	
}


In this example, I have created the basic array structure for the error, and defined a few different error codes. I am using strings for my codes, but it is possible to use integers. I am doing this mainly for readability. Each error has a specific message, and may be generic on all functions, and the best thing, this can be expanded if needed. The parameters for this function include the error code variable, setting the struct variable to use the showPhrases “phrase” structure, and setting message to null.

The codes are:
empty – used when parameters are not passed to the service
partial – when only some of the parameters are passed
not_found – the identifier was not matched to anything on the server side
bounds – the ID is outside of allowable number ranges
less_than_zero – the number is a negative number and can not be used
custom – self defined error with self defined message
default – when any of the codes do not match up, return a generic error message

After unit testing this function, and make sure the function does behave the way we need it to behave, we need to alter the function for showPhrases to allow this error functionality. The code we did before can be located at:
http://www.hirdweb.com/webservice/20100707_server.txt

We are going to modify the function for showPhrases now. Here is a possible alteration using the new error function. I added a couple more validation points as an example.

function showPhrases($id, $name){
	// Set up the phrases
	$phrases = array(
		'O NnnnnnnnnN, NnnnnnnnnN! wherefore art thou NnnnnnnnnN?',
		'But NnnnnnnnnN, If you prick us, do we not bleed? if you tickle us, do we not laugh? if you poison us, do we not die? and if you wrong us, shall we not revenge?',
		'Will all great NnnnnnnnnN\'s ocean wash this blood clean from my hand? No, this my hand will rather the multitudinous seas incarnadine, making the green one red',
		'Love looks not with the eyes, but with the mind, NnnnnnnnnN, and therefore is winged Cupid painted blind',
		'Men at some time are masters of their fates: The fault, dear NnnnnnnnnN, is not in our stars, but in ourselves, that we are underlings',
	);
	
	// check for empty parameters
	if ( ($name == '') || (!isset($name)) ) {
		return error("partial");
	}
	
	if ( $id == 100 ) {
		return error("not_found");
	}
	
	// place all validation code here
	if ( ($id < 0) || ($id > 4)  ){
		return error('bounds');
	}
	
	// Check the name for swear words
	if ( strtolower($name) == 'ass'){
		return error('custom', '', 'Please do not use drunken sailor language when using the webservice.');
	}	
	
	// replace all the needles with the name
	$phrase = preg_replace("/NnnnnnnnnN/", $name, $phrases[$id]);
	$fin_data['status'] = array('status'=>'ok','message'=>'');
	$fin_data['phrase'] = $phrase;

	return $fin_data;
}

In this function we have the phrases we will use, and then we start to do some validation. This is pretty basic and could probably be retooled to work a little better, but we are checking for the following:
empty parameters
id = 100 (to trigger a not found error for the example)
number range boundary violations
derogatory words (this one only including “ass”)

After it passes the checks, it does the normal process of building the response message structure. Only this time, we also need to change the status structure to:

$fin_data['status'] = array('status'=>'ok','message'=>'');

Before we can use this, though, we need to change the complexType structures in the $server object.

// Status Message Array ++++++++++++++++++++++++++++++++++++++++++
$server->wsdl->addComplexType('status_mssg','complexType','struct','all','',
    array(
    	'status' => array('name' => 'status', 'type' => 'xsd:string'),
    	'message' => array('name' => 'message', 'type' => 'xsd:string')
    )
);

// showPhrases types ++++++++++++++++++++++++++++++++++++++++++
$server->wsdl->addComplexType('showPhrases','complexType','struct','all','',
    array(
        'status' => array('name' => 'head', 'type' => 'tns:status_mssg'),
        'phrase' => array('name' => 'message', 'type' => 'xsd:string')
    )
);

We change the ‘status’ element in the showPhrases type from an xsd:string to a tns:struct-name, in this case, tns:status_mssg. The TNS name is important, because that tells the struct where to get the complex type definition. We added a new complex type. In this one, we defined two elements. Now when the response is given, it provides a multidimensional array. An example of the returned response with an error:

Array
(
    [status] => Array
        (
            [status] => error
            [message] => Not all required parameters were passed, so the webservice can not return any information.
        )

    [phrase] => 
)

You do not have to structure the status message this way, and you can keep it as a string, that is up to you. Or you can make it have even more elements, for example: the status, the message, the code to report it, url for documentation on the error, and company contact information. But by doing this, we have run through a basic complex type. If the response needs to have a multidimensional array, we know how to do this. Remember, as a good practice habit, the bottom of the WSDL is the basic, and build more complex types on top.

Now we can start on the taxes portion. I promise I will be less long winded on this one.

A function that will take a price (float type) and then calculate the tax and return it back with the original amount, tax amount and total amount with state name. I created a file, named items.php, that contains the arrays for the values of the taxes and the phrases. Here are the items for the taxes:

$tax_rates = array(
	'az'	=> '0.056',
	'al'	=> '0.04',
	'ak'	=> '0.05',
	'ca'	=> '0.0875',
	'or'	=> '0.00',
	'wa'	=> '0.065',
	'ut'	=> '0.047',
	'id'	=> '0.06',
	'wy'	=> '0.04',
);

$states = array(
	'az'	=> 'Arizona',
	'al'	=> 'Alabam',
	'ak'	=> 'Alaska',
	'ca'	=> 'California',
	'or'	=> 'Oregon',
	'wa'	=> 'Washington',
	'ut'	=> 'Utah',
	'id'	=> 'Idaho',
	'wy'	=> 'Wyoming',
);

Next we need to create the function. I am setting the default state to CA, so that if an empty string is passed, it will be set to CA automatically. Since we have already seen the error functionality in action, we can do this for brevity. I want to make sure the string is all lowercase, the price is greater than 0, and make sure that the state sent is in our array. So the following solution should be acceptable.

function showTaxes($price, $state = "ca"){
	require('items.php');
	
	// Make sure the state is lowercase
	$state = strtolower($state);
	
	// Check the array keys to validate
	$validate = array_keys($tax_rates);
	
	// place all validation code here
	if ( strlen($state) > 2 ){
		return error('custom', '', 'The state that was passed is not in the correct format. It should only be 2 characters');
	}
	
	if ( !in_array($state, $validate) ){
		// return an error
		return error("not_found");
	}
	
	if ( $price < 0 ){
		return error('less_than_zero');
	}
	
	if ( $price == '' ){
		return error('empty');
	}
	
	$tax = $tax_rates[$state] * $price;
	$final_price = $tax + $price;
	
	// set the array
	$final = array(
		'price' => $price,
		'tax'	=> money_format('%i',$tax),
		'total' => money_format('%i',$final_price),
		'state' => $states[$state],
	);
	
	$fin_data['status'] = array('status'=>'ok','message'=>'');
	$fin_data['taxes'] = $final;
	
	return  $fin_data;
}

The function validates the input, returns errors as needed. Again, I am sure there are more ways to validate, and more things to validate. If it passes all the validation, then it starts the final array formatting. In this array, there are four elements:
price
tax
total
state

So we need to create the same type of structure in our $server object.
First build the variables to register the function:

$in = array(
	'price' => 'xsd:string',
	'state' => 'xsd:string',
);
$out = array('return' => 'tns:showTaxes');
$namespace = 'uri:hirdwebservice';
$soapaction = 'uri:hirdwebservice/showTaxes';
$doc = 'Shows the taxes based on state taxes. Currently, the only states available in this are: AZ,AL,AK, CA, OR, WA, UT, ID, WY, Defaults to CA if no state is provided.';		
$server->register('showTaxes', $in, $out, $namespace, $soapaction, 'rpc', 'encoded', $doc); 

Now we need to build the structures. This will require 3 structures: the base complex type that splits the status and response, and the status structure, and the response structure. Since we already have the status structure in place, we need to worry about the response structure.

$server->wsdl->addComplexType('taxes','complexType','struct','all','',
	array(
		'price' => array('name' => 'price', 'type' => 'xsd:float', 'minOccurs' => "0"),
		'tax' => array('name' => 'tax', 'type' => 'xsd:float', 'minOccurs' => "0"),
		'total' => array('name' => 'total', 'type' => 'xsd:float', 'minOccurs' => "0"),
		'state' => array('name' => 'state', 'type' => 'xsd:string', 'minOccurs' => "0"),
	)
);

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

The base structure defines the status structure and sets the type the tns:status_mssg. The response sets the name to “taxes” and the type to tns:taxes. Now the name of the TNS structure can be whatever you really want to call it, but it must be the same as the complex type that is defined. For example, you can not call a complex type of “type1forme” and have the actual complex type added as ‘type_for_me’. I made this bold in the example above so you can see the connection between the two. The taxes structure has four elements, just like the function returns. When the response is given, it will show up as:

Array
(
    [status] => Array
        (
            [status] => ok
            [message] => 
        )

    [taxes] => Array
        (
            [price] => 12.18
            [tax] => 0.68
            [total] => 12.86
            [state] => Arizona
        )

)

I created a test client for this entry, and this is located at:
http://www.hirdweb.com/webservice/20100710.php

It shows the response for the examples with showPhrases (1 good call, 1 error), and showTaxes (2 good calls, 2 errors).

The next post will include the rest of the functions, with one of them adding a third level of complexity to the response, and completing the server.

The code for the server portion of this post: http://www.hirdweb.com/webservice/20100710_server.txt

The code for the client side of this post: http://www.hirdweb.com/webservice/20100710.txt

This Post Has 2 Comments

  1. Paul

    Wow this is a great resource.. I’m enjoying it.. good article

  2. Anon

    Terrific work! This is the type of information that should be shared around the web. Shame on the search engines for not positioning this post higher!

Leave a Reply