Turbo-charging xmlrpc: from AJAX to infinity...

The dreaded situation
Suppose you are an old school xmlrpc guru, grumpy, lone and unshaven.
You have been coding webservices years before the acronym "SOA" was conjured in the deranged mind of an Enterprise IT Consultant.
You are pretty happy with the introspection capabilities offered by System.ListMethods and its sibling methods, and everybody else is pleased with the speed with which you can whip up real enterprise integration (you have possibly coded some auto-discovery service on top of that, and automated generators of code stubs, too, just to show the world that Axis is nothing special, really).

Then one day the new kid on the block comes along: a lazy, sloppy, ignorant newbie (just like you have been years ago, in fact).
He knows next to nothing about webservices and could not tell an xml from a cvs file for the sake of his life.
But he's sold to management the magic word: AJAX. And you are there to help make the new application happen, integrating it with the existing webservices. Ready for yesterday.

(Note: the following tutorial assumes familiarity with xmlrpc and web services concepts, and experience in the usage of the php-xmlrpc library).
Primary goals are:
  1. Documenting all the existing web service APIs (i.e. available xmlrpc methods) in a human-readable form
  2. Having all existing xml-rpc methods accessible directly by a browser, with no xml performace penalty (i.e. using json syntax)
Luckily, you have chosen the PHP-XMLRPC library as basis for your implementations, and you know the task is within your reach.
Step 1: upgrade (or install) the lib
Head to the http://sourceforge.net/projects/phpxmlrpc website and grab the latest version of the code: Once downloaded, you will need to make the following files available to your web app: xmlrpc.inc, xmlrpcs.inc, jsonrpc.inc, jsonrpcs.inc, docxmlrpcs.inc
(dumping them in a directory that is part of your php include_path is usually ok.
The docxmlrpcs.css file should be copied to the same directory where the server.php file is.

In case this is the first time you install php-xmlrpc, it is now a good time to read the manual and play around.
Before continuing you should have set up a working xmlrpc server with at least one webservice exposed.
Step 2: add html docs to all existing methods
Suppose the server.php page that is servicing xmlrpc method calls looks like this:
<?php
include('xmlrpc.inc');
include('xmlrpcs.inc');

$methods = array(
  'module1.dothis' => array(
    'function' => 'ConvertIntToString',
    'signature' => array(array($xmlrpcString, $xmlrpcInt)),
    'docstring" => 'Converts an integer value to its string representation'
  ),
  'module1.dothat' => array(
    // ...
  )
);

$server = new xmlrpc_server($methods);
?>
Listing 1 By changing two lines of code, the server can be made to generate HTML docs describing the methods it exposes:
<?php
include('xmlrpc.inc');
include('xmlrpcs.inc');
include('docxmlrpcs.inc);

$methods = array(
  'module1.dothis' => array(
    'function' => 'ConvertIntToString',
    'signature' => array(array($xmlrpcString, $xmlrpcInt)),
    'docstring" => 'Converts an integer value to its string representation'
  ),
  'module1.dothat' => array(
    // ...
  )
);

$server = new documenting_xmlrpc_server($methods);
?>
Listing 2 Now go and try it out, using a plain browser to access the server page instead of the xmlrpc debugger!
In case you cannot afford to do it, here's a page built just like that.

What is happening behind the scenes is that the documenting_xmlrpc_server server object differentiates between a POST request, such as xmlrpc requests are, and a GET request. If the latter is detected, instead of returning an xmlrpc response with some error message, the html-equivalent of system.listmethods is generated and fed back to the user. Support for documenting the single xmlrpc method is also provided, as well as a test-by-web-form area for quick'n'dirty tests.

That was easy, wasn't it?
Step 3: add better support for browser clients: a completely new protocol (json-rpc)
With the recent AJAX craze, the JSON protocol is gaining steady support. If you have not yet read about it, go do it now.
Json has a couple of advantages compared to xmlrpc and soap: Unlike xmlrpc, json makes no assumptions about http methods or call conventions, and is easily used to build custom web service applications.
The extension to the xmlrpc lib that you have just downloaded includes support for a json 'dialect' called json-rpc, which can be summarized as: 'the closest you will ever get to xmlrpc, without the xml'.
To JSON-RPC-enable your webservices, make the following modifications to Listing 1:
<?php
include('xmlrpc.inc');
include('xmlrpc.incs');
include('jsonrpc.inc');
include('jsonrpcs.inc');

$methods = array(
  'module1.dothis' => array(
    'function' => 'ConvertIntToString',
    'signature' => array(array($xmlrpcString, $xmlrpcInt)),
    'docstring" => 'Converts an integer value to its string representation'
  ),
  'module1.dothat' => array(
    // ...
  )
);

$server = new jsonrpc_server($methods);
?>
Listing 3 Now, fire up your xmlrpc debugger and try to fetch the list of methods out of the new server, enabling debug info. If your new web page is accessible from the net, you can use the public xmlrpc debugger on sf.net to test it. Do not be disappointed if you see an error reported: it is in fact the correct response ;) The server now expects to receive a json-rpc call, and will respond with a json-rpc error response if the call is not valid.
What you should see is something similar to this:
---GOT---
HTTP/1.1 200 OK
Date: Wed, 26 Apr 2006 09:22:07 GMT
Server: Apache/2.0.55 (Win32) DAV/2 mod_auth_sspi/1.0.3 SVN/1.3.0 mod_python/3.2.8 Python/2.3.5 PHP/5.1.2
X-Powered-By: PHP/5.1.2
Vary: Accept-Charset
Content-Length: 105
Connection: close
Content-Type: text/plain
{
"id": null, "error": { "Code": 15, "String": "Invalid request payload Invalid data" }, "result": null
}
---END---
HEADER: date: Wed, 26 Apr 2006 09:22:07 GMT
HEADER: server: Apache/2.0.55 (Win32) DAV/2 mod_auth_sspi/1.0.3 SVN/1.3.0 mod_python/3.2.8 Python/2.3.5 PHP/5.1.2
HEADER: x-powered-by: PHP/5.1.2
HEADER: vary: Accept-Charset
HEADER: content-length: 105
HEADER: connection: close
HEADER: content-type: text/plain
XML error at line 1, check URL
I highligted the interesting bits in the communication: the payload returned by the server, in json-rpc format, and the error reported by the debugger, which expects to receive well-formed xml and gets javascript instead.

Note that in 99% of the cases, you will not need to modify the php code servicing the requests: functions that expect an xmlrpcmsg object as input and return an xmlrpcresp object will be handled just fine by the jsonrpc server (with the exception that base64 and datetime are not valid data types in json, and should not be used in this case).
Unfortunately, there is no json-rpc debugger available (yet) as part of the library, so you will have to use a real-world jsonrpc client to test the server.
Luckily, a new client class is available in the file jsonrpc.inc: jsonrpc_client. It supports the same APIs of the basic xmlrpc_client.

Once you are confident that the server responds correctly to json-rpc calls, move on to the last section:
Step 4: putting it all together
Wouldn't it be nice to have a single url (php page) to serve xmlrpc method calls, along with jsonrpc calls, and provide html documentation too?
Here's how to do it. It will take a couple more lines of code...
<?php
include('xmlrpc.inc');
include('xmlrpc.incs');

$methods = array(
  'module1.dothis' => array(
    'function' => 'ConvertIntToString',
    'signature' => array(array($xmlrpcString, $xmlrpcInt)),
    'docstring" => 'Converts an integer value to its string representation'
  ),
  'module1.dothat' => array(
    // ...
  )
);


if($_SERVER['REQUEST_METHOD'] != 'POST' || $_SERVER['CONTENT_TYPE'] == 'application/x-www-form-urlencoded')
{
  // on GET requests or requests generated by web forms, generate HTML docs
  include('docxmlrpcs.inc');
  $server = new documenting_xmlrpc_server($methods);
}
else
{
  // serve either jsonrpc or xmlrpc method calls: xmlrpc calls have a fixed
  // content-type of text/xml, while there is no consensus for jsonrpc...
  if strpos($_SERVER['CONTENT_TYPE'], 'text/xml') === 0)
    $server = new xmlrpc_server($methods);
  else
  {
    include('jsonrpc.inc');
    include('jsonrpcs.inc');
    $server = new jsonrpc_server($methods);
  }
}
?>
Listing 4 There you have it: a multi-protocol, self-documenting web service server.
Step 5: next steps
The json-rpc support provided by phpxmlrpc is rough at the edges, and will see some polish in the next release, so stay tuned.

Another tutorial might also follow up, where the process of building the json-rpc client object inside the web page will be detailed (but you can already find working examples in the ajax dir of the "extras" package).

Any feedback is welcome. To contact me, please use the Sourceforge.net account.

Happy coding.
Gaetano
Changes

2006/05/04: fixed code: class doc_xmlrpc_server is really documenting_xmlrpc_server