[ Index ]

PHP Cross Reference of Textpattern 4.0.8

title

Body

[close]

/textpattern/lib/ -> IXRClass.php (source)

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


Generated: Thu May 21 23:03:01 2009 Cross-referenced by PHPXref 0.7