| [ Index ] |
PHP Cross Reference of Joomla 1.5.26 DE |
[Summary view] [Print] [Text view]
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,v 1.10 2006/09/01 21:49:19 ggiunta 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, '@') !== 0 && !$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[$i] = array(); 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_response, true); 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($val, true); 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 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Mar 28 15:54:07 2012 | Cross-referenced by PHPXref 0.7.1 |