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