Programming with NuSOAP Part 2

NuSOAP is a group of PHP classes that allow developers to create and consume SOAP web services. It does not require any special PHP extensions. The current stable version (0.6.3) of NuSOAP at the time these samples written (23-April-2003), supports much of the SOAP 1.1 specification. It can generate WSDL 1.1 and also consume it for use in serialization. Both rpc/encoded and document/literal services are supported. However, it must be noted that NuSOAP does not provide coverage of the SOAP 1.1 and WSDL 1.1 that is as complete as some other implementations, such as .NET and Apache Axis.

This document follows up Introduction to NuSOAP and Programming with NuSOAP with additional samples that demonstrate how to use SOAP Arrays and Structs when using NuSOAP to create and consume SOAP web services.

SOAP Arrays
SOAP Structs
Resources

SOAP Arrays

Showing no imagination whatsoever, I used the ubiquitous "Hello, World" example in Introduction to NuSOAP and Programming with NuSOAP. I will show the use of arrays by changing that sample to say hello to not just one person, but many.

SOAP arrays are numerically-indexed (non-associative), similar to those in many programming languages such as C and FORTRAN. Therefore, our service can access elements of the array using numeric indices, rather than associative keys.

<?php
// Pull in the NuSOAP code
require_once('nusoap.php');
// Create the server instance
$server = new soap_server;
// Register the method to expose
// Note: with NuSOAP 0.6.3, only method name is used w/o WSDL
$server->register(
    'hello'                            // method name
);
// Define the method as a PHP function
function hello($names) {
    for ($i = 0; $i < count($names); $i++) {
        $retval[$i] = 'Hello, ' . $names[$i];
    }
    
    return $retval;
}
// Use the request to (try to) invoke the service
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>

The client changes only in that it passes a parameter that is an array of names, rather than a single scalar name.

<?php
// Pull in the NuSOAP code
require_once('nusoap.php');
// Create the client instance
$client = new soapclient('http://localhost/phphack/helloworld5.php');
// Check for an error
$err = $client->getError();
if ($err) {
    // Display the error
    echo '<p><b>Constructor error: ' . $err . '</b></p>';
    // At this point, you know the call that follows will fail
}
// Call the SOAP method
$names = array('Scott', 'Albert', 'Robert', 'Phyllis');
$result = $client->call(
    'hello',                       // method name
    array('names' => $names)    // input parameters
);
// Check for a fault
if ($client->fault) {
    echo '<p><b>Fault: ';
    print_r($result);
    echo '</b></p>';
} else {
    // Check for errors
    $err = $client->getError();
    if ($err) {
        // Display the error
        echo '<p><b>Error: ' . $err . '</b></p>';
    } else {
        // Display the result
        print_r($result);
    }
}
?>

The request and response look like the following.

POST /phphack/helloworld5.php HTTP/1.0
User-Agent: NuSOAP/0.6.3
Host: localhost:80
Content-Type: text/xml; charset="ISO-8859-1"
Content-Length: 736
SOAPAction: ""

<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:si="http://soapinterop.org/xsd">
  <SOAP-ENV:Body>
    <ns1:hello xmlns:ns1="http://testuri.org">
      <names xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:string[4]">
        <item xsi:type="xsd:string">Scott</item>
        <item xsi:type="xsd:string">Albert</item>
        <item xsi:type="xsd:string">Robert</item>
        <item xsi:type="xsd:string">Phyllis</item>
      </names>
    </ns1:hello>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Thu, 29 May 2003 18:46:12 GMT
X-Powered-By: PHP/4.0.6
Server: NuSOAP Server v0.6.3
Connection: Close
Content-Type: text/xml; charset=UTF-8
Content-Length: 743

<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:si="http://soapinterop.org/xsd">
  <SOAP-ENV:Body>
    <helloResponse>
      <soapVal xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:string[4]">
        <item xsi:type="xsd:string">Hello, Scott</item>
        <item xsi:type="xsd:string">Hello, Albert</item>
        <item xsi:type="xsd:string">Hello, Robert</item>
        <item xsi:type="xsd:string">Hello, Phyllis</item>
      </soapVal>
    </helloResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Return to top.

SOAP Structs

To this point, the examples that have been developed have used scalar types and arrays of scalars for parameters and return values. In this section, I will show the use of SOAP Structs, which more-or-less correspond to complex types in XML Schema. The example is yet another variation of Hello, World, this time with a structure for a parameter that provides more information about the person being greeted.

NuSOAP leverages the capabilities of PHP, using associative arrays to represent SOAP Structs. For this example, we will use an array containing the first name, age and gender for a person. In PHP, this will look like

$person = array(
             'firstname' => 'Betty',
             'age' => 32,
             'gender' => 'female'
          );

The service accepts a parameter that is an associative array as shown above. It returns another array like the following.

$return = array(
             'greeting' => 'Hello...',
             'winner' => false
          );

The code for the service follows.

<?php
// Pull in the NuSOAP code
require_once('nusoap.php');
// Create the server instance
$server = new soap_server;
// Register the method to expose
$server->register(
    'hello'                            // method name
);
// Define the method as a PHP function
function hello($person) {
    $greeting = 'Hello, ' . $person['firstname'] .
                '. It is nice to meet a ' . $person['age'] .
                ' year old ' . $person['gender'] . '.';
    
    $winner = $person['firstname'] == 'Scott';

    $retval = array(
                'greeting' => $greeting,
                'winner' => $winner
                );

    return new soapval('return', 'ContestInfo', $retval, false, 'urn:MyURN');
}
// Use the request to (try to) invoke the service
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>

One thing to notice is that the service method returns a soapval, so that the XML data type, in this case urn:MyURN:ContestInfo, can be specified for the return value.

Here is a client for this.

<?php
// Pull in the NuSOAP code
require_once('nusoap.php');
// Create the client instance
$client = new soapclient('http://localhost/phphack/helloworld6.php');
// Check for an error
$err = $client->getError();
if ($err) {
    // Display the error
    echo '<p><b>Constructor error: ' . $err . '</b></p>';
    // At this point, you know the call that follows will fail
}
// Call the SOAP method
$person = array('firstname' => 'Willi', 'age' => 22, 'gender' => 'male');
$result = $client->call(
    'hello',                    // method name
    array('person' => new soapval('person', 'Person',
                                  $person, false, 'urn:MyURN'))    // input parameters
);
// Check for a fault
if ($client->fault) {
    echo '<p><b>Fault: ';
    print_r($result);
    echo '</b></p>';
} else {
    // Check for errors
    $err = $client->getError();
    if ($err) {
        // Display the error
        echo '<p><b>Error: ' . $err . '</b></p>';
    } else {
        // Display the result
        print_r($result);
    }
}
?>

Like the server, this uses a soapval for a parameter, so that the XML data type of urn:MyURN:Person can be specified.

The request and response look like the following.

POST /phphack/helloworld6.php HTTP/1.0
User-Agent: NuSOAP/0.6.3
Host: localhost:80
Content-Type: text/xml; charset="ISO-8859-1"
Content-Length: 688
SOAPAction: ""

<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:si="http://soapinterop.org/xsd">
  <SOAP-ENV:Body>
    <ns1:hello xmlns:ns1="http://testuri.org">
      <person xmlns:ns6678="urn:MyURN" xsi:type="ns6678:Person">
        <firstname xsi:type="xsd:string">Willi</firstname>
        <age xsi:type="xsd:int">22</age>
        <gender xsi:type="xsd:string">male</gender>
      </person>
    </ns1:hello>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Thu, 29 May 2003 19:50:30 GMT
X-Powered-By: PHP/4.0.6
Server: NuSOAP Server v0.6.3
Connection: Close
Content-Type: text/xml; charset=UTF-8
Content-Length: 679

<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:si="http://soapinterop.org/xsd">
  <SOAP-ENV:Body>
    <helloResponse>
      <return xmlns:ns7437="urn:MyURN"
       xsi:type="ns7437:ContestInfo">
        <greeting xsi:type="xsd:string">
          Hello, Willi. It is nice to meet a 22 year old male.
        </greeting>
        <winner xsi:type="xsd:boolean">0</winner>
      </return>
    </helloResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This example has shown how to send a SOAP Struct as a parameter, and return one as a result. It has also shown how to specify the XML type of each Struct. Without any additional facilities, interoperability of the SOAP client and server using these types might require communication between parties in advance to agree upon these structures. This is one of the great freedoms of the original SOAP 1.1 specification, but it causes interoperability problems. The next document in this series will show how to use WSDL to provide metadata for a Web service, including the data structures in use.

You can download the source for these examples as well.

Return to top.

Resources

Join the NuSOAP mailing list to learn more and ask questions.
The home of the NuSOAP project.
NuSOAP home of Dietrich Ayala, the author of NuSOAP.

Return to top.

Copyright © 2003 Scott Nichol.
29-May-2003