Automatic conversion of php functions into xmlrpc methods (and vice versa)

For the extremely lazy coder, helper functions have been added that allow to convert a php function into an xmlrpc method, and a remotely exposed xmlrpc method into a local php function - or a set of methods into a php class. Note that these comes with many caveat.

wrap_xmlrpc_method

stringwrap_xmlrpc_method($client,
 $methodname,
 $extra_options);
 
stringwrap_xmlrpc_method($client,
 $methodname,
 $signum,
 $timeout,
 $protocol,
 $funcname);
 

Given an xmlrpc server and a method name, creates a php wrapper function that will call the remote method and return results using native php types for both params and results. The generated php function will return an xmlrpcresp object for failed xmlrpc calls.

The second syntax is deprecated, and is listed here only for backward compatibility.

The server must support the system.methodSignature xmlrpc method call for this function to work.

The client param must be a valid xmlrpc_client object, previously created with the address of the target xmlrpc server, and to which the preferred communication options have been set.

The optional parameters can be passed as array key,value pairs in the extra_options param.

The signum optional param has the purpose of indicating which method signature to use, if the given server method has multiple signatures (defaults to 0).

The timeout and protocol optional params are the same as in the xmlrpc_client::send() method.

If set, the optional new_function_name parameter indicates which name should be used for the generated function. In case it is not set the function name will be auto-generated.

If the return_source optional parameter is set, the function will return the php source code to build the wrapper function, instead of evaluating it (useful to save the code and use it later as stand-alone xmlrpc client).

If the encode_php_objs optional parameter is set, instances of php objects later passed as parameters to the newly created function will receive a 'special' treatment that allows the server to rebuild them as php objects instead of simple arrays. Note that this entails using a "slightly augmented" version of the xmlrpc protocol (ie. using element attributes), which might not be understood by xmlrpc servers implemented using other libraries.

If the decode_php_objs optional parameter is set, instances of php objects that have been appropriately encoded by the server using a coordinate option will be deserialized as php objects instead of simple arrays (the same class definition should be present server side and client side).

Note that this might pose a security risk, since in order to rebuild the object instances their constructor method has to be invoked, and this means that the remote server can trigger execution of unforeseen php code on the client: not really a code injection, but almost. Please enable this option only when you trust the remote server.

In case of an error during generation of the wrapper function, FALSE is returned, otherwise the name (or source code) of the new function.

Known limitations: server must support system.methodsignature for the wanted xmlrpc method; for methods that expose multiple signatures, only one can be picked; for remote calls with nested xmlrpc params, the caller of the generated php function has to encode on its own the params passed to the php function if these are structs or arrays whose (sub)members include values of type base64.

Note: calling the generated php function 'might' be slow: a new xmlrpc client is created on every invocation and an xmlrpc-connection opened+closed. An extra 'debug' param is appended to the parameter list of the generated php function, useful for debugging purposes.

Example usage:


$c = new xmlrpc_client('http://phpxmlrpc.sourceforge.net/server.php');

$function wrap_xmlrpc_method($client'examples.getStateName');

if (!
$function)
  die(
'Cannot introspect remote method');
else {
  
$stateno 15;
  
$statename $function($a);
  if (
is_a($statename'xmlrpcresp')) // call failed
  
{
    echo 
'Call failed: '.$statename->faultCode().'. Calling again with debug on';
    
$function($atrue);
  }
  else
    echo 
"OK, state nr. $stateno is $statename";
}

wrap_php_function

arraywrap_php_function(string$funcname,
 string$wrapper_function_name,
 array$extra_options);
 

Given a user-defined PHP function, create a PHP 'wrapper' function that can be exposed as xmlrpc method from an xmlrpc_server object and called from remote clients, and return the appropriate definition to be added to a server's dispatch map.

The optional $wrapper_function_name specifies the name that will be used for the auto-generated function.

Since php is a typeless language, to infer types of input and output parameters, it relies on parsing the javadoc-style comment block associated with the given function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64) in the docblock @param tag is also allowed, if you need the php function to receive/send data in that particular format (note that base64 encoding/decoding is transparently carried out by the lib, while datetime vals are passed around as strings).

Known limitations: only works for user-defined functions, not for PHP internal functions (reflection does not support retrieving number/type of params for those); the wrapped php function will not be able to programmatically return an xmlrpc error response.

If the return_source optional parameter is set, the function will return the php source code to build the wrapper function, instead of evaluating it (useful to save the code and use it later in a stand-alone xmlrpc server). It will be in the stored in the source member of the returned array.

If the suppress_warnings optional parameter is set, any runtime warning generated while processing the user-defined php function will be catched and not be printed in the generated xml response.

If the extra_options array contains the encode_php_objs value, wrapped functions returning php objects will generate "special" xmlrpc responses: when the xmlrpc decoding of those responses is carried out by this same lib, using the appropriate param in php_xmlrpc_decode(), the objects will be rebuilt.

In short: php objects can be serialized, too (except for their resource members), using this function. Other libs might choke on the very same xml that will be generated in this case (i.e. it has a nonstandard attribute on struct element tags)

If the decode_php_objs optional parameter is set, instances of php objects that have been appropriately encoded by the client using a coordinate option will be deserialized and passed to the user function as php objects instead of simple arrays (the same class definition should be present server side and client side).

Note that this might pose a security risk, since in order to rebuild the object instances their constructor method has to be invoked, and this means that the remote client can trigger execution of unforeseen php code on the server: not really a code injection, but almost. Please enable this option only when you trust the remote clients.

Example usage:


<?php /**
* State name from state number decoder. NB: do NOT remove this comment block.
* @param integer $stateno the state number
* @return string the name of the state (or error description)
*/
function findstate($stateno)
{
  global 
$stateNames;
  if (isset(
$stateNames[$stateno-1]))
  {
    return 
$stateNames[$stateno-1];
  }
  else
  {
    return 
"I don't have a state for the index '" $stateno "'";
  }
}

// wrap php function, build xmlrpc server
$methods = array();
$findstate_sig wrap_php_function('findstate');
if (
$findstate_sig)
  
$methods['examples.getStateName'] = $findstate_sig;
$srv = new xmlrpc_server($methods);