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.
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
...). Exceptions thrown duting execution of handler functions are
caught by default and a XML-RPC error reponse is generated instead.
This behaviour can be finetuned by usage of the
exception_handling
member variable (see
...).
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 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:
- 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).function
- 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.signature
- this
entry is a string containing documentation for the method. The
documentation may contain HTML markup.docstring
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.
parameters_type
- this entry can be used
when the server is working in 'xmlrpcvals' mode (see ...) to
define one or more entries in the dispatch map as being functions
that follow the 'phpvals' calling convention. The only useful
value is currently the string phpvals
.
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
<?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).
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($myDispMap, 0); // 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, possibly in combination
with output buffering.
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_body, true); // parse a variable instead of POST body, retrieve response payload
// ... some code that does other stuff with xml response $resp here
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.
This variable controls the behaviour of the server when an exception is thrown by a method handler php function. Valid values: 0,1,2, with 0 being the default. At level 0, the server catches the exception and return an 'internal error' xmlrpc response; at 1 it catches the exceptions and return an xmlrpc response with the error code and error message corresponding to the exception that was thron; at 2 = the exception is floated to the upper layers in the code
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
. The only
other way for the server to know when an error response should be
served to the client is to throw an exception and set the server's
exception_handling
memeber var to 1;
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.