Wordpress 2.6.2-scripts
[autoinstalls/wordpress.git] / wp-includes / class-IXR.php
1 <?php
2 /**
3  * IXR - The Inutio XML-RPC Library
4  *
5  * @package IXR
6  * @since 1.5
7  *
8  * @copyright Incutio Ltd 2002-2005
9  * @version 1.7 (beta) 23rd May 2005
10  * @author Simon Willison
11  * @link http://scripts.incutio.com/xmlrpc/ Site
12  * @link http://scripts.incutio.com/xmlrpc/manual.php Manual
13  * @license BSD License http://www.opensource.org/licenses/bsd-license.php
14  */
15
16 /**
17  * IXR_Value
18  *
19  * @package IXR
20  * @since 1.5
21  */
22 class IXR_Value {
23     var $data;
24     var $type;
25     function IXR_Value ($data, $type = false) {
26         $this->data = $data;
27         if (!$type) {
28             $type = $this->calculateType();
29         }
30         $this->type = $type;
31         if ($type == 'struct') {
32             /* Turn all the values in the array in to new IXR_Value objects */
33             foreach ($this->data as $key => $value) {
34                 $this->data[$key] = new IXR_Value($value);
35             }
36         }
37         if ($type == 'array') {
38             for ($i = 0, $j = count($this->data); $i < $j; $i++) {
39                 $this->data[$i] = new IXR_Value($this->data[$i]);
40             }
41         }
42     }
43     function calculateType() {
44         if ($this->data === true || $this->data === false) {
45             return 'boolean';
46         }
47         if (is_integer($this->data)) {
48             return 'int';
49         }
50         if (is_double($this->data)) {
51             return 'double';
52         }
53         // Deal with IXR object types base64 and date
54         if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
55             return 'date';
56         }
57         if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
58             return 'base64';
59         }
60         // If it is a normal PHP object convert it in to a struct
61         if (is_object($this->data)) {
62
63             $this->data = get_object_vars($this->data);
64             return 'struct';
65         }
66         if (!is_array($this->data)) {
67             return 'string';
68         }
69         /* We have an array - is it an array or a struct ? */
70         if ($this->isStruct($this->data)) {
71             return 'struct';
72         } else {
73             return 'array';
74         }
75     }
76     function getXml() {
77         /* Return XML for this value */
78         switch ($this->type) {
79             case 'boolean':
80                 return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
81                 break;
82             case 'int':
83                 return '<int>'.$this->data.'</int>';
84                 break;
85             case 'double':
86                 return '<double>'.$this->data.'</double>';
87                 break;
88             case 'string':
89                 return '<string>'.htmlspecialchars($this->data).'</string>';
90                 break;
91             case 'array':
92                 $return = '<array><data>'."\n";
93                 foreach ($this->data as $item) {
94                     $return .= '  <value>'.$item->getXml()."</value>\n";
95                 }
96                 $return .= '</data></array>';
97                 return $return;
98                 break;
99             case 'struct':
100                 $return = '<struct>'."\n";
101                 foreach ($this->data as $name => $value) {
102                                         $name = htmlspecialchars($name);
103                     $return .= "  <member><name>$name</name><value>";
104                     $return .= $value->getXml()."</value></member>\n";
105                 }
106                 $return .= '</struct>';
107                 return $return;
108                 break;
109             case 'date':
110             case 'base64':
111                 return $this->data->getXml();
112                 break;
113         }
114         return false;
115     }
116     function isStruct($array) {
117         /* Nasty function to check if an array is a struct or not */
118         $expected = 0;
119         foreach ($array as $key => $value) {
120             if ((string)$key != (string)$expected) {
121                 return true;
122             }
123             $expected++;
124         }
125         return false;
126     }
127 }
128
129 /**
130  * IXR_Message
131  *
132  * @package IXR
133  * @since 1.5
134  */
135 class IXR_Message {
136     var $message;
137     var $messageType;  // methodCall / methodResponse / fault
138     var $faultCode;
139     var $faultString;
140     var $methodName;
141     var $params;
142     // Current variable stacks
143     var $_arraystructs = array();   // The stack used to keep track of the current array/struct
144     var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
145     var $_currentStructName = array();  // A stack as well
146     var $_param;
147     var $_value;
148     var $_currentTag;
149     var $_currentTagContents;
150     // The XML parser
151     var $_parser;
152     function IXR_Message ($message) {
153         $this->message = $message;
154     }
155     function parse() {
156         // first remove the XML declaration
157         $this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message);
158         if (trim($this->message) == '') {
159             return false;
160         }
161         $this->_parser = xml_parser_create();
162         // Set XML parser to take the case of tags in to account
163         xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
164         // Set XML parser callback functions
165         xml_set_object($this->_parser, $this);
166         xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
167         xml_set_character_data_handler($this->_parser, 'cdata');
168         if (!xml_parse($this->_parser, $this->message)) {
169             /* die(sprintf('XML error: %s at line %d',
170                 xml_error_string(xml_get_error_code($this->_parser)),
171                 xml_get_current_line_number($this->_parser))); */
172             return false;
173         }
174         xml_parser_free($this->_parser);
175         // Grab the error messages, if any
176         if ($this->messageType == 'fault') {
177             $this->faultCode = $this->params[0]['faultCode'];
178             $this->faultString = $this->params[0]['faultString'];
179         }
180         return true;
181     }
182     function tag_open($parser, $tag, $attr) {
183                 $this->_currentTagContents = '';
184         $this->currentTag = $tag;
185         switch($tag) {
186             case 'methodCall':
187             case 'methodResponse':
188             case 'fault':
189                 $this->messageType = $tag;
190                 break;
191             /* Deal with stacks of arrays and structs */
192             case 'data':    // data is to all intents and puposes more interesting than array
193                 $this->_arraystructstypes[] = 'array';
194                 $this->_arraystructs[] = array();
195                 break;
196             case 'struct':
197                 $this->_arraystructstypes[] = 'struct';
198                 $this->_arraystructs[] = array();
199                 break;
200         }
201     }
202     function cdata($parser, $cdata) {
203         $this->_currentTagContents .= $cdata;
204     }
205     function tag_close($parser, $tag) {
206         $valueFlag = false;
207         switch($tag) {
208             case 'int':
209             case 'i4':
210                 $value = (int) trim($this->_currentTagContents);
211                 $valueFlag = true;
212                 break;
213             case 'double':
214                 $value = (double) trim($this->_currentTagContents);
215                 $valueFlag = true;
216                 break;
217             case 'string':
218                 $value = $this->_currentTagContents;
219                 $valueFlag = true;
220                 break;
221             case 'dateTime.iso8601':
222                 $value = new IXR_Date(trim($this->_currentTagContents));
223                 // $value = $iso->getTimestamp();
224                 $valueFlag = true;
225                 break;
226             case 'value':
227                 // "If no type is indicated, the type is string."
228                 if (trim($this->_currentTagContents) != '') {
229                     $value = (string)$this->_currentTagContents;
230                     $valueFlag = true;
231                 }
232                 break;
233             case 'boolean':
234                 $value = (boolean) trim($this->_currentTagContents);
235                 $valueFlag = true;
236                 break;
237             case 'base64':
238                 $value = base64_decode( trim( $this->_currentTagContents ) );
239                 $valueFlag = true;
240                 break;
241             /* Deal with stacks of arrays and structs */
242             case 'data':
243             case 'struct':
244                 $value = array_pop($this->_arraystructs);
245                 array_pop($this->_arraystructstypes);
246                 $valueFlag = true;
247                 break;
248             case 'member':
249                 array_pop($this->_currentStructName);
250                 break;
251             case 'name':
252                 $this->_currentStructName[] = trim($this->_currentTagContents);
253                 break;
254             case 'methodName':
255                 $this->methodName = trim($this->_currentTagContents);
256                 break;
257         }
258         if ($valueFlag) {
259             if (count($this->_arraystructs) > 0) {
260                 // Add value to struct or array
261                 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
262                     // Add to struct
263                     $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
264                 } else {
265                     // Add to array
266                     $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
267                 }
268             } else {
269                 // Just add as a paramater
270                 $this->params[] = $value;
271             }
272         }
273                 $this->_currentTagContents = '';
274     }
275 }
276
277 /**
278  * IXR_Server
279  *
280  * @package IXR
281  * @since 1.5
282  */
283 class IXR_Server {
284     var $data;
285     var $callbacks = array();
286     var $message;
287     var $capabilities;
288     function IXR_Server($callbacks = false, $data = false) {
289         $this->setCapabilities();
290         if ($callbacks) {
291             $this->callbacks = $callbacks;
292         }
293         $this->setCallbacks();
294         $this->serve($data);
295     }
296     function serve($data = false) {
297         if (!$data) {
298             global $HTTP_RAW_POST_DATA;
299             if (!$HTTP_RAW_POST_DATA) {
300                die('XML-RPC server accepts POST requests only.');
301             }
302             $data = $HTTP_RAW_POST_DATA;
303         }
304         $this->message = new IXR_Message($data);
305         if (!$this->message->parse()) {
306             $this->error(-32700, 'parse error. not well formed');
307         }
308         if ($this->message->messageType != 'methodCall') {
309             $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
310         }
311         $result = $this->call($this->message->methodName, $this->message->params);
312         // Is the result an error?
313         if (is_a($result, 'IXR_Error')) {
314             $this->error($result);
315         }
316         // Encode the result
317         $r = new IXR_Value($result);
318         $resultxml = $r->getXml();
319         // Create the XML
320         $xml = <<<EOD
321 <methodResponse>
322   <params>
323     <param>
324       <value>
325         $resultxml
326       </value>
327     </param>
328   </params>
329 </methodResponse>
330
331 EOD;
332         // Send it
333         $this->output($xml);
334     }
335     function call($methodname, $args) {
336         if (!$this->hasMethod($methodname)) {
337             return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.');
338         }
339         $method = $this->callbacks[$methodname];
340         // Perform the callback and send the response
341         if (count($args) == 1) {
342             // If only one paramater just send that instead of the whole array
343             $args = $args[0];
344         }
345         // Are we dealing with a function or a method?
346         if (substr($method, 0, 5) == 'this:') {
347             // It's a class method - check it exists
348             $method = substr($method, 5);
349             if (!method_exists($this, $method)) {
350                 return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.');
351             }
352             // Call the method
353             $result = $this->$method($args);
354         } else {
355             // It's a function - does it exist?
356             if (is_array($method)) {
357                 if (!method_exists($method[0], $method[1])) {
358                 return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.');
359                 }
360             } else if (!function_exists($method)) {
361                 return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.');
362             }
363             // Call the function
364             $result = call_user_func($method, $args);
365         }
366         return $result;
367     }
368
369     function error($error, $message = false) {
370         // Accepts either an error object or an error code and message
371         if ($message && !is_object($error)) {
372             $error = new IXR_Error($error, $message);
373         }
374         $this->output($error->getXml());
375     }
376     function output($xml) {
377         $xml = '<?xml version="1.0"?>'."\n".$xml;
378         $length = strlen($xml);
379         header('Connection: close');
380         header('Content-Length: '.$length);
381         header('Content-Type: text/xml');
382         header('Date: '.date('r'));
383         echo $xml;
384         exit;
385     }
386     function hasMethod($method) {
387         return in_array($method, array_keys($this->callbacks));
388     }
389     function setCapabilities() {
390         // Initialises capabilities array
391         $this->capabilities = array(
392             'xmlrpc' => array(
393                 'specUrl' => 'http://www.xmlrpc.com/spec',
394                 'specVersion' => 1
395             ),
396             'faults_interop' => array(
397                 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
398                 'specVersion' => 20010516
399             ),
400             'system.multicall' => array(
401                 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
402                 'specVersion' => 1
403             ),
404         );
405     }
406     function getCapabilities($args) {
407         return $this->capabilities;
408     }
409     function setCallbacks() {
410         $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
411         $this->callbacks['system.listMethods'] = 'this:listMethods';
412         $this->callbacks['system.multicall'] = 'this:multiCall';
413     }
414     function listMethods($args) {
415         // Returns a list of methods - uses array_reverse to ensure user defined
416         // methods are listed before server defined methods
417         return array_reverse(array_keys($this->callbacks));
418     }
419     function multiCall($methodcalls) {
420         // See http://www.xmlrpc.com/discuss/msgReader$1208
421         $return = array();
422         foreach ($methodcalls as $call) {
423             $method = $call['methodName'];
424             $params = $call['params'];
425             if ($method == 'system.multicall') {
426                 $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
427             } else {
428                 $result = $this->call($method, $params);
429             }
430             if (is_a($result, 'IXR_Error')) {
431                 $return[] = array(
432                     'faultCode' => $result->code,
433                     'faultString' => $result->message
434                 );
435             } else {
436                 $return[] = array($result);
437             }
438         }
439         return $return;
440     }
441 }
442
443 /**
444  * IXR_Request
445  *
446  * @package IXR
447  * @since 1.5
448  */
449 class IXR_Request {
450     var $method;
451     var $args;
452     var $xml;
453     function IXR_Request($method, $args) {
454         $this->method = $method;
455         $this->args = $args;
456         $this->xml = <<<EOD
457 <?xml version="1.0"?>
458 <methodCall>
459 <methodName>{$this->method}</methodName>
460 <params>
461
462 EOD;
463         foreach ($this->args as $arg) {
464             $this->xml .= '<param><value>';
465             $v = new IXR_Value($arg);
466             $this->xml .= $v->getXml();
467             $this->xml .= "</value></param>\n";
468         }
469         $this->xml .= '</params></methodCall>';
470     }
471     function getLength() {
472         return strlen($this->xml);
473     }
474     function getXml() {
475         return $this->xml;
476     }
477 }
478
479 /**
480  * IXR_Client
481  *
482  * @package IXR
483  * @since 1.5
484  */
485 class IXR_Client {
486     var $server;
487     var $port;
488     var $path;
489     var $useragent;
490     var $response;
491     var $message = false;
492     var $debug = false;
493         var $timeout;
494     // Storage place for an error message
495     var $error = false;
496     function IXR_Client($server, $path = false, $port = 80, $timeout = false) {
497         if (!$path) {
498             // Assume we have been given a URL instead
499             $bits = parse_url($server);
500             $this->server = $bits['host'];
501             $this->port = isset($bits['port']) ? $bits['port'] : 80;
502             $this->path = isset($bits['path']) ? $bits['path'] : '/';
503             // Make absolutely sure we have a path
504             if (!$this->path) {
505                 $this->path = '/';
506             }
507         } else {
508             $this->server = $server;
509             $this->path = $path;
510             $this->port = $port;
511         }
512         $this->useragent = 'Incutio XML-RPC';
513                 $this->timeout = $timeout;
514     }
515     function query() {
516         $args = func_get_args();
517         $method = array_shift($args);
518         $request = new IXR_Request($method, $args);
519         $length = $request->getLength();
520         $xml = $request->getXml();
521         $r = "\r\n";
522         $request  = "POST {$this->path} HTTP/1.0$r";
523         $request .= "Host: {$this->server}$r";
524         $request .= "Content-Type: text/xml$r";
525         $request .= "User-Agent: {$this->useragent}$r";
526         $request .= "Content-length: {$length}$r$r";
527         $request .= $xml;
528         // Now send the request
529         if ($this->debug) {
530             echo '<pre>'.htmlspecialchars($request)."\n</pre>\n\n";
531         }
532         if ($this->timeout) {
533             $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);
534         } else {
535             $fp = @fsockopen($this->server, $this->port, $errno, $errstr);
536         }
537         if (!$fp) {
538             $this->error = new IXR_Error(-32300, "transport error - could not open socket: $errno $errstr");
539             return false;
540         }
541         fputs($fp, $request);
542         $contents = '';
543         $gotFirstLine = false;
544         $gettingHeaders = true;
545         while (!feof($fp)) {
546             $line = fgets($fp, 4096);
547             if (!$gotFirstLine) {
548                 // Check line for '200'
549                 if (strstr($line, '200') === false) {
550                     $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
551                     return false;
552                 }
553                 $gotFirstLine = true;
554             }
555             if (trim($line) == '') {
556                 $gettingHeaders = false;
557             }
558             if (!$gettingHeaders) {
559                 $contents .= trim($line)."\n";
560             }
561         }
562         if ($this->debug) {
563             echo '<pre>'.htmlspecialchars($contents)."\n</pre>\n\n";
564         }
565         // Now parse what we've got back
566         $this->message = new IXR_Message($contents);
567         if (!$this->message->parse()) {
568             // XML error
569             $this->error = new IXR_Error(-32700, 'parse error. not well formed');
570             return false;
571         }
572         // Is the message a fault?
573         if ($this->message->messageType == 'fault') {
574             $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
575             return false;
576         }
577         // Message must be OK
578         return true;
579     }
580     function getResponse() {
581         // methodResponses can only have one param - return that
582         return $this->message->params[0];
583     }
584     function isError() {
585         return (is_object($this->error));
586     }
587     function getErrorCode() {
588         return $this->error->code;
589     }
590     function getErrorMessage() {
591         return $this->error->message;
592     }
593 }
594
595 /**
596  * IXR_Error
597  *
598  * @package IXR
599  * @since 1.5
600  */
601 class IXR_Error {
602     var $code;
603     var $message;
604     function IXR_Error($code, $message) {
605         $this->code = $code;
606         $this->message = htmlspecialchars($message);
607     }
608     function getXml() {
609         $xml = <<<EOD
610 <methodResponse>
611   <fault>
612     <value>
613       <struct>
614         <member>
615           <name>faultCode</name>
616           <value><int>{$this->code}</int></value>
617         </member>
618         <member>
619           <name>faultString</name>
620           <value><string>{$this->message}</string></value>
621         </member>
622       </struct>
623     </value>
624   </fault>
625 </methodResponse>
626
627 EOD;
628         return $xml;
629     }
630 }
631
632 /**
633  * IXR_Date
634  *
635  * @package IXR
636  * @since 1.5
637  */
638 class IXR_Date {
639     var $year;
640     var $month;
641     var $day;
642     var $hour;
643     var $minute;
644     var $second;
645     function IXR_Date($time) {
646         // $time can be a PHP timestamp or an ISO one
647         if (is_numeric($time)) {
648             $this->parseTimestamp($time);
649         } else {
650             $this->parseIso($time);
651         }
652     }
653     function parseTimestamp($timestamp) {
654         $this->year = date('Y', $timestamp);
655         $this->month = date('m', $timestamp);
656         $this->day = date('d', $timestamp);
657         $this->hour = date('H', $timestamp);
658         $this->minute = date('i', $timestamp);
659         $this->second = date('s', $timestamp);
660     }
661     function parseIso($iso) {
662         $this->year = substr($iso, 0, 4);
663         $this->month = substr($iso, 4, 2);
664         $this->day = substr($iso, 6, 2);
665         $this->hour = substr($iso, 9, 2);
666         $this->minute = substr($iso, 12, 2);
667         $this->second = substr($iso, 15, 2);
668         $this->timezone = substr($iso, 17);
669     }
670     function getIso() {
671         return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone;
672     }
673     function getXml() {
674         return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
675     }
676     function getTimestamp() {
677         return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
678     }
679 }
680
681 /**
682  * IXR_Base64
683  *
684  * @package IXR
685  * @since 1.5
686  */
687 class IXR_Base64 {
688     var $data;
689     function IXR_Base64($data) {
690         $this->data = $data;
691     }
692     function getXml() {
693         return '<base64>'.base64_encode($this->data).'</base64>';
694     }
695 }
696
697 /**
698  * IXR_IntrospectionServer
699  *
700  * @package IXR
701  * @since 1.5
702  */
703 class IXR_IntrospectionServer extends IXR_Server {
704     var $signatures;
705     var $help;
706     function IXR_IntrospectionServer() {
707         $this->setCallbacks();
708         $this->setCapabilities();
709         $this->capabilities['introspection'] = array(
710             'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
711             'specVersion' => 1
712         );
713         $this->addCallback(
714             'system.methodSignature',
715             'this:methodSignature',
716             array('array', 'string'),
717             'Returns an array describing the return type and required parameters of a method'
718         );
719         $this->addCallback(
720             'system.getCapabilities',
721             'this:getCapabilities',
722             array('struct'),
723             'Returns a struct describing the XML-RPC specifications supported by this server'
724         );
725         $this->addCallback(
726             'system.listMethods',
727             'this:listMethods',
728             array('array'),
729             'Returns an array of available methods on this server'
730         );
731         $this->addCallback(
732             'system.methodHelp',
733             'this:methodHelp',
734             array('string', 'string'),
735             'Returns a documentation string for the specified method'
736         );
737     }
738     function addCallback($method, $callback, $args, $help) {
739         $this->callbacks[$method] = $callback;
740         $this->signatures[$method] = $args;
741         $this->help[$method] = $help;
742     }
743     function call($methodname, $args) {
744         // Make sure it's in an array
745         if ($args && !is_array($args)) {
746             $args = array($args);
747         }
748         // Over-rides default call method, adds signature check
749         if (!$this->hasMethod($methodname)) {
750             return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.');
751         }
752         $method = $this->callbacks[$methodname];
753         $signature = $this->signatures[$methodname];
754         $returnType = array_shift($signature);
755         // Check the number of arguments
756         if (count($args) != count($signature)) {
757             return new IXR_Error(-32602, 'server error. wrong number of method parameters');
758         }
759         // Check the argument types
760         $ok = true;
761         $argsbackup = $args;
762         for ($i = 0, $j = count($args); $i < $j; $i++) {
763             $arg = array_shift($args);
764             $type = array_shift($signature);
765             switch ($type) {
766                 case 'int':
767                 case 'i4':
768                     if (is_array($arg) || !is_int($arg)) {
769                         $ok = false;
770                     }
771                     break;
772                 case 'base64':
773                 case 'string':
774                     if (!is_string($arg)) {
775                         $ok = false;
776                     }
777                     break;
778                 case 'boolean':
779                     if ($arg !== false && $arg !== true) {
780                         $ok = false;
781                     }
782                     break;
783                 case 'float':
784                 case 'double':
785                     if (!is_float($arg)) {
786                         $ok = false;
787                     }
788                     break;
789                 case 'date':
790                 case 'dateTime.iso8601':
791                     if (!is_a($arg, 'IXR_Date')) {
792                         $ok = false;
793                     }
794                     break;
795             }
796             if (!$ok) {
797                 return new IXR_Error(-32602, 'server error. invalid method parameters');
798             }
799         }
800         // It passed the test - run the "real" method call
801         return parent::call($methodname, $argsbackup);
802     }
803     function methodSignature($method) {
804         if (!$this->hasMethod($method)) {
805             return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
806         }
807         // We should be returning an array of types
808         $types = $this->signatures[$method];
809         $return = array();
810         foreach ($types as $type) {
811             switch ($type) {
812                 case 'string':
813                     $return[] = 'string';
814                     break;
815                 case 'int':
816                 case 'i4':
817                     $return[] = 42;
818                     break;
819                 case 'double':
820                     $return[] = 3.1415;
821                     break;
822                 case 'dateTime.iso8601':
823                     $return[] = new IXR_Date(time());
824                     break;
825                 case 'boolean':
826                     $return[] = true;
827                     break;
828                 case 'base64':
829                     $return[] = new IXR_Base64('base64');
830                     break;
831                 case 'array':
832                     $return[] = array('array');
833                     break;
834                 case 'struct':
835                     $return[] = array('struct' => 'struct');
836                     break;
837             }
838         }
839         return $return;
840     }
841     function methodHelp($method) {
842         return $this->help[$method];
843     }
844 }
845
846 /**
847  * IXR_ClientMulticall
848  *
849  * @package IXR
850  * @since 1.5
851  */
852 class IXR_ClientMulticall extends IXR_Client {
853     var $calls = array();
854     function IXR_ClientMulticall($server, $path = false, $port = 80) {
855         parent::IXR_Client($server, $path, $port);
856         $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
857     }
858     function addCall() {
859         $args = func_get_args();
860         $methodName = array_shift($args);
861         $struct = array(
862             'methodName' => $methodName,
863             'params' => $args
864         );
865         $this->calls[] = $struct;
866     }
867     function query() {
868         // Prepare multicall, then call the parent::query() method
869         return parent::query('system.multicall', $this->calls);
870     }
871 }
872
873 ?>