class.IXR_Library.php 28 KB

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