Make WordPress Core

source: trunk/wp-includes/class-IXR.php @ 1608

Last change on this file since 1608 was 1564, checked in by michelvaldrighi, 21 years ago

added very basic timezone support to IXR_Date

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