b2evolution

Multilingual multiuser multiblog engine

b2evolution Technical Documentation (Version 2.4) [ class tree: main ] [ index: main ] [ all elements ]

Source for file _xmlrpc_wrappers.inc.php

Documentation is available at _xmlrpc_wrappers.inc.php

  1. <?php
  2. /**
  3.  * PHP-XMLRPC "wrapper" functions
  4.  * Generate stubs to transparently access xmlrpc methods as php functions and viceversa
  5.  *
  6.  * @version $Id: _xmlrpc_wrappers.inc.php,v 1.1 2008/01/14 07:17:32 fplanque Exp $
  7.  * @copyright G. Giunta (C) 2006
  8.  * @author Gaetano Giunta
  9.  *
  10.  * @todo separate introspection from code generation for func-2-method wrapping
  11.  * @todo use some better templating system from code generation?
  12.  * @todo implement method wrapping with preservation of php objs in calls
  13.  * @todo when wrapping methods without obj rebuilding, use return_type = 'phpvals' (faster)
  14.  * @todo implement self-parsing of php code for PHP <= 4
  15.  */
  16.  
  17.     // requires: xmlrpc.inc
  18.  
  19.     /**
  20.     * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
  21.     * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
  22.     * NB: for php 'resource' types returns empty string, since resources cannot be serialized;
  23.     * for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
  24.     * @param string $phptype 
  25.     * @return string 
  26.     */
  27.     function php_2_xmlrpc_type($phptype)
  28.     {
  29.         switch(strtolower($phptype))
  30.         {
  31.             case 'string':
  32.                 return $GLOBALS['xmlrpcString'];
  33.             case 'integer':
  34.             case $GLOBALS['xmlrpcInt']// 'int'
  35.             case $GLOBALS['xmlrpcI4']:
  36.                 return $GLOBALS['xmlrpcInt'];
  37.             case 'double':
  38.                 return $GLOBALS['xmlrpcDouble'];
  39.             case 'boolean':
  40.                 return $GLOBALS['xmlrpcBoolean'];
  41.             case 'array':
  42.                 return $GLOBALS['xmlrpcArray'];
  43.             case 'object':
  44.                 return $GLOBALS['xmlrpcStruct'];
  45.             case $GLOBALS['xmlrpcBase64']:
  46.             case $GLOBALS['xmlrpcStruct']:
  47.                 return strtolower($phptype);
  48.             case 'resource':
  49.                 return '';
  50.             default:
  51.                 if(class_exists($phptype))
  52.                 {
  53.                     return $GLOBALS['xmlrpcStruct'];
  54.                 }
  55.                 else
  56.                 {
  57.                     // unknown: might be any 'extended' xmlrpc type
  58.                     return $GLOBALS['xmlrpcValue'];
  59.                 }
  60.         }
  61.     }
  62.  
  63.     /**
  64.     * Given a string defining a phpxmlrpc type return corresponding php type.
  65.     * @param string $xmlrpctype 
  66.     * @return string 
  67.     */
  68.     function xmlrpc_2_php_type($xmlrpctype)
  69.     {
  70.         switch(strtolower($xmlrpctype))
  71.         {
  72.             case 'base64':
  73.             case 'datetime.iso8601':
  74.             case 'string':
  75.                 return $GLOBALS['xmlrpcString'];
  76.             case 'int':
  77.             case 'i4':
  78.                 return 'integer';
  79.             case 'struct':
  80.             case 'array':
  81.                 return 'array';
  82.             case 'double':
  83.                 return 'float';
  84.             case 'undefined':
  85.                 return 'mixed';
  86.             case 'boolean':
  87.             case 'null':
  88.             default:
  89.                 // unknown: might be any xmlrpc type
  90.                 return strtolower($xmlrpctype);
  91.         }
  92.     }
  93.  
  94.     /**
  95.     * Given a user-defined PHP function, create a PHP 'wrapper' function that can
  96.     * be exposed as xmlrpc method from an xmlrpc_server object and called from remote
  97.     * clients (as well as its corresponding signature info).
  98.     *
  99.     * Since php is a typeless language, to infer types of input and output parameters,
  100.     * it relies on parsing the javadoc-style comment block associated with the given
  101.     * function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
  102.     * in the @param tag is also allowed, if you need the php function to receive/send
  103.     * data in that particular format (note that base64 encoding/decoding is transparently
  104.     * carried out by the lib, while datetime vals are passed around as strings)
  105.     *
  106.     * Known limitations:
  107.     * - requires PHP 5.0.3 +
  108.     * - only works for user-defined functions, not for PHP internal functions
  109.     *   (reflection does not support retrieving number/type of params for those)
  110.     * - functions returning php objects will generate special xmlrpc responses:
  111.     *   when the xmlrpc decoding of those responses is carried out by this same lib, using
  112.     *   the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
  113.     *   In short: php objects can be serialized, too (except for their resource members),
  114.     *   using this function.
  115.     *   Other libs might choke on the very same xml that will be generated in this case
  116.     *   (i.e. it has a nonstandard attribute on struct element tags)
  117.     * - usage of javadoc @param tags using param names in a different order from the
  118.     *   function prototype is not considered valid (to be fixed?)
  119.     *
  120.     * Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
  121.     * php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
  122.     * is by making use of the functions_parameters_type class member.
  123.     *
  124.     * @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') might be ok too, in the future...
  125.     * @param string $newfuncname (optional) name for function to be created
  126.     * @param array $extra_options (optional) array of options for conversion. valid values include:
  127.     *         bool  return_source when true, php code w. function definition will be returned, not evaluated
  128.     *         bool  encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
  129.     *         bool  decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
  130.     *         bool  suppress_warnings  remove from produced xml any runtime warnings due to the php function being invoked
  131.     * @return false on error, or an array containing the name of the new php function,
  132.     *          its signature and docs, to be used in the server dispatch map
  133.     *
  134.     * @todo decide how to deal with params passed by ref: bomb out or allow?
  135.     * @todo finish using javadoc info to build method sig if all params are named but out of order
  136.     * @todo add a check for params of 'resource' type
  137.     * @todo add some trigger_errors / error_log when returning false?
  138.     * @todo what to do when the PHP function returns NULL? we are currently an empty string value...
  139.     * @todo add an option to suppress php warnings in invocation of user function, similar to server debug level 3?
  140.     */
  141.     function wrap_php_function($funcname$newfuncname=''$extra_options=array())
  142.     {
  143.         $buildit = isset($extra_options['return_source']!($extra_options['return_source']true;
  144.         $prefix = isset($extra_options['prefix']$extra_options['prefix''xmlrpc';
  145.         $encode_php_objects = isset($extra_options['encode_php_objs']? (bool)$extra_options['encode_php_objs'false;
  146.         $decode_php_objects = isset($extra_options['decode_php_objs']? (bool)$extra_options['decode_php_objs'false;
  147.         $catch_warnings = isset($extra_options['suppress_warnings']&& $extra_options['suppress_warnings''@' '';
  148.  
  149.         if(version_compare(phpversion()'5.0.3'== -1)
  150.         {
  151.             // up to php 5.0.3 some useful reflection methods were missing
  152.             error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
  153.             return false;
  154.         }
  155.         if((is_array($funcname&& !method_exists($funcname[0]$funcname[1])) || !function_exists($funcname))
  156.         {
  157.             error_log('XML-RPC: function to be wrapped is not defined: '.$funcname);
  158.             return false;
  159.         }
  160.         else
  161.         {
  162.             // determine name of new php function
  163.             if($newfuncname == '')
  164.             {
  165.                 if(is_array($funcname))
  166.                 {
  167.                     $xmlrpcfuncname "{$prefix}_".implode('_'$funcname);
  168.                 }
  169.                 else
  170.                 {
  171.                     $xmlrpcfuncname "{$prefix}_$funcname";
  172.                 }
  173.             }
  174.             else
  175.             {
  176.                 $xmlrpcfuncname $newfuncname;
  177.             }
  178.             while($buildit && function_exists($xmlrpcfuncname))
  179.             {
  180.                 $xmlrpcfuncname .= 'x';
  181.             }
  182.  
  183.             // start to introspect PHP code
  184.             $func =new ReflectionFunction($funcname);
  185.             if($func->isInternal())
  186.             {
  187.                 // Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
  188.                 // instead of getparameters to fully reflect internal php functions ?
  189.                 error_log('XML-RPC: function to be wrapped is internal: '.$funcname);
  190.                 return false;
  191.             }
  192.  
  193.             // retrieve parameter names, types and description from javadoc comments
  194.  
  195.             // function description
  196.             $desc '';
  197.             // type of return val: by default 'any'
  198.             $returns $GLOBALS['xmlrpcValue'];
  199.             // desc of return val
  200.             $returnsDocs '';
  201.             // type + name of function parameters
  202.             $paramDocs array();
  203.  
  204.             $docs $func->getDocComment();
  205.             if($docs != '')
  206.             {
  207.                 $docs explode("\n"$docs);
  208.                 $i 0;
  209.                 foreach($docs as $doc)
  210.                 {
  211.                     $doc trim($doc" \r\t/*");
  212.                     if(strlen($doc&& strpos($doc'@'!== && !$i)
  213.                     {
  214.                         if($desc)
  215.                         {
  216.                             $desc .= "\n";
  217.                         }
  218.                         $desc .= $doc;
  219.                     }
  220.                     elseif(strpos($doc'@param'=== 0)
  221.                     {
  222.                         // syntax: @param type [$name] desc
  223.                         if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/'$doc$matches))
  224.                         {
  225.                             if(strpos($matches[1]'|'))
  226.                             {
  227.                                 //$paramDocs[$i]['type'] = explode('|', $matches[1]);
  228.                                 $paramDocs[$i]['type''mixed';
  229.                             }
  230.                             else
  231.                             {
  232.                                 $paramDocs[$i]['type'$matches[1];
  233.                             }
  234.                             $paramDocs[$i]['name'trim($matches[2]);
  235.                             $paramDocs[$i]['doc'$matches[3];
  236.                         }
  237.                         $i++;
  238.                     }
  239.                     elseif(strpos($doc'@return'=== 0)
  240.                     {
  241.                         // syntax: @return type desc
  242.                         //$returns = preg_split('/\s+/', $doc);
  243.                         if(preg_match('/@return\s+(\S+)\s+(.+)/'$doc$matches))
  244.                         {
  245.                             $returns php_2_xmlrpc_type($matches[1]);
  246.                             if(isset($matches[2]))
  247.                             {
  248.                                 $returnsDocs $matches[2];
  249.                             }
  250.                         }
  251.                     }
  252.                 }
  253.             }
  254.  
  255.             // execute introspection of actual function prototype
  256.             $params array();
  257.             $i 0;
  258.             foreach($func->getParameters(as $paramobj)
  259.             {
  260.                 $params[$iarray();
  261.                 $params[$i]['name''$'.$paramobj->getName();
  262.                 $params[$i]['isoptional'$paramobj->isOptional();
  263.                 $i++;
  264.             }
  265.  
  266.  
  267.             // start  building of PHP code to be eval'd
  268.             $innercode '';
  269.             $i 0;
  270.             $parsvariations array();
  271.             $pars array();
  272.             $pnum count($params);
  273.             foreach($params as $param)
  274.             {
  275.                 if (isset($paramDocs[$i]['name']&& $paramDocs[$i]['name'&& strtolower($paramDocs[$i]['name']!= strtolower($param['name']))
  276.                 {
  277.                     // param name from phpdoc info does not match param definition!
  278.                     $paramDocs[$i]['type''mixed';
  279.                 }
  280.  
  281.                 if($param['isoptional'])
  282.                 {
  283.                     // this particular parameter is optional. save as valid previous list of parameters
  284.                     $innercode .= "if (\$paramcount > $i) {\n";
  285.                     $parsvariations[$pars;
  286.                 }
  287.                 $innercode .= "\$p$i = \$msg->getParam($i);\n";
  288.                 if ($decode_php_objects)
  289.                 {
  290.                     $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i, array('decode_php_objs'));\n";
  291.                 }
  292.                 else
  293.                 {
  294.                     $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i);\n";
  295.                 }
  296.  
  297.                 $pars["\$p$i";
  298.                 $i++;
  299.                 if($param['isoptional'])
  300.                 {
  301.                     $innercode .= "}\n";
  302.                 }
  303.                 if($i == $pnum)
  304.                 {
  305.                     // last allowed parameters combination
  306.                     $parsvariations[$pars;
  307.                 }
  308.             }
  309.  
  310.             $sigs array();
  311.             $psigs array();
  312.             if(count($parsvariations== 0)
  313.             {
  314.                 // only known good synopsis = no parameters
  315.                 $parsvariations[array();
  316.                 $minpars 0;
  317.             }
  318.             else
  319.             {
  320.                 $minpars count($parsvariations[0]);
  321.             }
  322.  
  323.             if($minpars)
  324.             {
  325.                 // add to code the check for min params number
  326.                 // NB: this check needs to be done BEFORE decoding param values
  327.                 $innercode "\$paramcount = \$msg->getNumParams();\n" .
  328.                 "if (\$paramcount < $minpars) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n$innercode;
  329.             }
  330.             else
  331.             {
  332.                 $innercode "\$paramcount = \$msg->getNumParams();\n" $innercode;
  333.             }
  334.  
  335.             $innercode .= "\$np = false;\n";
  336.             foreach($parsvariations as $pars)
  337.             {
  338.                 $innercode .= "if (\$paramcount == " count($pars") \$retval = {$catch_warnings}$funcname(implode(','$pars"); else\n";
  339.                 // build a 'generic' signature (only use an appropriate return type)
  340.                 $sig array($returns);
  341.                 $psig array($returnsDocs);
  342.                 for($i=0$i count($pars)$i++)
  343.                 {
  344.                     if (isset($paramDocs[$i]['type']))
  345.                     {
  346.                         $sig[php_2_xmlrpc_type($paramDocs[$i]['type']);
  347.                     }
  348.                     else
  349.                     {
  350.                         $sig[$GLOBALS['xmlrpcValue'];
  351.                     }
  352.                     $psig[= isset($paramDocs[$i]['doc']$paramDocs[$i]['doc''';
  353.                 }
  354.                 $sigs[$sig;
  355.                 $psigs[$psig;
  356.             }
  357.             $innercode .= "\$np = true;\n";
  358.             $innercode .= "if (\$np) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else {\n";
  359.             //$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
  360.             $innercode .= "if (is_a(\$retval, '{$prefix}resp')) return \$retval; else\n";
  361.             if($returns == $GLOBALS['xmlrpcDateTime'|| $returns == $GLOBALS['xmlrpcBase64'])
  362.             {
  363.                 $innercode .= "return new {$prefix}resp(new {$prefix}val(\$retval, '$returns'));";
  364.             }
  365.             else
  366.             {
  367.                 if ($encode_php_objects)
  368.                     $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval, array('encode_php_objs')));\n";
  369.                 else
  370.                     $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval));\n";
  371.             }
  372.             // shall we exclude functions returning by ref?
  373.             // if($func->returnsReference())
  374.             //     return false;
  375.             $code "function $xmlrpcfuncname(\$msg) {\n$innercode "}\n}";
  376.             //print_r($code);
  377.             if ($buildit)
  378.             {
  379.                 $allOK 0;
  380.                 eval($code.'$allOK=1;');
  381.                 // alternative
  382.                 //$xmlrpcfuncname = create_function('$m', $innercode);
  383.  
  384.                 if(!$allOK)
  385.                 {
  386.                     error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap php function '.$funcname);
  387.                     return false;
  388.                 }
  389.             }
  390.  
  391.             /// @todo examine if $paramDocs matches $parsvariations and build array for
  392.             /// usage as method signature, plus put together a nice string for docs
  393.  
  394.             $ret array('function' => $xmlrpcfuncname'signature' => $sigs'docstring' => $desc'signature_docs' => $psigs'source' => $code);
  395.             return $ret;
  396.         }
  397.     }
  398.  
  399.     /**
  400.     * Given an xmlrpc client and a method name, register a php wrapper function
  401.     * that will call it and return results using native php types for both
  402.     * params and results. The generated php function will return an xmlrpcresp
  403.     * oject for failed xmlrpc calls
  404.     *
  405.     * Known limitations:
  406.     * - server must support system.methodsignature for the wanted xmlrpc method
  407.     * - for methods that expose many signatures, only one can be picked (we
  408.     *   could in priciple check if signatures differ only by number of params
  409.     *   and not by type, but it would be more complication than we can spare time)
  410.     * - nested xmlrpc params: the caller of the generated php function has to
  411.     *   encode on its own the params passed to the php function if these are structs
  412.     *   or arrays whose (sub)members include values of type datetime or base64
  413.     *
  414.     * Notes: the connection properties of the given client will be copied
  415.     * and reused for the connection used during the call to the generated
  416.     * php function.
  417.     * Calling the generated php function 'might' be slow: a new xmlrpc client
  418.     * is created on every invocation and an xmlrpc-connection opened+closed.
  419.     * An extra 'debug' param is appended to param list of xmlrpc method, useful
  420.     * for debugging purposes.
  421.     *
  422.     * @param xmlrpc_client $client     an xmlrpc client set up correctly to communicate with target server
  423.     * @param string        $methodname the xmlrpc method to be mapped to a php function
  424.     * @param array         $extra_options array of options that specify conversion details. valid ptions include
  425.     *         integer       signum      the index of the method signature to use in mapping (if method exposes many sigs)
  426.     *         integer       timeout     timeout (in secs) to be used when executing function/calling remote method
  427.     *         string        protocol    'http' (default), 'http11' or 'https'
  428.     *         string        new_function_name the name of php function to create. If unsepcified, lib will pick an appropriate name
  429.     *         string        return_source if true return php code w. function definition instead fo function name
  430.     *         bool          encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
  431.     *         bool          decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
  432.     *         mixed         return_on_fault a php value to be returned when the xmlrpc call fails/returns a fault response (by default the xmlrpcresp object is returned in this case). If a string is used, '%faultCode%' and '%faultString%' tokens will be substituted with actual error values
  433.     *         bool          debug        set it to 1 or 2 to see debug results of querying server for method synopsis
  434.     * @return string                   the name of the generated php function (or false) - OR AN ARRAY...
  435.     */
  436.     function wrap_xmlrpc_method($client$methodname$extra_options=0$timeout=0$protocol=''$newfuncname='')
  437.     {
  438.         // mind numbing: let caller use sane calling convention (as per javadoc, 3 params),
  439.         // OR the 2.0 calling convention (no ptions) - we really love backward compat, don't we?
  440.         if (!is_array($extra_options))
  441.         {
  442.             $signum $extra_options;
  443.             $extra_options array();
  444.         }
  445.         else
  446.         {
  447.             $signum = isset($extra_options['signum']? (int)$extra_options['signum'0;
  448.             $timeout = isset($extra_options['timeout']? (int)$extra_options['timeout'0;
  449.             $protocol = isset($extra_options['protocol']$extra_options['protocol''';
  450.             $newfuncname = isset($extra_options['new_function_name']$extra_options['new_function_name''';
  451.         }
  452.         //$encode_php_objects = in_array('encode_php_objects', $extra_options);
  453.         //$verbatim_client_copy = in_array('simple_client_copy', $extra_options) ? 1 :
  454.         //    in_array('build_class_code', $extra_options) ? 2 : 0;
  455.  
  456.         $encode_php_objects = isset($extra_options['encode_php_objs']? (bool)$extra_options['encode_php_objs'false;
  457.         $decode_php_objects = isset($extra_options['decode_php_objs']? (bool)$extra_options['decode_php_objs'false;
  458.         $simple_client_copy = isset($extra_options['simple_client_copy']? (int)($extra_options['simple_client_copy']0;
  459.         $buildit = isset($extra_options['return_source']!($extra_options['return_source']true;
  460.         $prefix = isset($extra_options['prefix']$extra_options['prefix''xmlrpc';
  461.         if (isset($extra_options['return_on_fault']))
  462.         {
  463.             $decode_fault true;
  464.             $fault_response $extra_options['return_on_fault'];
  465.         }
  466.         else
  467.         {
  468.             $decode_fault false;
  469.             $fault_response '';
  470.         }
  471.         $debug = isset($extra_options['debug']($extra_options['debug']0;
  472.  
  473.         $msgclass $prefix.'msg';
  474.         $valclass $prefix.'val';
  475.         $decodefunc 'php_'.$prefix.'_decode';
  476.  
  477.         $msg =new $msgclass('system.methodSignature');
  478.         $msg->addparam(new $valclass($methodname));
  479.         $client->setDebug($debug);
  480.         $response =$client->send($msg$timeout$protocol);
  481.         if($response->faultCode())
  482.         {
  483.             error_log('XML-RPC: could not retrieve method signature from remote server for method '.$methodname);
  484.             return false;
  485.         }
  486.         else
  487.         {
  488.             $msig $response->value();
  489.             if ($client->return_type != 'phpvals')
  490.             {
  491.                 $msig $decodefunc($msig);
  492.             }
  493.             if(!is_array($msig|| count($msig<= $signum)
  494.             {
  495.                 error_log('XML-RPC: could not retrieve method signature nr.'.$signum.' from remote server for method '.$methodname);
  496.                 return false;
  497.             }
  498.             else
  499.             {
  500.                 // pick a suitable name for the new function, avoiding collisions
  501.                 if($newfuncname != '')
  502.                 {
  503.                     $xmlrpcfuncname $newfuncname;
  504.                 }
  505.                 else
  506.                 {
  507.                     // take care to insure that methodname is translated to valid
  508.                     // php function name
  509.                     $xmlrpcfuncname $prefix.'_'.preg_replace(array('/\./''/[^a-zA-Z0-9_\x7f-\xff]/'),
  510.                         array('_''')$methodname);
  511.                 }
  512.                 while($buildit && function_exists($xmlrpcfuncname))
  513.                 {
  514.                     $xmlrpcfuncname .= 'x';
  515.                 }
  516.  
  517.                 $msig $msig[$signum];
  518.                 $mdesc '';
  519.                 // if in 'offline' mode, get method description too.
  520.                 // in online mode, favour speed of operation
  521.                 if(!$buildit)
  522.                 {
  523.                     $msg =new $msgclass('system.methodHelp');
  524.                     $msg->addparam(new $valclass($methodname));
  525.                     $response =$client->send($msg$timeout$protocol);
  526.                     if (!$response->faultCode())
  527.                     {
  528.                         $mdesc $response->value();
  529.                         if ($client->return_type != 'phpvals')
  530.                         {
  531.                             $mdesc $mdesc->scalarval();
  532.                         }
  533.                     }
  534.                 }
  535.  
  536.                 $results build_remote_method_wrapper_code($client$methodname,
  537.                     $xmlrpcfuncname$msig$mdesc$timeout$protocol$simple_client_copy,
  538.                     $prefix$decode_php_objects$encode_php_objects$decode_fault,
  539.                     $fault_response);
  540.  
  541.                 //print_r($code);
  542.                 if ($buildit)
  543.                 {
  544.                     $allOK 0;
  545.                     eval($results['source'].'$allOK=1;');
  546.                     // alternative
  547.                     //$xmlrpcfuncname = create_function('$m', $innercode);
  548.                     if($allOK)
  549.                     {
  550.                         return $xmlrpcfuncname;
  551.                     }
  552.                     else
  553.                     {
  554.                         error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap remote method '.$methodname);
  555.                         return false;
  556.                     }
  557.                 }
  558.                 else
  559.                 {
  560.                     $results['function'$xmlrpcfuncname;
  561.                     return $results;
  562.                 }
  563.             }
  564.         }
  565.     }
  566.  
  567.     /**
  568.     * Similar to wrap_xmlrpc_method, but will generate a php class that wraps
  569.     * all xmlrpc methods exposed by the remote server as own methods.
  570.     * For more details see wrap_xmlrpc_method.
  571.     * @param xmlrpc_client $client the client obj all set to query the desired server
  572.     * @param array $extra_options list of options for wrapped code
  573.     * @return mixed false on error, the name of the created class if all ok or an array with code, class name and comments (if the appropriatevoption is set in extra_options)
  574.     */
  575.     function wrap_xmlrpc_server($client$extra_options=array())
  576.     {
  577.         $methodfilter = isset($extra_options['method_filter']$extra_options['method_filter''';
  578.         $signum = isset($extra_options['signum']? (int)$extra_options['signum'0;
  579.         $timeout = isset($extra_options['timeout']? (int)$extra_options['timeout'0;
  580.         $protocol = isset($extra_options['protocol']$extra_options['protocol''';
  581.         $newclassname = isset($extra_options['new_class_name']$extra_options['new_class_name''';
  582.         $encode_php_objects = isset($extra_options['encode_php_objs']? (bool)$extra_options['encode_php_objs'false;
  583.         $decode_php_objects = isset($extra_options['decode_php_objs']? (bool)$extra_options['decode_php_objs'false;
  584.         $verbatim_client_copy = isset($extra_options['simple_client_copy']!($extra_options['simple_client_copy']true;
  585.         $buildit = isset($extra_options['return_source']!($extra_options['return_source']true;
  586.         $prefix = isset($extra_options['prefix']$extra_options['prefix''xmlrpc';
  587.  
  588.         $msgclass $prefix.'msg';
  589.         //$valclass = $prefix.'val';
  590.         $decodefunc 'php_'.$prefix.'_decode';
  591.  
  592.         $msg =new $msgclass('system.listMethods');
  593.         $response =$client->send($msg$timeout$protocol);
  594.         if($response->faultCode())
  595.         {
  596.             error_log('XML-RPC: could not retrieve method list from remote server');
  597.             return false;
  598.         }
  599.         else
  600.         {
  601.             $mlist $response->value();
  602.             if ($client->return_type != 'phpvals')
  603.             {
  604.                 $mlist $decodefunc($mlist);
  605.             }
  606.             if(!is_array($mlist|| !count($mlist))
  607.             {
  608.                 error_log('XML-RPC: could not retrieve meaningful method list from remote server');
  609.                 return false;
  610.             }
  611.             else
  612.             {
  613.                 // pick a suitable name for the new function, avoiding collisions
  614.                 if($newclassname != '')
  615.                 {
  616.                     $xmlrpcclassname $newclassname;
  617.                 }
  618.                 else
  619.                 {
  620.                     $xmlrpcclassname $prefix.'_'.preg_replace(array('/\./''/[^a-zA-Z0-9_\x7f-\xff]/'),
  621.                         array('_''')$client->server).'_client';
  622.                 }
  623.                 while($buildit && class_exists($xmlrpcclassname))
  624.                 {
  625.                     $xmlrpcclassname .= 'x';
  626.                 }
  627.  
  628.                 /// @todo add function setdebug() to new class, to enable/disable debugging
  629.                 $source "class $xmlrpcclassname\n{\nvar \$client;\n\n";
  630.                 $source .= "function $xmlrpcclassname()\n{\n";
  631.                 $source .= build_client_wrapper_code($client$verbatim_client_copy$prefix);
  632.                 $source .= "\$this->client =& \$client;\n}\n\n";
  633.                 $opts array('simple_client_copy' => 2'return_source' => true,
  634.                     'timeout' => $timeout'protocol' => $protocol,
  635.                     'encode_php_objs' => $encode_php_objects'prefix' => $prefix,
  636.                     'decode_php_objs' => $decode_php_objects
  637.                     );
  638.                 /// @todo build javadoc for class definition, too
  639.                 foreach($mlist as $mname)
  640.                 {
  641.                     if ($methodfilter == '' || preg_match($methodfilter$mname))
  642.                     {
  643.                         $opts['new_function_name'preg_replace(array('/\./''/[^a-zA-Z0-9_\x7f-\xff]/'),
  644.                             array('_''')$mname);
  645.                         $methodwrap wrap_xmlrpc_method($client$mname$opts);
  646.                         if ($methodwrap)
  647.                         {
  648.                             if (!$buildit)
  649.                             {
  650.                                 $source .= $methodwrap['docstring'];
  651.                             }
  652.                             $source .= $methodwrap['source']."\n";
  653.                         }
  654.                         else
  655.                         {
  656.                             error_log('XML-RPC: will not create class method to wrap remote method '.$mname);
  657.                         }
  658.                     }
  659.                 }
  660.                 $source .= "}\n";
  661.                 if ($buildit)
  662.                 {
  663.                     $allOK 0;
  664.                     eval($source.'$allOK=1;');
  665.                     // alternative
  666.                     //$xmlrpcfuncname = create_function('$m', $innercode);
  667.                     if($allOK)
  668.                     {
  669.                         return $xmlrpcclassname;
  670.                     }
  671.                     else
  672.                     {
  673.                         error_log('XML-RPC: could not create class '.$xmlrpcclassname.' to wrap remote server '.$client->server);
  674.                         return false;
  675.                     }
  676.                 }
  677.                 else
  678.                 {
  679.                     return array('class' => $xmlrpcclassname'code' => $source'docstring' => '');
  680.                 }
  681.             }
  682.         }
  683.     }
  684.  
  685.     /**
  686.     * Given the necessary info, build php code that creates a new function to
  687.     * invoke a remote xmlrpc method.
  688.     * Take care that no full checking of input parameters is done to ensure that
  689.     * valid php code is emitted.
  690.     * Note: real spaghetti code follows...
  691.     * @access private
  692.     */
  693.     function build_remote_method_wrapper_code($client$methodname$xmlrpcfuncname,
  694.         $msig$mdesc=''$timeout=0$protocol=''$client_copy_mode=0$prefix='xmlrpc',
  695.         $decode_php_objects=false$encode_php_objects=false$decode_fault=false,
  696.         $fault_response='')
  697.     {
  698.         $code "function $xmlrpcfuncname (";
  699.         if ($client_copy_mode 2)
  700.         {
  701.             // client copy mode 0 or 1 == partial / full client copy in emitted code
  702.             $innercode build_client_wrapper_code($client$client_copy_mode$prefix);
  703.             $innercode .= "\$client->setDebug(\$debug);\n";
  704.             $this_ '';
  705.         }
  706.         else
  707.         {
  708.             // client copy mode 2 == no client copy in emitted code
  709.             $innercode '';
  710.             $this_ 'this->';
  711.         }
  712.         $innercode .= "\$msg =& new {$prefix}msg('$methodname');\n";
  713.  
  714.         if ($mdesc != '')
  715.         {
  716.             // take care that PHP comment is not terminated unwillingly by method description
  717.             $mdesc "/**\n* ".str_replace('*/''* /'$mdesc)."\n";
  718.         }
  719.         else
  720.         {
  721.             $mdesc "/**\nFunction $xmlrpcfuncname\n";
  722.         }
  723.  
  724.         // param parsing
  725.         $plist array();
  726.         $pcount count($msig);
  727.         for($i 1$i $pcount$i++)
  728.         {
  729.             $plist["\$p$i";
  730.             $ptype $msig[$i];
  731.             if($ptype == 'i4' || $ptype == 'int' || $ptype == 'boolean' || $ptype == 'double' ||
  732.                 $ptype == 'string' || $ptype == 'dateTime.iso8601' || $ptype == 'base64' || $ptype == 'null')
  733.             {
  734.                 // only build directly xmlrpcvals when type is known and scalar
  735.                 $innercode .= "\$p$i =& new {$prefix}val(\$p$i, '$ptype');\n";
  736.             }
  737.             else
  738.             {
  739.                 if ($encode_php_objects)
  740.                 {
  741.                     $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i, array('encode_php_objs'));\n";
  742.                 }
  743.                 else
  744.                 {
  745.                     $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i);\n";
  746.                 }
  747.             }
  748.             $innercode .= "\$msg->addparam(\$p$i);\n";
  749.             $mdesc .= '* @param '.xmlrpc_2_php_type($ptype)." \$p$i\n";
  750.         }
  751.         if ($client_copy_mode 2)
  752.         {
  753.             $plist['$debug=0';
  754.             $mdesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
  755.         }
  756.         $plist implode(', '$plist);
  757.         $mdesc .= '* @return '.xmlrpc_2_php_type($msig[0])." (or an {$prefix}resp obj instance if call fails)\n*/\n";
  758.  
  759.         $innercode .= "\$res =& \${$this_}client->send(\$msg, $timeout, '$protocol');\n";
  760.         if ($decode_fault)
  761.         {
  762.             if (is_string($fault_response&& ((strpos($fault_response'%faultCode%'!== false|| (strpos($fault_response'%faultString%'!== false)))
  763.             {
  764.                 $respcode "str_replace(array('%faultCode%', '%faultString%'), array(\$res->faultCode(), \$res->faultString()), '".str_replace("'""''"$fault_response)."')";
  765.             }
  766.             else
  767.             {
  768.                 $respcode var_export($fault_responsetrue);
  769.             }
  770.         }
  771.         else
  772.         {
  773.             $respcode '$res';
  774.         }
  775.         if ($decode_php_objects)
  776.         {
  777.             $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value(), array('decode_php_objs'));";
  778.         }
  779.         else
  780.         {
  781.             $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value());";
  782.         }
  783.  
  784.         $code $code $plist") {\n" $innercode "\n}\n";
  785.  
  786.         return array('source' => $code'docstring' => $mdesc);
  787.     }
  788.  
  789.     /**
  790.     * Given necessary info, generate php code that will rebuild a client object
  791.     * Take care that no full checking of input parameters is done to ensure that
  792.     * valid php code is emitted.
  793.     * @access private
  794.     */
  795.     function build_client_wrapper_code($client$verbatim_client_copy$prefix='xmlrpc')
  796.     {
  797.         $code "\$client =& new {$prefix}_client('".str_replace("'""\'"$client->path).
  798.             "', '" str_replace("'""\'"$client->server"', $client->port);\n";
  799.  
  800.         // copy all client fields to the client that will be generated runtime
  801.         // (this provides for future expansion or subclassing of client obj)
  802.         if ($verbatim_client_copy)
  803.         {
  804.             foreach($client as $fld => $val)
  805.             {
  806.                 if($fld != 'debug' && $fld != 'return_type')
  807.                 {
  808.                     $val var_export($valtrue);
  809.                     $code .= "\$client->$fld = $val;\n";
  810.                 }
  811.             }
  812.         }
  813.         // only make sure that client always returns the correct data type
  814.         $code .= "\$client->return_type = '{$prefix}vals';\n";
  815.         //$code .= "\$client->setDebug(\$debug);\n";
  816.         return $code;
  817.     }
  818. ?>

Documentation generated on Sat, 06 Mar 2010 03:43:46 +0100 by phpDocumentor 1.4.2. This site is hosted and maintained by Daniel HAHLER (Contact).