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($somexmlrpcval); } class bar { function foobar($xmlrpcmsg) { ... return new xmlrpcresp($somexmlrpcval); } } $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 method names to 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. ù
Both php functions and static 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.
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 value $meth = $xmlrpcmsg->method(); // retrieve method name $par = $xmlrpcmsg->getParam(0); // decode value of first parameter $val = $par->scalarval(); ... 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 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.
Look at the server.php
example in the distribution to see what a dispatch map looks like.
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
string examples.getStateName(int)
has the signature
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 and type 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 reimplemented (see Reserved Methods below).
You may want to construct the server, but for some reason not fulfill the request immediately (security verification, for instance). If you pass the constructor 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($myDispMap, 0); // ... some code that does other stuff here $s->service();
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)
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.
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
.
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.
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 codes for your servers should start at the value indicated by the global $xmlrpcerruser
+ 1.
Standard errors returned by the server include:
1
Unknown methodReturned if the server was asked to dispatch a method it didn't know about
2
Invalid return payloadThis 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 parametersThis 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 unknownThis 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 serverThis 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 serverThis 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 inThis 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 errorThis error is generated by the client when trying to send a request with HTTPS and the HTTPS communication fails.
9-14
multicall errorsThese errors are generated by the server when something fails inside a system.multicall request.
100-
XML parse errorsReturns 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.
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.