Simpletest Coverage - includes/xmlrpc.inc

1 <?php
2 // $Id: xmlrpc.inc,v 1.58 2009/06/15 10:10:46 dries Exp $
3
4 /**
5 * @file
6 * Drupal XML-RPC library. Based on the IXR - The Incutio XML-RPC Library - (c) Incutio Ltd 2002-2005
7 * Version 1.7 (beta) - Simon Willison, 23rd May 2005
8 * Site: http://scripts.incutio.com/xmlrpc/
9 * Manual: http://scripts.incutio.com/xmlrpc/manual.php
10 * This version is made available under the GNU GPL License
11 */
12
13 /**
14 * Recursively turn a data structure into objects with 'data' and 'type' attributes.
15 *
16 * @param $data
17 * The data structure.
18 * @param $type
19 * Optional type assign to $data.
20 * @return
21 * Object.
22 */
23 function xmlrpc_value($data, $type = FALSE) {
24 $xmlrpc_value = new stdClass();
25 $xmlrpc_value->data = $data;
26 if (!$type) {
27 $type = xmlrpc_value_calculate_type($xmlrpc_value);
28 }
29 $xmlrpc_value->type = $type;
30 if ($type == 'struct') {
31 // Turn all the values in the array into new xmlrpc_values
32 foreach ($xmlrpc_value->data as $key => $value) {
33 $xmlrpc_value->data[$key] = xmlrpc_value($value);
34 }
35 }
36 if ($type == 'array') {
37 for ($i = 0, $j = count($xmlrpc_value->data); $i < $j; $i++) {
38 $xmlrpc_value->data[$i] = xmlrpc_value($xmlrpc_value->data[$i]);
39 }
40 }
41 return $xmlrpc_value;
42 }
43
44 /**
45 * Map PHP type to XML-RPC type.
46 *
47 * @param $xmlrpc_value
48 * Variable whose type should be mapped.
49 * @return
50 * XML-RPC type as string.
51 * @see
52 * http://www.xmlrpc.com/spec#scalars
53 */
54 function xmlrpc_value_calculate_type($xmlrpc_value) {
55 // http://www.php.net/gettype: Never use gettype() to test for a certain type [...] Instead, use the is_* functions.
56 if (is_bool($xmlrpc_value->data)) {
57 return 'boolean';
58 }
59 if (is_double($xmlrpc_value->data)) {
60 return 'double';
61 }
62 if (is_int($xmlrpc_value->data)) {
63 return 'int';
64 }
65 if (is_array($xmlrpc_value->data)) {
66 // empty or integer-indexed arrays are 'array', string-indexed arrays 'struct'
67 return empty($xmlrpc_value->data) || range(0, count($xmlrpc_value->data) - 1) === array_keys($xmlrpc_value->data) ? 'array' : 'struct';
68 }
69 if (is_object($xmlrpc_value->data)) {
70 if (isset($xmlrpc_value->data->is_date)) {
71 return 'date';
72 }
73 if (isset($xmlrpc_value->data->is_base64)) {
74 return 'base64';
75 }
76 $xmlrpc_value->data = get_object_vars($xmlrpc_value->data);
77 return 'struct';
78 }
79 // default
80 return 'string';
81 }
82
83 /**
84 * Generate XML representing the given value.
85 *
86 * @param $xmlrpc_value
87 * @return
88 * XML representation of value.
89 */
90 function xmlrpc_value_get_xml($xmlrpc_value) {
91 switch ($xmlrpc_value->type) {
92 case 'boolean':
93 return '<boolean>' . (($xmlrpc_value->data) ? '1' : '0') . '</boolean>';
94 break;
95 case 'int':
96 return '<int>' . $xmlrpc_value->data . '</int>';
97 break;
98 case 'double':
99 return '<double>' . $xmlrpc_value->data . '</double>';
100 break;
101 case 'string':
102 // Note: we don't escape apostrophes because of the many blogging clients
103 // that don't support numerical entities (and XML in general) properly.
104 return '<string>' . htmlspecialchars($xmlrpc_value->data) . '</string>';
105 break;
106 case 'array':
107 $return = '<array><data>' . "\n";
108 foreach ($xmlrpc_value->data as $item) {
109 $return .= ' <value>' . xmlrpc_value_get_xml($item) . "</value>\n";
110 }
111 $return .= '</data></array>';
112 return $return;
113 break;
114 case 'struct':
115 $return = '<struct>' . "\n";
116 foreach ($xmlrpc_value->data as $name => $value) {
117 $return .= " <member><name>" . check_plain($name) . "</name><value>";
118 $return .= xmlrpc_value_get_xml($value) . "</value></member>\n";
119 }
120 $return .= '</struct>';
121 return $return;
122 break;
123 case 'date':
124 return xmlrpc_date_get_xml($xmlrpc_value->data);
125 break;
126 case 'base64':
127 return xmlrpc_base64_get_xml($xmlrpc_value->data);
128 break;
129 }
130 return FALSE;
131 }
132
133 /**
134 * Construct an object representing an XML-RPC message.
135 *
136 * @param $message
137 * String containing XML as defined at http://www.xmlrpc.com/spec
138 * @return
139 * Object
140 */
141 function xmlrpc_message($message) {
142 $xmlrpc_message = new stdClass();
143 $xmlrpc_message->array_structs = array(); // The stack used to keep track of the current array/struct
144 $xmlrpc_message->array_structs_types = array(); // The stack used to keep track of if things are structs or array
145 $xmlrpc_message->current_struct_name = array(); // A stack as well
146 $xmlrpc_message->message = $message;
147 return $xmlrpc_message;
148 }
149
150 /**
151 * Parse an XML-RPC message. If parsing fails, the faultCode and faultString
152 * will be added to the message object.
153 *
154 * @param $xmlrpc_message
155 * Object generated by xmlrpc_message()
156 * @return
157 * TRUE if parsing succeeded; FALSE otherwise
158 */
159 function xmlrpc_message_parse($xmlrpc_message) {
160 // First remove the XML declaration
161 $xmlrpc_message->message = preg_replace('/<\?xml(.*)?\?' . '>/', '', $xmlrpc_message->message);
162 if (trim($xmlrpc_message->message) == '') {
163 return FALSE;
164 }
165 $xmlrpc_message->_parser = xml_parser_create();
166 // Set XML parser to take the case of tags into account.
167 xml_parser_set_option($xmlrpc_message->_parser, XML_OPTION_CASE_FOLDING, FALSE);
168 // Set XML parser callback functions
169 xml_set_element_handler($xmlrpc_message->_parser, 'xmlrpc_message_tag_open', 'xmlrpc_message_tag_close');
170 xml_set_character_data_handler($xmlrpc_message->_parser, 'xmlrpc_message_cdata');
171 xmlrpc_message_set($xmlrpc_message);
172 if (!xml_parse($xmlrpc_message->_parser, $xmlrpc_message->message)) {
173 return FALSE;
174 }
175 xml_parser_free($xmlrpc_message->_parser);
176 // Grab the error messages, if any
177 $xmlrpc_message = xmlrpc_message_get();
178 if ($xmlrpc_message->messagetype == 'fault') {
179 $xmlrpc_message->fault_code = $xmlrpc_message->params[0]['faultCode'];
180 $xmlrpc_message->fault_string = $xmlrpc_message->params[0]['faultString'];
181 }
182 return TRUE;
183 }
184
185 /**
186 * Store a copy of the $xmlrpc_message object temporarily.
187 *
188 * @param $value
189 * Object
190 * @return
191 * The most recently stored $xmlrpc_message
192 */
193 function xmlrpc_message_set($value = NULL) {
194 static $xmlrpc_message;
195 if ($value) {
196 $xmlrpc_message = $value;
197 }
198 return $xmlrpc_message;
199 }
200
201 function xmlrpc_message_get() {
202 return xmlrpc_message_set();
203 }
204
205 function xmlrpc_message_tag_open($parser, $tag, $attr) {
206 $xmlrpc_message = xmlrpc_message_get();
207 $xmlrpc_message->current_tag_contents = '';
208 $xmlrpc_message->last_open = $tag;
209 switch ($tag) {
210 case 'methodCall':
211 case 'methodResponse':
212 case 'fault':
213 $xmlrpc_message->messagetype = $tag;
214 break;
215 // Deal with stacks of arrays and structs
216 case 'data':
217 $xmlrpc_message->array_structs_types[] = 'array';
218 $xmlrpc_message->array_structs[] = array();
219 break;
220 case 'struct':
221 $xmlrpc_message->array_structs_types[] = 'struct';
222 $xmlrpc_message->array_structs[] = array();
223 break;
224 }
225 xmlrpc_message_set($xmlrpc_message);
226 }
227
228 function xmlrpc_message_cdata($parser, $cdata) {
229 $xmlrpc_message = xmlrpc_message_get();
230 $xmlrpc_message->current_tag_contents .= $cdata;
231 xmlrpc_message_set($xmlrpc_message);
232 }
233
234 function xmlrpc_message_tag_close($parser, $tag) {
235 $xmlrpc_message = xmlrpc_message_get();
236 $value_flag = FALSE;
237 switch ($tag) {
238 case 'int':
239 case 'i4':
240 $value = (int)trim($xmlrpc_message->current_tag_contents);
241 $value_flag = TRUE;
242 break;
243 case 'double':
244 $value = (double)trim($xmlrpc_message->current_tag_contents);
245 $value_flag = TRUE;
246 break;
247 case 'string':
248 $value = $xmlrpc_message->current_tag_contents;
249 $value_flag = TRUE;
250 break;
251 case 'dateTime.iso8601':
252 $value = xmlrpc_date(trim($xmlrpc_message->current_tag_contents));
253 // $value = $iso->getTimestamp();
254 $value_flag = TRUE;
255 break;
256 case 'value':
257 // If no type is indicated, the type is string
258 // We take special care for empty values
259 if (trim($xmlrpc_message->current_tag_contents) != '' || (isset($xmlrpc_message->last_open) && ($xmlrpc_message->last_open == 'value'))) {
260 $value = (string)$xmlrpc_message->current_tag_contents;
261 $value_flag = TRUE;
262 }
263 unset($xmlrpc_message->last_open);
264 break;
265 case 'boolean':
266 $value = (boolean)trim($xmlrpc_message->current_tag_contents);
267 $value_flag = TRUE;
268 break;
269 case 'base64':
270 $value = base64_decode(trim($xmlrpc_message->current_tag_contents));
271 $value_flag = TRUE;
272 break;
273 // Deal with stacks of arrays and structs
274 case 'data':
275 case 'struct':
276 $value = array_pop($xmlrpc_message->array_structs );
277 array_pop($xmlrpc_message->array_structs_types);
278 $value_flag = TRUE;
279 break;
280 case 'member':
281 array_pop($xmlrpc_message->current_struct_name);
282 break;
283 case 'name':
284 $xmlrpc_message->current_struct_name[] = trim($xmlrpc_message->current_tag_contents);
285 break;
286 case 'methodName':
287 $xmlrpc_message->methodname = trim($xmlrpc_message->current_tag_contents);
288 break;
289 }
290 if ($value_flag) {
291 if (count($xmlrpc_message->array_structs ) > 0) {
292 // Add value to struct or array
293 if ($xmlrpc_message->array_structs_types[count($xmlrpc_message->array_structs_types)-1] == 'struct') {
294 // Add to struct
295 $xmlrpc_message->array_structs [count($xmlrpc_message->array_structs )-1][$xmlrpc_message->current_struct_name[count($xmlrpc_message->current_struct_name)-1]] = $value;
296 }
297 else {
298 // Add to array
299 $xmlrpc_message->array_structs [count($xmlrpc_message->array_structs )-1][] = $value;
300 }
301 }
302 else {
303 // Just add as a parameter
304 $xmlrpc_message->params[] = $value;
305 }
306 }
307 if (!in_array($tag, array("data", "struct", "member"))) {
308 $xmlrpc_message->current_tag_contents = '';
309 }
310 xmlrpc_message_set($xmlrpc_message);
311 }
312
313 /**
314 * Construct an object representing an XML-RPC request
315 *
316 * @param $method
317 * The name of the method to be called
318 * @param $args
319 * An array of parameters to send with the method.
320 * @return
321 * Object
322 */
323 function xmlrpc_request($method, $args) {
324 $xmlrpc_request = new stdClass();
325 $xmlrpc_request->method = $method;
326 $xmlrpc_request->args = $args;
327 $xmlrpc_request->xml = <<<EOD
328 <?xml version="1.0"?>
329 <methodCall>
330 <methodName>{$xmlrpc_request->method}</methodName>
331 <params>
332
333 EOD;
334 foreach ($xmlrpc_request->args as $arg) {
335 $xmlrpc_request->xml .= '<param><value>';
336 $v = xmlrpc_value($arg);
337 $xmlrpc_request->xml .= xmlrpc_value_get_xml($v);
338 $xmlrpc_request->xml .= "</value></param>\n";
339 }
340 $xmlrpc_request->xml .= '</params></methodCall>';
341 return $xmlrpc_request;
342 }
343
344
345 function xmlrpc_error($code = NULL, $message = NULL) {
346 static $xmlrpc_error;
347 if (isset($code)) {
348 $xmlrpc_error = new stdClass();
349 $xmlrpc_error->is_error = TRUE;
350 $xmlrpc_error->code = $code;
351 $xmlrpc_error->message = $message;
352 }
353 return $xmlrpc_error;
354 }
355
356 function xmlrpc_error_get_xml($xmlrpc_error) {
357 return <<<EOD
358 <methodResponse>
359 <fault>
360 <value>
361 <struct>
362 <member>
363 <name>faultCode</name>
364 <value><int>{$xmlrpc_error->code}</int></value>
365 </member>
366 <member>
367 <name>faultString</name>
368 <value><string>{$xmlrpc_error->message}</string></value>
369 </member>
370 </struct>
371 </value>
372 </fault>
373 </methodResponse>
374
375 EOD;
376 }
377
378 function xmlrpc_date($time) {
379 $xmlrpc_date = new stdClass();
380 $xmlrpc_date->is_date = TRUE;
381 // $time can be a PHP timestamp or an ISO one
382 if (is_numeric($time)) {
383 $xmlrpc_date->year = gmdate('Y', $time);
384 $xmlrpc_date->month = gmdate('m', $time);
385 $xmlrpc_date->day = gmdate('d', $time);
386 $xmlrpc_date->hour = gmdate('H', $time);
387 $xmlrpc_date->minute = gmdate('i', $time);
388 $xmlrpc_date->second = gmdate('s', $time);
389 $xmlrpc_date->iso8601 = gmdate('Ymd\TH:i:s', $time);
390 }
391 else {
392 $xmlrpc_date->iso8601 = $time;
393 $time = str_replace(array('-', ':'), '', $time);
394 $xmlrpc_date->year = substr($time, 0, 4);
395 $xmlrpc_date->month = substr($time, 4, 2);
396 $xmlrpc_date->day = substr($time, 6, 2);
397 $xmlrpc_date->hour = substr($time, 9, 2);
398 $xmlrpc_date->minute = substr($time, 11, 2);
399 $xmlrpc_date->second = substr($time, 13, 2);
400 }
401 return $xmlrpc_date;
402 }
403
404 function xmlrpc_date_get_xml($xmlrpc_date) {
405 return '<dateTime.iso8601>' . $xmlrpc_date->year . $xmlrpc_date->month . $xmlrpc_date->day . 'T' . $xmlrpc_date->hour . ':' . $xmlrpc_date->minute . ':' . $xmlrpc_date->second . '</dateTime.iso8601>';
406 }
407
408 function xmlrpc_base64($data) {
409 $xmlrpc_base64 = new stdClass();
410 $xmlrpc_base64->is_base64 = TRUE;
411 $xmlrpc_base64->data = $data;
412 return $xmlrpc_base64;
413 }
414
415 function xmlrpc_base64_get_xml($xmlrpc_base64) {
416 return '<base64>' . base64_encode($xmlrpc_base64->data) . '</base64>';
417 }
418
419 /**
420 * Performs one or more XML-RPC request(s).
421 *
422 * @param $url
423 * An absolute URL of the XML-RPC endpoint.
424 * Example:
425 * http://www.example.com/xmlrpc.php
426 * @param ...
427 * For one request:
428 * The method name followed by a variable number of arguments to the method.
429 * For multiple requests (system.multicall):
430 * An array of call arrays. Each call array follows the pattern of the single
431 * request: method name followed by the arguments to the method.
432 * @return
433 * For one request:
434 * Either the return value of the method on success, or FALSE.
435 * If FALSE is returned, see xmlrpc_errno() and xmlrpc_error_msg().
436 * For multiple requests:
437 * An array of results. Each result will either be the result
438 * returned by the method called, or an xmlrpc_error object if the call
439 * failed. See xmlrpc_error().
440 */
441 function xmlrpc() {
442 $args = func_get_args();
443 $url = array_shift($args);
444 if (is_array($args[0])) {
445 $method = 'system.multicall';
446 $multicall_args = array();
447 foreach ($args[0] as $call) {
448 $multicall_args[] = array('methodName' => array_shift($call), 'params' => $call);
449 }
450 $args = array($multicall_args);
451 }
452 else {
453 $method = array_shift($args);
454 }
455 $xmlrpc_request = xmlrpc_request($method, $args);
456 $options = array(
457 'headers' => array('Content-Type' => 'text/xml'),
458 'method' => 'POST',
459 'data' => $xmlrpc_request->xml,
460 );
461 $result = drupal_http_request($url, $options);
462 if ($result->code != 200) {
463 xmlrpc_error($result->code, $result->error);
464 return FALSE;
465 }
466 $message = xmlrpc_message($result->data);
467 // Now parse what we've got back
468 if (!xmlrpc_message_parse($message)) {
469 // XML error
470 xmlrpc_error(-32700, t('Parse error. Not well formed'));
471 return FALSE;
472 }
473 // Is the message a fault?
474 if ($message->messagetype == 'fault') {
475 xmlrpc_error($message->fault_code, $message->fault_string);
476 return FALSE;
477 }
478 // Message must be OK
479 return $message->params[0];
480 }
481
482 /**
483 * Returns the last XML-RPC client error number
484 */
485 function xmlrpc_errno() {
486 $error = xmlrpc_error();
487 return ($error != NULL ? $error->code : NULL);
488 }
489
490 /**
491 * Returns the last XML-RPC client error message
492 */
493 function xmlrpc_error_msg() {
494 $error = xmlrpc_error();
495 return ($error != NULL ? $error->message : NULL);
496 }
497

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.