xmlrpc_server

The implementation of this class has been kept as simple to use as possible. The constructor for the server basically does all the work. Here's a minimal example:


  function foo ($xmlrpcmsg) {
    ...
    return new 
xmlrpcresp($some_xmlrpc_val);
  }

  class 
bar {
    function 
foobar($xmlrpcmsg) {
      ...
      return new 
xmlrpcresp($some_xmlrpc_val);
    }
  }

  
$s = new xmlrpc_server(
    array(
      
"examples.myFunc1" => array("function" => "foo"),
      
"examples.myFunc2" => array("function" => "bar::foobar"),
    ));

This performs everything you need to do with a server. The single constructor argument is an associative array from xmlrpc method names to php function names. The incoming request is parsed and dispatched to the relevant php function, which is responsible for returning a xmlrpcresp object, that will be serialized back to the caller.

Method handler functions

Both php functions and class methods can be registered as xmlrpc method handlers.

The synopsis of a method handler function is:

xmlrpcresp $resp = function (xmlrpcmsg $msg)

No text should be echoed 'to screen' by the handler function, or it will break the xml response sent back to the client. This applies also to error and warning messages that PHP prints to screen unless the appropriate parameters have been set in the php.in file. Another way to prevent echoing of errors inside the response and facilitate debugging is to use the server SetDebug method with debug level 3 (see below).

Note that if you implement a method with a name prefixed by system. the handler function will be invoked by the server with two parameters, the first being the server itself and the second being the xmlrpcmsg object.

The same php function can be registered as handler of multiple xmlrpc methods.

Here is a more detailed example of what the handler function foo may do:


  function foo ($xmlrpcmsg) {
    global 
$xmlrpcerruser// import user errcode base value

    
$meth $xmlrpcmsg->method(); // retrieve method name
    
$par $xmlrpcmsg->getParam(0); // retrieve value of first parameter - assumes at least one param received
    
$val $par->scalarval(); // decode value of first parameter - assumes it is a scalar value

    
...

    if (
$err) {
      
// this is an error condition
      
return new xmlrpcresp(0$xmlrpcerruser+1// user error 1
        
"There's a problem, Captain");
    } else {
      
// this is a successful value being returned
      
return new xmlrpcresp(new xmlrpcval("All's fine!""string"));
    }
  }

See server.php in this distribution for more examples of how to do this.

Since release 2.0RC3 there is a new, even simpler way of registering php functions with the server. See section 5.7 below

The dispatch map

The first argument to the xmlrpc_server constructor is an array, called the dispatch map. In this array is the information the server needs to service the XML-RPC methods you define.

The dispatch map takes the form of an associative array of associative arrays: the outer array has one entry for each method, the key being the method name. The corresponding value is another associative array, which can have the following members:

  • function - this entry is mandatory. It must be either a name of a function in the global scope which services the XML-RPC method, or an array containing an instance of an object and a static method name (for static class methods the 'class::method' syntax is also supported).

  • signature - this entry is an array containing the possible signatures (see Signatures) for the method. If this entry is present then the server will check that the correct number and type of parameters have been sent for this method before dispatching it.

  • docstring - this entry is a string containing documentation for the method. The documentation may contain HTML markup.

  • signature_docs - this entry can be used to provide documentation for the single parameters. It must match in structure the 'signature' member. By default, only the documenting_xmlrpc_server class in the extras package will take advantage of this, since the "system.methodHelp" protocol does not support documenting method parameters individually.

Look at the server.php example in the distribution to see what a dispatch map looks like.

Method signatures

A signature is a description of a method's return type and its parameter types. A method may have more than one signature.

Within a server's dispatch map, each method has an array of possible signatures. Each signature is an array of types. The first entry is the return type. For instance, the method


<?php string examples.getStateName(int)

has the signature


<?php array($xmlrpcString$xmlrpcInt)

and, assuming that it is the only possible signature for the method, it might be used like this in server creation:


$findstate_sig = array(array($xmlrpcString$xmlrpcInt));

$findstate_doc 'When passed an integer between 1 and 51 returns the
name of a US state, where the integer is the index of that state name
in an alphabetic order.'
;

$s = new xmlrpc_server( array(
  
"examples.getStateName" => array(
    
"function" => "findstate",
    
"signature" => $findstate_sig,
    
"docstring" => $findstate_doc
  
)));

Note that method signatures do not allow to check nested parameters, e.g. the number, names and types of the members of a struct param cannot be validated.

If a method that you want to expose has a definite number of parameters, but each of those parameters could reasonably be of multiple types, the array of acceptable signatures will easily grow into a combinatorial explosion. To avoid such a situation, the lib defines the global var $xmlrpcValue, which can be used in method signatures as a placeholder for 'any xmlrpc type':


$echoback_sig = array(array($xmlrpcValue$xmlrpcValue));

$findstate_doc 'Echoes back to the client the received value, regardless of its type';

$s = new xmlrpc_server( array(
  
"echoBack" => array(
    
"function" => "echoback",
    
"signature" => $echoback_sig// this sig guarantees that the method handler will be called with one and only one parameter
    
"docstring" => $echoback_doc
  
)));

Methods system.listMethods, system.methodHelp, system.methodSignature and system.multicall are already defined by the server, and should not be reimplemented (see Reserved Methods below).

Delaying the server response

You may want to construct the server, but for some reason not fulfill the request immediately (security verification, for instance). If you omit to pass to the constructor the dispatch map or pass it a second argument of 0 this will have the desired effect. You can then use the service() method of the server class to service the request. For example:


$s = new xmlrpc_server($myDispMap0); // second parameter = 0 prevents automatic servicing of request

// ... some code that does other stuff here

$s->service();

Note that the service method will print the complete result payload to screen and send appropriate HTTP headers back to the client, but also return the response object. This permits further manipulation of the response.

To prevent the server from sending HTTP headers back to the client, you can pass a second parameter with a value of TRUE to the service method. In this case, the response payload will be returned instead of the response object.

Xmlrpc requests retrieved by other means than HTTP POST bodies can also be processed. For example:


$s = new xmlrpc_server(); // not passing a dispatch map prevents automatic servicing of request

// ... some code that does other stuff here, including setting dispatch map into server object

$resp $s->service($xmlrpc_request_bodytrue); // parse a variable instead of POST body, retrieve response payload

// ... some code that does other stuff with xml response $resp here

Modifying the server behaviour

A couple of methods / class variables are available to modify the behaviour of the server. The only way to take advantage of their existence is by usage of a delayed server response (see above)

setDebug()

This function controls weather the server is going to echo debugging messages back to the client as comments in response body. Valid values: 0,1,2,3, with 1 being the default. At level 0, no debug info is returned to the client. At level 2, the complete client request is added to the response, as part of the xml comments. At level 3, a new PHP error handler is set when executing user functions exposed as server methods, and all non-fatal errors are trapped and added as comments into the response.

allow_system_funcs

Default_value: TRUE. When set to FALSE, disables support for System.xxx functions in the server. It might be useful e.g. if you do not wish the server to respond to requests to System.ListMethods.

compress_response

When set to TRUE, enables the server to take advantage of HTTP compression, otherwise disables it. Responses will be transparently compressed, but only when an xmlrpc-client declares its support for compression in the HTTP headers of the request.

Note that the ZLIB php extension must be installed for this to work. If it is, compress_response will default to TRUE.

response_charset_encoding

Charset encoding to be used for response (only affects string values).

If it can, the server will convert the generated response from internal_encoding to the intended one.

Valid values are: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled), null (leave charset unspecified in response and convert output stream to US_ASCII), 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed), or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).

Fault reporting

Fault codes for your servers should start at the value indicated by the global $xmlrpcerruser + 1.

Standard errors returned by the server include:

1 Unknown method

Returned if the server was asked to dispatch a method it didn't know about

2 Invalid return payload

This error is actually generated by the client, not server, code, but signifies that a server returned something it couldn't understand. A more detailed error report is sometimes added onto the end of the phrase above.

3 Incorrect parameters

This error is generated when the server has signature(s) defined for a method, and the parameters passed by the client do not match any of signatures.

4 Can't introspect: method unknown

This error is generated by the builtin system.* methods when any kind of introspection is attempted on a method undefined by the server.

5 Didn't receive 200 OK from remote server

This error is generated by the client when a remote server doesn't return HTTP/1.1 200 OK in response to a request. A more detailed error report is added onto the end of the phrase above.

6 No data received from server

This error is generated by the client when a remote server returns HTTP/1.1 200 OK in response to a request, but no response body follows the HTTP headers.

7 No SSL support compiled in

This error is generated by the client when trying to send a request with HTTPS and the CURL extension is not available to PHP.

8 CURL error

This error is generated by the client when trying to send a request with HTTPS and the HTTPS communication fails.

9-14 multicall errors

These errors are generated by the server when something fails inside a system.multicall request.

100- XML parse errors

Returns 100 plus the XML parser error code for the fault that occurred. The faultString returned explains where the parse error was in the incoming XML stream.

'New style' servers

In the same spirit of simplification that inspired the xmlrpc_client::return_type class variable, a new class variable has been added to the server class: functions_parameters_type. When set to 'phpvals', the functions registered in the server dispatch map will be called with plain php values as parameters, instead of a single xmlrpcmsg instance parameter. The return value of those functions is expected to be a plain php value, too. An example is worth a thousand words:


  function foo($usr_id$out_lang='en') {
    global 
$xmlrpcerruser;

    ...

    if (
$someErrorCondition)
      return new 
xmlrpcresp(0$xmlrpcerruser+1'DOH!');
    else
      return array(
        
'name' => 'Joe',
        
'age' => 27,
        
'picture' => new xmlrpcval(file_get_contents($picOfTheGuy), 'base64')
      );
  }

  
$s = new xmlrpc_server(
    array(
      
"examples.myFunc" => array(
        
"function" => "bar::foobar",
        
"signature" => array(
          array(
$xmlrpcString$xmlrpcInt),
          array(
$xmlrpcString$xmlrpcInt$xmlrpcString)
        )
      )
    ), 
false);
  
$s->functions_parameters_type 'phpvals';
  
$s->service();

There are a few things to keep in mind when using this simplified syntax:

to return an xmlrpc error, the method handler function must return an instance of xmlrpcresp. There is no other way for the server to know when an error response should be served to the client;

to return a base64 value, the method handler function must encode it on its own, creating an instance of an xmlrpcval object;

the method handler function cannot determine the name of the xmlrpc method it is serving, unlike standard handler functions that can retrieve it from the message object;

when receiving nested parameters, the method handler function has no way to distinguish a php string that was sent as base64 value from one that was sent as a string value;

this has a direct consequence on the support of system.multicall: a method whose signature contains datetime or base64 values will not be available to multicall calls;

last but not least, the direct parsing of xml to php values is much faster than using xmlrpcvals, and allows the library to handle much bigger messages without allocating all available server memory or smashing PHP recursive call stack.