home *** CD-ROM | disk | FTP | other *** search
/ MacAddict 108 / MacAddict108.iso / Software / Internet & Communication / WordPress 1.5.1.dmg / wordpress / wp-includes / class-IXR.php next >
Encoding:
PHP Script  |  2005-01-24  |  27.5 KB  |  828 lines

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