| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Drupal XML-RPC library. 6 * 7 * Based on the IXR - The Incutio XML-RPC Library - (c) Incutio Ltd 2002-2005 8 * Version 1.7 (beta) - Simon Willison, 23rd May 2005 9 * Site: http://scripts.incutio.com/xmlrpc/ 10 * Manual: http://scripts.incutio.com/xmlrpc/manual.php 11 * This version is made available under the GNU GPL License 12 */ 13 14 /** 15 * Turns a data structure into objects with 'data' and 'type' attributes. 16 * 17 * @param $data 18 * The data structure. 19 * @param $type 20 * Optional type to assign to $data. 21 * 22 * @return object 23 * An XML-RPC data object containing the input $data. 24 */ 25 function xmlrpc_value($data, $type = FALSE) { 26 $xmlrpc_value = new stdClass(); 27 $xmlrpc_value->data = $data; 28 if (!$type) { 29 $type = xmlrpc_value_calculate_type($xmlrpc_value); 30 } 31 $xmlrpc_value->type = $type; 32 if ($type == 'struct') { 33 // Turn all the values in the array into new xmlrpc_values 34 foreach ($xmlrpc_value->data as $key => $value) { 35 $xmlrpc_value->data[$key] = xmlrpc_value($value); 36 } 37 } 38 if ($type == 'array') { 39 for ($i = 0, $j = count($xmlrpc_value->data); $i < $j; $i++) { 40 $xmlrpc_value->data[$i] = xmlrpc_value($xmlrpc_value->data[$i]); 41 } 42 } 43 return $xmlrpc_value; 44 } 45 46 /** 47 * Maps a PHP type to an XML-RPC type. 48 * 49 * @param $xmlrpc_value 50 * Variable whose type should be mapped. 51 * 52 * @return string 53 * The corresponding XML-RPC type. 54 * 55 * @see http://www.xmlrpc.com/spec#scalars 56 */ 57 function xmlrpc_value_calculate_type($xmlrpc_value) { 58 // http://www.php.net/gettype: Never use gettype() to test for a certain type 59 // [...] Instead, use the is_* functions. 60 if (is_bool($xmlrpc_value->data)) { 61 return 'boolean'; 62 } 63 if (is_double($xmlrpc_value->data)) { 64 return 'double'; 65 } 66 if (is_int($xmlrpc_value->data)) { 67 return 'int'; 68 } 69 if (is_array($xmlrpc_value->data)) { 70 // empty or integer-indexed arrays are 'array', string-indexed arrays 'struct' 71 return empty($xmlrpc_value->data) || range(0, count($xmlrpc_value->data) - 1) === array_keys($xmlrpc_value->data) ? 'array' : 'struct'; 72 } 73 if (is_object($xmlrpc_value->data)) { 74 if (isset($xmlrpc_value->data->is_date)) { 75 return 'date'; 76 } 77 if (isset($xmlrpc_value->data->is_base64)) { 78 return 'base64'; 79 } 80 $xmlrpc_value->data = get_object_vars($xmlrpc_value->data); 81 return 'struct'; 82 } 83 // default 84 return 'string'; 85 } 86 87 /** 88 * Generates XML representing the given value. 89 * 90 * @param $xmlrpc_value 91 * A value to be represented in XML. 92 * 93 * @return 94 * XML representation of $xmlrpc_value. 95 */ 96 function xmlrpc_value_get_xml($xmlrpc_value) { 97 switch ($xmlrpc_value->type) { 98 case 'boolean': 99 return '<boolean>' . (($xmlrpc_value->data) ? '1' : '0') . '</boolean>'; 100 101 case 'int': 102 return '<int>' . $xmlrpc_value->data . '</int>'; 103 104 case 'double': 105 return '<double>' . $xmlrpc_value->data . '</double>'; 106 107 case 'string': 108 // Note: we don't escape apostrophes because of the many blogging clients 109 // that don't support numerical entities (and XML in general) properly. 110 return '<string>' . htmlspecialchars($xmlrpc_value->data) . '</string>'; 111 112 case 'array': 113 $return = '<array><data>' . "\n"; 114 foreach ($xmlrpc_value->data as $item) { 115 $return .= ' <value>' . xmlrpc_value_get_xml($item) . "</value>\n"; 116 } 117 $return .= '</data></array>'; 118 return $return; 119 120 case 'struct': 121 $return = '<struct>' . "\n"; 122 foreach ($xmlrpc_value->data as $name => $value) { 123 $return .= " <member><name>" . check_plain($name) . "</name><value>"; 124 $return .= xmlrpc_value_get_xml($value) . "</value></member>\n"; 125 } 126 $return .= '</struct>'; 127 return $return; 128 129 case 'date': 130 return xmlrpc_date_get_xml($xmlrpc_value->data); 131 132 case 'base64': 133 return xmlrpc_base64_get_xml($xmlrpc_value->data); 134 } 135 return FALSE; 136 } 137 138 /** 139 * Constructs an object representing an XML-RPC message. 140 * 141 * @param $message 142 * A string containing an XML message. 143 * 144 * @return object 145 * An XML-RPC object containing the message. 146 * 147 * @see http://www.xmlrpc.com/spec 148 */ 149 function xmlrpc_message($message) { 150 $xmlrpc_message = new stdClass(); 151 // The stack used to keep track of the current array/struct 152 $xmlrpc_message->array_structs = array(); 153 // The stack used to keep track of if things are structs or array 154 $xmlrpc_message->array_structs_types = array(); 155 // A stack as well 156 $xmlrpc_message->current_struct_name = array(); 157 $xmlrpc_message->message = $message; 158 return $xmlrpc_message; 159 } 160 161 /** 162 * Parses an XML-RPC message. 163 * 164 * If parsing fails, the faultCode and faultString will be added to the message 165 * object. 166 * 167 * @param $xmlrpc_message 168 * An object generated by xmlrpc_message(). 169 * 170 * @return 171 * TRUE if parsing succeeded; FALSE otherwise. 172 */ 173 function xmlrpc_message_parse($xmlrpc_message) { 174 $xmlrpc_message->_parser = xml_parser_create(); 175 // Set XML parser to take the case of tags into account. 176 xml_parser_set_option($xmlrpc_message->_parser, XML_OPTION_CASE_FOLDING, FALSE); 177 // Set XML parser callback functions 178 xml_set_element_handler($xmlrpc_message->_parser, 'xmlrpc_message_tag_open', 'xmlrpc_message_tag_close'); 179 xml_set_character_data_handler($xmlrpc_message->_parser, 'xmlrpc_message_cdata'); 180 xmlrpc_message_set($xmlrpc_message); 181 if (!xml_parse($xmlrpc_message->_parser, $xmlrpc_message->message)) { 182 return FALSE; 183 } 184 xml_parser_free($xmlrpc_message->_parser); 185 186 // Grab the error messages, if any. 187 $xmlrpc_message = xmlrpc_message_get(); 188 if (!isset($xmlrpc_message->messagetype)) { 189 return FALSE; 190 } 191 elseif ($xmlrpc_message->messagetype == 'fault') { 192 $xmlrpc_message->fault_code = $xmlrpc_message->params[0]['faultCode']; 193 $xmlrpc_message->fault_string = $xmlrpc_message->params[0]['faultString']; 194 } 195 return TRUE; 196 } 197 198 /** 199 * Stores a copy of the most recent XML-RPC message object temporarily. 200 * 201 * @param $value 202 * An XML-RPC message to store, or NULL to keep the last message. 203 * 204 * @return object 205 * The most recently stored message. 206 * 207 * @see xmlrpc_message_get() 208 */ 209 function xmlrpc_message_set($value = NULL) { 210 static $xmlrpc_message; 211 if ($value) { 212 $xmlrpc_message = $value; 213 } 214 return $xmlrpc_message; 215 } 216 217 /** 218 * Returns the most recently stored XML-RPC message object. 219 * 220 * @return object 221 * The most recently stored message. 222 * 223 * @see xmlrpc_message_set() 224 */ 225 function xmlrpc_message_get() { 226 return xmlrpc_message_set(); 227 } 228 229 /** 230 * Handles opening tags for XML parsing in xmlrpc_message_parse(). 231 */ 232 function xmlrpc_message_tag_open($parser, $tag, $attr) { 233 $xmlrpc_message = xmlrpc_message_get(); 234 $xmlrpc_message->current_tag_contents = ''; 235 $xmlrpc_message->last_open = $tag; 236 switch ($tag) { 237 case 'methodCall': 238 case 'methodResponse': 239 case 'fault': 240 $xmlrpc_message->messagetype = $tag; 241 break; 242 243 // Deal with stacks of arrays and structs 244 case 'data': 245 $xmlrpc_message->array_structs_types[] = 'array'; 246 $xmlrpc_message->array_structs[] = array(); 247 break; 248 249 case 'struct': 250 $xmlrpc_message->array_structs_types[] = 'struct'; 251 $xmlrpc_message->array_structs[] = array(); 252 break; 253 } 254 xmlrpc_message_set($xmlrpc_message); 255 } 256 257 /** 258 * Handles character data for XML parsing in xmlrpc_message_parse(). 259 */ 260 function xmlrpc_message_cdata($parser, $cdata) { 261 $xmlrpc_message = xmlrpc_message_get(); 262 $xmlrpc_message->current_tag_contents .= $cdata; 263 xmlrpc_message_set($xmlrpc_message); 264 } 265 266 /** 267 * Handles closing tags for XML parsing in xmlrpc_message_parse(). 268 */ 269 function xmlrpc_message_tag_close($parser, $tag) { 270 $xmlrpc_message = xmlrpc_message_get(); 271 $value_flag = FALSE; 272 switch ($tag) { 273 case 'int': 274 case 'i4': 275 $value = (int)trim($xmlrpc_message->current_tag_contents); 276 $value_flag = TRUE; 277 break; 278 279 case 'double': 280 $value = (double)trim($xmlrpc_message->current_tag_contents); 281 $value_flag = TRUE; 282 break; 283 284 case 'string': 285 $value = $xmlrpc_message->current_tag_contents; 286 $value_flag = TRUE; 287 break; 288 289 case 'dateTime.iso8601': 290 $value = xmlrpc_date(trim($xmlrpc_message->current_tag_contents)); 291 // $value = $iso->getTimestamp(); 292 $value_flag = TRUE; 293 break; 294 295 case 'value': 296 // If no type is indicated, the type is string 297 // We take special care for empty values 298 if (trim($xmlrpc_message->current_tag_contents) != '' || (isset($xmlrpc_message->last_open) && ($xmlrpc_message->last_open == 'value'))) { 299 $value = (string) $xmlrpc_message->current_tag_contents; 300 $value_flag = TRUE; 301 } 302 unset($xmlrpc_message->last_open); 303 break; 304 305 case 'boolean': 306 $value = (boolean)trim($xmlrpc_message->current_tag_contents); 307 $value_flag = TRUE; 308 break; 309 310 case 'base64': 311 $value = base64_decode(trim($xmlrpc_message->current_tag_contents)); 312 $value_flag = TRUE; 313 break; 314 315 // Deal with stacks of arrays and structs 316 case 'data': 317 case 'struct': 318 $value = array_pop($xmlrpc_message->array_structs); 319 array_pop($xmlrpc_message->array_structs_types); 320 $value_flag = TRUE; 321 break; 322 323 case 'member': 324 array_pop($xmlrpc_message->current_struct_name); 325 break; 326 327 case 'name': 328 $xmlrpc_message->current_struct_name[] = trim($xmlrpc_message->current_tag_contents); 329 break; 330 331 case 'methodName': 332 $xmlrpc_message->methodname = trim($xmlrpc_message->current_tag_contents); 333 break; 334 } 335 if ($value_flag) { 336 if (count($xmlrpc_message->array_structs) > 0) { 337 // Add value to struct or array 338 if ($xmlrpc_message->array_structs_types[count($xmlrpc_message->array_structs_types) - 1] == 'struct') { 339 // Add to struct 340 $xmlrpc_message->array_structs[count($xmlrpc_message->array_structs) - 1][$xmlrpc_message->current_struct_name[count($xmlrpc_message->current_struct_name) - 1]] = $value; 341 } 342 else { 343 // Add to array 344 $xmlrpc_message->array_structs[count($xmlrpc_message->array_structs) - 1][] = $value; 345 } 346 } 347 else { 348 // Just add as a parameter 349 $xmlrpc_message->params[] = $value; 350 } 351 } 352 if (!in_array($tag, array("data", "struct", "member"))) { 353 $xmlrpc_message->current_tag_contents = ''; 354 } 355 xmlrpc_message_set($xmlrpc_message); 356 } 357 358 /** 359 * Constructs an object representing an XML-RPC request. 360 * 361 * @param $method 362 * The name of the method to be called. 363 * @param $args 364 * An array of parameters to send with the method. 365 * 366 * @return object 367 * An XML-RPC object representing the request. 368 */ 369 function xmlrpc_request($method, $args) { 370 $xmlrpc_request = new stdClass(); 371 $xmlrpc_request->method = $method; 372 $xmlrpc_request->args = $args; 373 $xmlrpc_request->xml = <<<EOD 374 <?xml version="1.0"?> 375 <methodCall> 376 <methodName>{$xmlrpc_request->method}</methodName> 377 <params> 378 379 EOD; 380 foreach ($xmlrpc_request->args as $arg) { 381 $xmlrpc_request->xml .= '<param><value>'; 382 $v = xmlrpc_value($arg); 383 $xmlrpc_request->xml .= xmlrpc_value_get_xml($v); 384 $xmlrpc_request->xml .= "</value></param>\n"; 385 } 386 $xmlrpc_request->xml .= '</params></methodCall>'; 387 return $xmlrpc_request; 388 } 389 390 /** 391 * Generates, temporarily saves, and returns an XML-RPC error object. 392 * 393 * @param $code 394 * The error code. 395 * @param $message 396 * The error message. 397 * @param $reset 398 * TRUE to empty the temporary error storage. Ignored if $code is supplied. 399 * 400 * @return object 401 * An XML-RPC error object representing $code and $message, or the most 402 * recently stored error object if omitted. 403 */ 404 function xmlrpc_error($code = NULL, $message = NULL, $reset = FALSE) { 405 static $xmlrpc_error; 406 if (isset($code)) { 407 $xmlrpc_error = new stdClass(); 408 $xmlrpc_error->is_error = TRUE; 409 $xmlrpc_error->code = $code; 410 $xmlrpc_error->message = $message; 411 } 412 elseif ($reset) { 413 $xmlrpc_error = NULL; 414 } 415 return $xmlrpc_error; 416 } 417 418 /** 419 * Converts an XML-RPC error object into XML. 420 * 421 * @param $xmlrpc_error 422 * The XML-RPC error object. 423 * 424 * @return string 425 * An XML representation of the error as an XML methodResponse. 426 */ 427 function xmlrpc_error_get_xml($xmlrpc_error) { 428 return <<<EOD 429 <methodResponse> 430 <fault> 431 <value> 432 <struct> 433 <member> 434 <name>faultCode</name> 435 <value><int>{$xmlrpc_error->code}</int></value> 436 </member> 437 <member> 438 <name>faultString</name> 439 <value><string>{$xmlrpc_error->message}</string></value> 440 </member> 441 </struct> 442 </value> 443 </fault> 444 </methodResponse> 445 446 EOD; 447 } 448 449 /** 450 * Converts a PHP or ISO date/time to an XML-RPC object. 451 * 452 * @param $time 453 * A PHP timestamp or an ISO date-time string. 454 * 455 * @return object 456 * An XML-RPC time/date object. 457 */ 458 function xmlrpc_date($time) { 459 $xmlrpc_date = new stdClass(); 460 $xmlrpc_date->is_date = TRUE; 461 // $time can be a PHP timestamp or an ISO one 462 if (is_numeric($time)) { 463 $xmlrpc_date->year = gmdate('Y', $time); 464 $xmlrpc_date->month = gmdate('m', $time); 465 $xmlrpc_date->day = gmdate('d', $time); 466 $xmlrpc_date->hour = gmdate('H', $time); 467 $xmlrpc_date->minute = gmdate('i', $time); 468 $xmlrpc_date->second = gmdate('s', $time); 469 $xmlrpc_date->iso8601 = gmdate('Ymd\TH:i:s', $time); 470 } 471 else { 472 $xmlrpc_date->iso8601 = $time; 473 $time = str_replace(array('-', ':'), '', $time); 474 $xmlrpc_date->year = substr($time, 0, 4); 475 $xmlrpc_date->month = substr($time, 4, 2); 476 $xmlrpc_date->day = substr($time, 6, 2); 477 $xmlrpc_date->hour = substr($time, 9, 2); 478 $xmlrpc_date->minute = substr($time, 11, 2); 479 $xmlrpc_date->second = substr($time, 13, 2); 480 } 481 return $xmlrpc_date; 482 } 483 484 /** 485 * Converts an XML-RPC date-time object into XML. 486 * 487 * @param $xmlrpc_date 488 * The XML-RPC date-time object. 489 * 490 * @return string 491 * An XML representation of the date/time as XML. 492 */ 493 function xmlrpc_date_get_xml($xmlrpc_date) { 494 return '<dateTime.iso8601>' . $xmlrpc_date->year . $xmlrpc_date->month . $xmlrpc_date->day . 'T' . $xmlrpc_date->hour . ':' . $xmlrpc_date->minute . ':' . $xmlrpc_date->second . '</dateTime.iso8601>'; 495 } 496 497 /** 498 * Returns an XML-RPC base 64 object. 499 * 500 * @param $data 501 * Base 64 data to store in returned object. 502 * 503 * @return object 504 * An XML-RPC base 64 object. 505 */ 506 function xmlrpc_base64($data) { 507 $xmlrpc_base64 = new stdClass(); 508 $xmlrpc_base64->is_base64 = TRUE; 509 $xmlrpc_base64->data = $data; 510 return $xmlrpc_base64; 511 } 512 513 /** 514 * Converts an XML-RPC base 64 object into XML. 515 * 516 * @param $xmlrpc_base64 517 * The XML-RPC base 64 object. 518 * 519 * @return string 520 * An XML representation of the base 64 data as XML. 521 */ 522 function xmlrpc_base64_get_xml($xmlrpc_base64) { 523 return '<base64>' . base64_encode($xmlrpc_base64->data) . '</base64>'; 524 } 525 526 /** 527 * Performs one or more XML-RPC requests. 528 * 529 * @param $url 530 * An absolute URL of the XML-RPC endpoint, e.g., 531 * http://example.com/xmlrpc.php 532 * @param $args 533 * An associative array whose keys are the methods to call and whose values 534 * are the arguments to pass to the respective method. If multiple methods 535 * are specified, a system.multicall is performed. 536 * @param $options 537 * (optional) An array of options to pass along to drupal_http_request(). 538 * 539 * @return 540 * A single response (single request) or an array of responses (multicall 541 * request). Each response is the return value of the method, just as if it 542 * has been a local function call, on success, or FALSE on failure. If FALSE 543 * is returned, see xmlrpc_errno() and xmlrpc_error_msg() to get more 544 * information. 545 */ 546 function _xmlrpc($url, $args, $options = array()) { 547 xmlrpc_clear_error(); 548 if (count($args) > 1) { 549 $multicall_args = array(); 550 foreach ($args as $method => $call) { 551 $multicall_args[] = array('methodName' => $method, 'params' => $call); 552 } 553 $method = 'system.multicall'; 554 $args = array($multicall_args); 555 } 556 else { 557 $method = key($args); 558 $args = $args[$method]; 559 } 560 $xmlrpc_request = xmlrpc_request($method, $args); 561 // Required options which will replace any that are passed in. 562 $options['method'] = 'POST'; 563 $options['headers']['Content-Type'] = 'text/xml'; 564 $options['data'] = $xmlrpc_request->xml; 565 $result = drupal_http_request($url, $options); 566 if ($result->code != 200) { 567 xmlrpc_error($result->code, $result->error); 568 return FALSE; 569 } 570 $message = xmlrpc_message($result->data); 571 // Now parse what we've got back 572 if (!xmlrpc_message_parse($message)) { 573 // XML error 574 xmlrpc_error(-32700, t('Parse error. Not well formed')); 575 return FALSE; 576 } 577 // Is the message a fault? 578 if ($message->messagetype == 'fault') { 579 xmlrpc_error($message->fault_code, $message->fault_string); 580 return FALSE; 581 } 582 // We now know that the message is well-formed and a non-fault result. 583 if ($method == 'system.multicall') { 584 // Return per-method results or error objects. 585 $return = array(); 586 foreach ($message->params[0] as $result) { 587 if (array_keys($result) == array(0)) { 588 $return[] = $result[0]; 589 } 590 else { 591 $return[] = xmlrpc_error($result['faultCode'], $result['faultString']); 592 } 593 } 594 } 595 else { 596 $return = $message->params[0]; 597 } 598 return $return; 599 } 600 601 /** 602 * Returns the last XML-RPC client error number. 603 */ 604 function xmlrpc_errno() { 605 $error = xmlrpc_error(); 606 return ($error != NULL ? $error->code : NULL); 607 } 608 609 /** 610 * Returns the last XML-RPC client error message. 611 */ 612 function xmlrpc_error_msg() { 613 $error = xmlrpc_error(); 614 return ($error != NULL ? $error->message : NULL); 615 } 616 617 /** 618 * Clears any previously-saved errors. 619 * 620 * @see xmlrpc_error() 621 */ 622 function xmlrpc_clear_error() { 623 xmlrpc_error(NULL, NULL, TRUE); 624 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title