Note: this module has been replaced by the new ar\connect\soap library.

MOD_SOAP: Sending SOAP Requests

SOAP stands for Simple Object Access Protocol and is a Remote Procedure Call (RPC) protocol using XML. Don't confuse it with XML-RPC, which looks similar but is much less complicated. SOAP is what Microsoft uses in its .Net framework and being Microsoft, it is anything but simple.

A RPC protocol allows you to call a method or function on a remote computer, through a network. You send a request, complete with parameters and a result is sent back in reply. It is in fact not very different from a HTTP GET or POST request with query parameters, except that how you send it is strictly defined and not intended for human readability.

Both SOAP and XML-RPC do in fact use HTTP or HTTPS as the transport layer of the protocol, thus making it easy to pass through properly configured firewalls. It is just that the data portion of the request and reply are strictly specified XML.

Fortunately you usually don't have to understand the full XML specification for SOAP to be able to use it. If you simply want to use it to 'talk' between two different Ariadne servers, you won't ever have to look at the exact XML that is sent between them, it will simply work. For SOAP servers based on other systems, specifically non-PHP based, things may get a bit more complicated though.

Ariadne's SOAP Client and Simple SOAP Requests

The PHP PEAR community has built a helpfull SOAP library, called SOAPClient. We've built a wrapper module in Ariadne, so you can use this in your PINP templates. (You will need to have the PEAR SOAP library installed on your server). This module can be loaded and used as follows:

<pinp>
load('mod_soap.php');
$client = SOAP::Client(http://www.example.com/soapService);
$result = $client->call('exampleSoapMethod', array(
'argument1' => 'value1',
'argument2' => 1
) );
</pinp>

And thats it. Well, usually. The PEAR Soap library will translate the call and arguments to the correct XML syntax, and translate the result back to something PHP understands. But that only works if the SOAP service only uses standard types. You can use almost any normal PHP type, like arrays, strings, integers, as is. The problem starts when you need objects or associative arrays (hashes). In that case, you'll need to pass a few hints to the Soap library, so it will understand how to translate these parameters.

Let's call an imaginary service, which requires an associative array of say, colors. The colors are defined as follows:

  $colors = array(
   'red' => '#FF0000',
   'green' => '#00FF00',
   'blue' => '#0000FF'
);

Now we let the SOAP library translate this to the correct SOAP value:

  $soapColors = SOAP::Value('colors', 'Struct', $colors);

And we send the arguments just like before:

  $result = $client->call('exampleSoapMethod', array(
   'colors' => $soapColors
) );

Valid types for SOAP::Value are

Array Simple arrays with numbers as keys
Struct associative arrays and objects
int integer and long
boolean   boolean
float float
string string

The PEAR Soap Library does a reasonable good job of detecting the type of the parameters and correctly setting the relevant SOAP type, so usually you don't need to be so explicit. Things change however when you want to connect to a .Net SOAP Service, or a similar system that uses things like WSDL or namespaces.

Complex SOAP Requests: WSDL and namespaces

Some SOAP Services define their own data types and provide descriptions of them through WSDL. Usually such systems also provide a more human readable description. Microsofts' .Net framework for example delivers a human readable explanation of a Soap Service, including examples, when the URL is called through a webbrowser. Here is an example request from such a page:

POST /Notify_ws/Notifyws.asmx HTTP/1.1
Host: www.example.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "urn:Notifyws/searchNotice"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <searchNotice xmlns="urn:Notifyws">
      <SearchValue1>string</SearchValue1>
      <SearchValue2>string</SearchValue2>
    </searchNotice>
  </soap:Body>
</soap:Envelope>

For a complex request like this, the PEAR Soap Library needs a lot more help to produce something like this. First of all it uses an XML namespace, as specified in the <searchNotice> tag, 'Notifyws'. It then uses named parameters from that namespace. The PEAR Soap library has some problems figuring that out, so we need to help it a bit. We'll start by telling it that the parameters we'll pass to the service, have a namespace, like this:

  $search1 = SOAP::Value('{urn:Notifyws}SearchValue1', 'string', 'searchtext');
  $search2 = SOAP::Value('{urn:Notifyws}SearchValue2', 'string',
'moresearchtext');

But that is not enough, the searchNotice method, which the above piece of SOAP Requests says should exist, is also part of the Notifyws namespace. If you don't tell the SOAP library, it won't add the namespace and the SOAP service won't understand what you are trying to do. So, call the SOAP Service like this:

 $result = $client->call('{urn:Notifyws}searchNotice', array($search1, $search2), 
  '80', array('soapaction' => 'urn:Notifyws/searchNotice')
 );

This call specifically tells the SOAP library to add a namespace to the method searchNotice. The arguments have been prepared in the two lines before, so they also use the correct namespace. The last thing is to make sure the SOAPAction header is sent correctly. This is done through the last parameter of the call method. (The '80' in the argument list is the port number of the service, in case the default, '80', is not correct.)

In addition to the soapaction parameter, there are a number of other parameters you can add, which tell the client to do some more interesting stuff:

soapaction The SOAPAction header to send
user The username for Basic Authentication
pass The password for Basic Authentication
timeout The connection timeout (default is 4 seconds)
proxy_host The proxy to use
proxy_user A user for Proxy-Authentication
proxy_pass A password for Proxy-Authentication
headers An associative array with extra headers, name => value

If you want to know more about SOAP, start at http://www.soapware.org/.

Error handling

If an error occurs, the PEAR SOAP library uses the PEAR Error module to return an error object, similar to the mod_error module of Ariadne. It is fairly easy to check for errors, and also necessary:

if ($client->isError($result)) {
  // something went wrong:
  echo $client->errorMessage($result);
} else {
  // all is well
}

If something is going wrong and you want to know what the SOAP XML envelope sent or received looks like, you can find all of that in the $client->wire variable. The simplest way is usually to just do:

var_dump($client->wire);

And all the information you need will be in there.

Ariadne's SOAP Server

Ariadne not only has a SOAP Client, it also provides a SOAP Server enabling you to deploy your own SOAP Services. To enable the SOAP Server, you will need to copy the file loader.php in the ariadne/soap/ directory to ariadne/www/soap.php

Now your Ariadne has a SOAP Service, which is accessible as http://www.yoursite.com/ariadne/soap.php Unfortunately, it doesn't do much yet. Just like the normal loader, it needs a path to an object and a template to call. The template follows from the method name that the client wants to call. To prevent just any template from being called through SOAP, the SOAP loader prefixes the template with "soap."

So all you need to do now, is create a template with a name like soap.my_soap_method

You will need to remember though, that this template is called as a method, as in RPC. So don't echo or print or otherwise output anything, just return it. The SOAP loader will make sure the returned value is sent back to the SOAP Client. You can use SOAP::Value just like the client to return complex SOAP envelopes, if you really want to.

If you want to return an error for any reason, just set an error message in
$this->error in the object that was called. The SOAP Server will create a SOAP Fault, which the Client should be able to parse.