Powerful Web Services with PHP and SOAP

You've tried your hand at building mashups, experimented with a few RESTful Web services, maybe even started your own. Sure, you've got data sharing working. But how do you make your Web applications really talk to each other?
In this tutorial, I'll show you how to take your Web applications to the next level with SOAP, the "powerhouse of Web services", building high-end Web services with pure PHP.
SOAP 101
SOAP is a protocol for applications and servers to communicate with each other. Its primary use in PHP is exposing Web services and building Web service clients. As a protocol, it can exchange messages over HTTP/HTTPS, which helps it get around firewalls that would block other protocols (eg, Remote Procedure Call). As an application layer protocol, SOAP is also entirely platform independent — your PHP Web application can talk to a Java database server, for example, as long as they both speak SOAP.
SOAP messages are simply XML with some custom namespaces, so they're fully machine-readable. Libraries are available for every major language, and working with SOAP Web services is quick, easy and fast.
SOAP for PHP
Today we're going to take a look at one of these libraries, NuSOAP. PHP has a few options for SOAP, including a PHP 5 extension, a PEAR package, and an independent (but very popular) library called NuSOAP. NuSOAP is the simplest way to get up and running with SOAP, but we could just as well have used PEAR::SOAP or the extension, and all three are interoperable — you can consume PEAR::SOAP exposed services with NuSOAP and vice versa, and scripts using either can happily run alongside each other.
Getting started with NuSOAP
To begin using NuSOAP, first head to the project page and download a copy of libraries — I'm using version 0.7.2. All examples should be forwards-compatible, but API changes happen, so check your library version if you encounter any errors. Drop the contents of the archive into a folder on your Web server — using /soap under my docroot. The latest version is compatible with the SOAP extension, but if you experience "class already declared" errors just disable the SOAP extension or rename the class in nusoap.php.
Your first SOAP request!
We'll start with the SOAP client. SOAP works with servers and clients; servers expose services and clients consume them. We'll start with a demo of a simple Web service that takes an argument and returns an array — but with the power of SOAP, we get that array data locally, almost as if the client was the server, and SOAP takes care of all the information in between.
Fire up your favourite text editor and type out the following:
call("hello_world", array("name" => "Josh"));
print_r($output);
Save it as hello_world.php in the folder you created earlier. It can be anywhere, as long as the lib/nusoap.php reference still points to your NuSOAP library. Do the same for the server:
register("hello_world");
function hello_world($name)
{
    return array("data"=>"Hello World, {$name}!");
}
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : "";
$srv->service($HTTP_RAW_POST_DATA);
Save this as hello_world_server.php in the same folder. If it can't be accessed at that URL in the client script (localhost/soap/...), change the reference in the client code as needed.
Then load up your Web browser, point it to the SOAP client — eg, http://localhost/soap/hello_world.php — and run the script. You should see the following:
Array
(
    [data] => Hello World, Josh!
)
That's perfectly normal print_r output — precisely what you would expect if you returned the array within the same PHP script. Except that our server script is separate, could be on a different server and could be running on a totally different platform — SOAP helps gets data from the server to the client as smoothly as possible.
The Server
Let's examine the server for a moment. Here's the code we used to build our server:
register("hello_world");
function hello_world($name)
{
    return array("data"=>"Hello World, {$name}!");
}
In this example, we first load up the NuSOAP library and register the service we want to expose, naming it "hello_world". We then define this service as a standard PHP function. At the moment, it does nothing but return a simple associative array, with the value containing an argument, $name. The client provides this argument from a totally separate PHP script and SOAP provides the glue to make sure it is passed in when the function is called.
HTTP is stateless, and our PHP script will be executed from the top down whenever a request is made, so each SOAP call (or other script execution) will be a new request. To check if we have a SOAP call (and what the SOAP client wants us to do), we have to scan each request for data. SOAP clients send POST requests, with XML data in the message body of the request, so we can fetch this raw POST data and pass it to the service() method of the soap_server class. Raw POST data is available in $HTTP_RAW_POST_DATA, but PHP won't set this unless it has a value, so we use a small hack to ensure it exists before passing it on to the SOAP server.
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$srv->service($HTTP_RAW_POST_DATA);
We quickly get together the raw POST data, pass it to the SOAP library, and away we go.
The Client
The client uses the NuSOAP library, but only because we choose to — the server could use the PHP SOAP extension or PEAR::SOAP, and could be hosted anywhere. With the power of SOAP, we're going to take the PHP function on the server and talk to it through SOAP; we're also going to receive the result just like any other variable within our script. Have a look at the code for the client:
call("hello_world", array("name" => "Josh"));
print_r($output);
The client is very basic — we first load up the library, then establish a connection to the SOAP server at the URL we've provided and call the "hello_world" service. For testing, we'll print_r the output. The second argument to the $soap->call() method is an array of parameters to be passed to the service. Notice we specify 'name' as the array key, the same as the $name argument on the server's function — this is not necessary as we aren't working with complex pre-defined services, however, it does hold importance when consuming slightly more elaborate SOAP services.
The service call returns a value which we then put into $output. If we check that print_r output earlier, it showed we had a perfectly good PHP associative array — [data] => Hello World, Josh! — just as our server's hello_world() function should have returned. In just a few lines of code, we've linked together two independent PHP scripts. Now your Web apps are really talking.
Behind the scenes: debugging SOAP
While you make high-level calls to the SOAP libraries, the NuSOAP library is actually busy generating and parsing XML request messages and passing them back and forth between server and client. You can easily examine the message body of your request and the server's response on the client side, using the request and response properties of the SOAP client class. These are invaluable in debugging, and will help you get a better understanding of SOAP internals, although you may never need it...
call("hello_world", array("name" => "Josh"));
print_r($output);
echo '
'.htmlspecialchars($soap->request).'
'; echo '
'.htmlspecialchars($soap->response).'
';
This request will output something like the following:
POST /soap/hello_world_server.php HTTP/1.0
Host: 127.0.0.1
User-Agent: NuSOAP/0.7.2 (1.94)
Content-Type: text/xml; charset=ISO-8859-1
SOAPAction: ""
Content-Length: 511

Notice we're making a direct POST request and passing all the XML through. In a HTML form request, that XML would be replaced with key=value&key=value pairs, which are then translated into elements of $_POST — this is why we have to request the raw POST data to check for a SOAP request. The actual XML schema for SOAP messages isn't important, as the libraries take care of it for us, but read up on the SOAP specifications if you want to take a closer look.
Further SOAP
Now that you've built your first SOAP client, experiment with more complex APIs, or try consuming one of the many SOAP-based APIs available. If your applications already receive data from other sources, check if they offer SOAP — its use is very prevalent in enterprise situations, especially where SOA is encouraged. Finally, if you want to learn more about NuSOAP, one of the authors provides some handy resources.

Share this

Related Posts

Previous
Next Post »