Introduction

SOAP or "Simple Object Access Protocol" has built his concept on XML-RPC, but provides much richer features sets. In this article we are going to build a simple SOAP server and client which will use our new service.
You should know what SOAP is (SOAP packet structure), but purpose of this article is to show programmers how easy is to build web services (SOAP). Even if you don't know XML, SOAP, WSDL, the only requirement is basic XML knowledge. But if you're interested, you can find some nice tutorials on www.w3schools.com (XML,SOAP), www.xml.com, www.w3.org/TR/SOAP.

PHP and SOAP

PHP (for now) doesn't have a SOAP extension, but there are some nice PHP SOAP toolkits. You can even use XML-RPC PHP extension, where you can, by using optional argument ("version"), specify which protocol to use ("soap 1.1.". However, you need to take care of data transmitting across the network, where PHP toolkit takes care of that.

NuSOAP Toolkit

NuSOAP (formerly SOAPx4) is a toolkit which provides simple API for building web services using SOAP. There are also some other toolkits like PEAR:SOAP (also stable). NuSOAP current version is 0.6.4, which provides simple API for making SOAP server/client applications and also supports features like WSDL generation, building proxy class, using SSL, using HTTP proxy, HTTP authentication. So download current NuSOAP toolkit from cvs.sourceforge.net/viewcvs.py/nusoap/lib/ and lets get started.

The Story

Let's assume the existence of a government department which performs weather measurements. They want their data to be publicly accessible. So basically let's assume that they have for each city (worldwide) a current degrees measurements and a basic weather forecast.
First we create a simple MySQL table (current_data):
  CREATE TABLE current_data (
    id int(10) unsigned NOT NULL auto_increment,
    city varchar(20) default NULL,
    degrees float NOT NULL default '0',
    forecast varchar(255) default NULL,
    PRIMARY KEY  (id),
    UNIQUE KEY city (city),
    KEY city_2 (city)
  ) TYPE=MyISAM;
And let's fill it with some test data:
  INSERT INTO current_data VALUES (1, 'Chicago', '5',  'Partly cloudy.');
  INSERT INTO current_data VALUES (2, 'London',  '15', 
  	'Sun along with patchy clouds.');

SOAP Server

Our server is going to create the web service. We will register one method (getWeather) which will accept a string (city) and return an array with degrees measurement and weather forecast.
First we need NuSOAP toolkit (nusoap.php), which we'll be in our "inc" directory so that we can easily include it. So let's look at the actually needed code:

<?php
    
/**************************************************************
    *  Description:
    *  Creates a simple SOAP Server (server.php).
    **************************************************************/
        
    // includes nusoap classes
    
require('inc/nusoap.php');
   
    
// create server
    
$l_oServer = new soap_server();
  
    
// wsdl generation
    
$l_oServer->debug_flag=false;
    
$l_oServer->configureWSDL('Weather''http://weather.org/Weather');
    
$l_oServer->wsdl->schemaTargetNamespace 'http://weather.org/Weather';

    
// add complex type
    
$l_oServer->wsdl->addComplexType(
        
'WeatherData',
        
'complexType',
        
'struct',
        
'all',
        
'',
        array(
            
'degrees' => array('name'=>'degrees''type'=>'xsd:string'), 
            
'forecast' => array('name'=>'forecast''type'=>'xsd:string'))
    );
  
    
// register method
    
$l_oServer->register('getWeather', array(
        
'city' => 'xsd:string'), 
            array(
'return'=>'tns:WeatherData'), 
                
'http://weather.org/Weather');
  
    
// method code (get DB result)
    
function getWeather ($a_stInput) {
        if (
is_string($a_stInput)) {   
            
$l_oDBlink   = @mysql_connect(
                
'localhost''someone''something');
            
$l_oDBresult = @mysql_db_query(
                
'weather'
                
'SELECT degrees, forecast FROM current_data WHERE city = LCASE("' mysql_escape_string((string)$a_stInput) . '") LIMIT 1');
          
            
// simple error checking
            
if (!$l_oDBresult) {
                return new 
soap_fault('Server''''Internal server error.');
            }
     
            
// no data avaible for x city
            
if (!mysql_num_rows($l_oDBresult)) {
                return new 
soap_fault('Server'''
                    
'Service contains data only for a few cities.');
            }
            
mysql_close($l_oDBlink);
          
            
// return data
            
return mysql_fetch_array($l_oDBresultMYSQL_ASSOC);    
        } 
        
// we accept only a string
        
else {
          return new 
soap_fault('Client''''Service requires a string parameter.');
        }
      }
  
    
// pass incoming (posted) data
    
$l_oServer->service($HTTP_RAW_POST_DATA);
?>
So let's go line by line. First, we obviously include NuSOAP toolkit. For a server we need to create "soap_server" object. We don't need debug messages, so we turn them off.
At line 15 we start to generate WSDL file. WSDL is "Web Services Description Language", which is a XML-based language. This file basically describe web service and let us know how to use web service (access it). For a simple example we don't need to generate WSDL file, but I'm also going to show you how you can use WSDL file to create "proxy class". We configure target namespace and create (simple) complex type, which is our returned data. It's a struct with two data rows, degree and forecast.
And finally we register "getWeather" method, so it can be called by a client, and we also define our complexType (WeatherData) for return value. What follows is the actual "getWeather" function (line 32). First we check if argument is a string and return "soap_fault" if it's not (line 53). Our string parameter (city) is used in SQL query, where we hopefully get some data. In case of error, or if there is no data accessible, we also return "soap_fault". If we got some data from the database, we return it like associated array (our complexType).
And on line 58 we pass to our service incoming data ($HTTP_RAW_POST_DATA). By the way, $HTTP_RAW_POST_DATA is only set if type of the data is unknown. So that's our web service, ready to be used. There are some WSDL stuffs that I also want to mention, but we will cover that at the end.

SOAP Client

In our client we're going to call webmethod "getWeather", with wanted city for input parameter (string). And if we get a valid response we'll output received data (degree, forecast). The code:

<?php    
    
/**************************************************************
    *  Description:
    *  Creates a simple SOAP Client (client.php).
    **************************************************************/
    
    // use form data
    
if ((string)$_GET['action'] == 'get_data') {
        
// includes nusoap classes
        
require('inc/nusoap.php');

        
// set parameters and create client
        
$l_aParam   = array((string)$_POST['city']);
        
$l_oClient  = new soapclient('http://somewhere.org/soap/server.php');
    
        
// call a webmethod (getWeather)
        
$l_stResult $l_oClient->call('getWeather'$l_aParam);
    
        
// check for errors
        
if (!$l_oClient->getError()) {
          
// print results
          
print '<h1>Current data for: '    $l_aParam[0
              . 
':</h1><ul><li>DEGREES: '   $l_stResult['degrees'
              . 
'&deg;C</li><li>FORECAST: ' $l_stResult['forecast'
              . 
'</li></ul>'
        }
        
// print error description
        
else {
          echo 
'<h1>Error: ' $l_oClient->getError() . '</h1>';
        }
    }

    
// output search form
    
print '
        <form name="input" action="'
.$_SERVER['PHP_SELF'].'?action=get_data"  method="POST">
        Your city: <input type="text" name="city">
        <input type="submit" value="Search">
        </form>
    '
;
?>
For getting input parameter we output a simple form (city is a text input). If some data was sent, we then include NuSOAP toolkit. First we put string parameter into array, so that we can use it for calling webmethod. For a client we create object "soapclient" (argument is URL to the server) at line 14. With created client we can call our webmetod "getWeather" and we pass the parameter (form data in array).
And that's all. Finally we only need to check for errors (line 20) and if there're no errors we simple output data (lines 22-25). We have just created a simple SOAP client, with just a few line of code.

SOAP Client Using WSDL

With the above example we have created a simple SOAP client. We can also use WSDL to create proxy class, which will hold method (getWeather) of our web service. So basically the programmer only needs to know webmethods name and required parameters, and all this can be retrieved from WSDL. We can save WSDL file localy and use it. So let�s make a SOAP client using WSDL (look at server code to see how we generated WSDL).

<?php    
     
/**************************************************************
     *  Description:
     *  Creates a simple SOAP Client using WSDL (client_wsdl.php).
     **************************************************************/
   
     // use form data
     
if ((string)$_GET['action'] == 'get_data') {
   
         
// includes nusoap classes
         
require('inc/nusoap.php');
  
         
// set parameters and create client
        
$l_oClient = new soapclient(
            
'http://somewhere.org/soap/weather.wsdl''wsdl');
        
$l_oProxy  $l_oClient->getProxy();
        
        
// call a webmethod (getWeather)
        
$l_stResult $l_oProxy->getWeather((string)$_POST['city']);
      
        
// check for errors
        
if (!$l_oClient->getError()) {
          
// print results  
          
print '<h1>>Current data for: '  . (string)$_POST['city'
              . 
':</h1><ul><li>DEGREES: '   $l_stResult['degrees'
              . 
'&deg;C</li><li>FORECAST: ' $l_stResult['forecast'
              . 
'</li></ul>';
          
        }
        
// print error description
        
else {
          echo 
'<h1>Napaka: ' $l_oClient->getError() . '</h1>';
        }
    }
  
    
// output search fclientorm
    
print '
        <form name="input" action="'
.$_SERVER['PHP_SELF'].'?action=get_data"  method="POST">
        Your city: <input type="text" name="city">
        <input type="submit" value="Search">
        </form>
    '
;
?>
So where's the difference? When we're creating "soapclient" object we now pass URL to the WSDL file (second argument tells the client that we are using WSDL). Next we generate proxy class (line 15); we simply call "getProxy" method and that's all.
Now we have in our proxy class all the methods that our web service has. So we can "locally" access "getWeather" method, which sends and returns data from web service. Lastly, we do the same error checking just like in previous client.
I have told you that you can get all the needed data from WSDL (webmethod name and required parameter). You can take a look at WSDL file, which is accessed from http://somewhere.org/soap/server.php?wsdl (use parameter wsdl), but if you go to http://somewhere.org/soap/server.php you will get nice GUI which tells us accessible methods and required parameters. This GUI and WSDL is created by NuSOAP toolkit, it's accessible because we generated WSDL in the server.

Conclusion

This was just a simple example of using SOAP with NuSOAP toolkit. You could build the same system using XML-RPC or WDDX but SOAP has some other "goodies" like advance error checking, XSD validation and other cool stuff. You can also just use web service (there are some good web services resources like www.xmethods.com). So dig into SOAP and NuSOAP API documentation.
Happy messaging!
You can download the full source code