PageRenderTime 77ms CodeModel.GetById 19ms RepoModel.GetById 2ms app.codeStats 0ms

/www/libs/wp/IXR.php

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