PageRenderTime 67ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/public/include/jsonrpc.inc.php

https://github.com/usrio/php-mmcfe-ng
PHP | 1583 lines | 1523 code | 8 blank | 52 comment | 8 complexity | bbd58dc495b6ced6c63f6e86d618c359 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. // Make sure we are called from index.php
  3. if (!defined('SECURITY'))
  4. die('Hacking attempt');
  5. /**
  6. * JSON extension to the PHP-XMLRPC lib
  7. *
  8. * For more info see:
  9. * http://www.json.org/
  10. * http://json-rpc.org/
  11. *
  12. * @version $Id: jsonrpc.inc,v 1.36 2009/02/05 09:50:59 ggiunta Exp $
  13. * @author Gaetano Giunta
  14. * @copyright (c) 2005-2009 G. Giunta
  15. * @license code licensed under the BSD License: http://phpxmlrpc.sourceforge.net/license.txt
  16. *
  17. * @todo the JSON proposed RFC states that when making json calls, we should
  18. * specify an 'accep: application/json' http header. Currently we either
  19. * do not otuput an 'accept' header or specify 'any' (in curl mode)
  20. **/
  21. // requires: xmlrpc.inc 2.0 or later
  22. // Note: the json spec omits \v, but it is present in ECMA-262, so we allow it
  23. $GLOBALS['ecma262_entities'] = array(
  24. 'b' => chr(8),
  25. 'f' => chr(12),
  26. 'n' => chr(10),
  27. 'r' => chr(13),
  28. 't' => chr(9),
  29. 'v' => chr(11)
  30. );
  31. // tables used for transcoding different charsets into us-ascii javascript
  32. $GLOBALS['ecma262_iso88591_Entities']=array();
  33. $GLOBALS['ecma262_iso88591_Entities']['in'] = array();
  34. $GLOBALS['ecma262_iso88591_Entities']['out'] = array();
  35. for ($i = 0; $i < 32; $i++)
  36. {
  37. $GLOBALS['ecma262_iso88591_Entities']['in'][] = chr($i);
  38. $GLOBALS['ecma262_iso88591_Entities']['out'][] = sprintf('\u%\'04x', $i);
  39. }
  40. for ($i = 160; $i < 256; $i++)
  41. {
  42. $GLOBALS['ecma262_iso88591_Entities']['in'][] = chr($i);
  43. $GLOBALS['ecma262_iso88591_Entities']['out'][] = sprintf('\u%\'04x', $i);
  44. }
  45. /**
  46. * Encode php strings to valid JSON unicode representation.
  47. * All chars outside ASCII range are converted to \uXXXX for maximum portability.
  48. * @param string $data (in iso-8859-1 charset by default)
  49. * @param string charset of source string, defaults to $GLOBALS['xmlrpc_internalencoding']
  50. * @param string charset of the encoded string, defaults to ASCII for maximum interoperabilty
  51. * @return string
  52. * @access private
  53. * @todo add support for UTF-16 as destination charset instead of ASCII
  54. * @todo add support for UTF-16 as source charset
  55. */
  56. function json_encode_entities($data, $src_encoding='', $dest_encoding='')
  57. {
  58. if ($src_encoding == '')
  59. {
  60. // lame, but we know no better...
  61. $src_encoding = $GLOBALS['xmlrpc_internalencoding'];
  62. }
  63. switch(strtoupper($src_encoding.'_'.$dest_encoding))
  64. {
  65. case 'ISO-8859-1_':
  66. case 'ISO-8859-1_US-ASCII':
  67. $escaped_data = str_replace(array('\\', '"', '/', "\t", "\n", "\r", chr(8), chr(11), chr(12)), array('\\\\', '\"', '\/', '\t', '\n', '\r', '\b', '\v', '\f'), $data);
  68. $escaped_data = str_replace($GLOBALS['ecma262_iso88591_Entities']['in'], $GLOBALS['ecma262_iso88591_Entities']['out'], $escaped_data);
  69. break;
  70. case 'ISO-8859-1_UTF-8':
  71. $escaped_data = str_replace(array('\\', '"', '/', "\t", "\n", "\r", chr(8), chr(11), chr(12)), array('\\\\', '\"', '\/', '\t', '\n', '\r', '\b', '\v', '\f'), $data);
  72. $escaped_data = utf8_encode($escaped_data);
  73. break;
  74. case 'ISO-8859-1_ISO-8859-1':
  75. case 'US-ASCII_US-ASCII':
  76. case 'US-ASCII_UTF-8':
  77. case 'US-ASCII_':
  78. case 'US-ASCII_ISO-8859-1':
  79. case 'UTF-8_UTF-8':
  80. $escaped_data = str_replace(array('\\', '"', '/', "\t", "\n", "\r", chr(8), chr(11), chr(12)), array('\\\\', '\"', '\/', '\t', '\n', '\r', '\b', '\v', '\f'), $data);
  81. break;
  82. case 'UTF-8_':
  83. case 'UTF-8_US-ASCII':
  84. case 'UTF-8_ISO-8859-1':
  85. // NB: this will choke on invalid UTF-8, going most likely beyond EOF
  86. $escaped_data = "";
  87. // be kind to users creating string jsonrpcvals out of different php types
  88. $data = (string) $data;
  89. $ns = strlen ($data);
  90. for ($nn = 0; $nn < $ns; $nn++)
  91. {
  92. $ch = $data[$nn];
  93. $ii = ord($ch);
  94. //1 7 0bbbbbbb (127)
  95. if ($ii < 128)
  96. {
  97. /// @todo shall we replace this with a (supposedly) faster str_replace?
  98. switch($ii){
  99. case 8:
  100. $escaped_data .= '\b';
  101. break;
  102. case 9:
  103. $escaped_data .= '\t';
  104. break;
  105. case 10:
  106. $escaped_data .= '\n';
  107. break;
  108. case 11:
  109. $escaped_data .= '\v';
  110. break;
  111. case 12:
  112. $escaped_data .= '\f';
  113. break;
  114. case 13:
  115. $escaped_data .= '\r';
  116. break;
  117. case 34:
  118. $escaped_data .= '\"';
  119. break;
  120. case 47:
  121. $escaped_data .= '\/';
  122. break;
  123. case 92:
  124. $escaped_data .= '\\\\';
  125. break;
  126. default:
  127. $escaped_data .= $ch;
  128. } // switch
  129. }
  130. //2 11 110bbbbb 10bbbbbb (2047)
  131. else if ($ii>>5 == 6)
  132. {
  133. $b1 = ($ii & 31);
  134. $ii = ord($data[$nn+1]);
  135. $b2 = ($ii & 63);
  136. $ii = ($b1 * 64) + $b2;
  137. $ent = sprintf ('\u%\'04x', $ii);
  138. $escaped_data .= $ent;
  139. $nn += 1;
  140. }
  141. //3 16 1110bbbb 10bbbbbb 10bbbbbb
  142. else if ($ii>>4 == 14)
  143. {
  144. $b1 = ($ii & 15);
  145. $ii = ord($data[$nn+1]);
  146. $b2 = ($ii & 63);
  147. $ii = ord($data[$nn+2]);
  148. $b3 = ($ii & 63);
  149. $ii = ((($b1 * 64) + $b2) * 64) + $b3;
  150. $ent = sprintf ('\u%\'04x', $ii);
  151. $escaped_data .= $ent;
  152. $nn += 2;
  153. }
  154. //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
  155. else if ($ii>>3 == 30)
  156. {
  157. $b1 = ($ii & 7);
  158. $ii = ord($data[$nn+1]);
  159. $b2 = ($ii & 63);
  160. $ii = ord($data[$nn+2]);
  161. $b3 = ($ii & 63);
  162. $ii = ord($data[$nn+3]);
  163. $b4 = ($ii & 63);
  164. $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
  165. $ent = sprintf ('\u%\'04x', $ii);
  166. $escaped_data .= $ent;
  167. $nn += 3;
  168. }
  169. }
  170. break;
  171. default:
  172. $escaped_data = '';
  173. error_log("Converting from $src_encoding to $dest_encoding: not supported...");
  174. } // switch
  175. return $escaped_data;
  176. /*
  177. $length = strlen($data);
  178. $escapeddata = "";
  179. for($position = 0; $position < $length; $position++)
  180. {
  181. $character = substr($data, $position, 1);
  182. $code = ord($character);
  183. switch($code)
  184. {
  185. case 8:
  186. $character = '\b';
  187. break;
  188. case 9:
  189. $character = '\t';
  190. break;
  191. case 10:
  192. $character = '\n';
  193. break;
  194. case 12:
  195. $character = '\f';
  196. break;
  197. case 13:
  198. $character = '\r';
  199. break;
  200. case 34:
  201. $character = '\"';
  202. break;
  203. case 47:
  204. $character = '\/';
  205. break;
  206. case 92:
  207. $character = '\\\\';
  208. break;
  209. default:
  210. if($code < 32 || $code > 159)
  211. {
  212. $character = "\u".str_pad(dechex($code), 4, '0', STR_PAD_LEFT);
  213. }
  214. break;
  215. }
  216. $escapeddata .= $character;
  217. }
  218. return $escapeddata;
  219. */
  220. }
  221. /**
  222. * Parse a JSON string.
  223. * NB: try to accept any valid string according to ECMA, even though the JSON
  224. * spec is much more strict.
  225. * Assumes input is UTF-8...
  226. * @param string $data a json string
  227. * @param bool $return_phpvals if true, do not rebuild jsonrpcval objects, but plain php values
  228. * @param string $src_encoding
  229. * @param string $dest_encoding
  230. * @return bool
  231. * @access private
  232. * @todo support for other source encodings than UTF-8
  233. * @todo optimization creep: build elements of arrays/objects asap instead of counting chars many times
  234. * @todo we should move to xmlrpc_defencoding and xmlrpc_internalencoding as predefined values, but it would make this even slower...
  235. * Maybe just move those two parameters outside of here into callers?
  236. *
  237. * @bug parsing of "[1]// comment here" works in ie/ff, but not here
  238. * @bug parsing of "[.1]" works in ie/ff, but not here
  239. * @bug parsing of "[01]" works in ie/ff, but not here
  240. * @bug parsing of "{true:1}" works here, but not in ie/ff
  241. * @bug parsing of "{a b:1}" works here, but not in ie/ff
  242. */
  243. function json_parse($data, $return_phpvals=false, $src_encoding='UTF-8', $dest_encoding='ISO-8859-1')
  244. {
  245. // optimization creep: this is quite costly. Is there any better way to achieve it?
  246. // also note that json does not really allow comments...
  247. $data = preg_replace(array(
  248. // eliminate single line comments in '// ...' form
  249. // REMOVED BECAUSE OF BUGS: 1-does not match at end of non-empty line, 2-eats inside strings, too
  250. //'#^\s*//(.*)$#m',
  251. // eliminate multi-line comments in '/* ... */' form, at start of string
  252. '#^\s*/\*(.*)\*/#Us',
  253. // eliminate multi-line comments in '/* ... */' form, at end of string
  254. '#/\*(.*)\*/\s*$#Us'
  255. ), '', $data);
  256. $data = trim($data); // remove excess whitespace
  257. if ($data == '')
  258. {
  259. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (empty string?)';
  260. return false;
  261. }
  262. //echo "Parsing string (".$data.")\n";
  263. switch($data[0])
  264. {
  265. case '"':
  266. case "'":
  267. $len = strlen($data);
  268. // quoted string: check for closing char first
  269. if ($data[$len-1] == $data[0] && $len > 1)
  270. {
  271. // UTF8-decode (or encode) string
  272. // NB: we MUST do this BEFORE looking for \xNN, \uMMMM or other escape sequences
  273. if ($src_encoding == 'UTF-8' && ($dest_encoding == 'ISO-8859-1' || $dest_encoding == 'US-ASCII'))
  274. {
  275. $data = utf8_decode($data);
  276. $len = strlen($data);
  277. }
  278. else
  279. {
  280. if ($dest_encoding == 'UTF-8' && ($src_encoding == 'ISO-8859-1' || $src_encoding == 'US-ASCII'))
  281. {
  282. $data = utf8_encode($data);
  283. $len = strlen($data);
  284. }
  285. //else
  286. //{
  287. // $GLOBALS['_xh']['value'] = $GLOBALS['_xh']['ac'];
  288. //}
  289. }
  290. $outdata = '';
  291. $delim = $data[0];
  292. for ($i = 1; $i < $len-1; $i++)
  293. {
  294. switch($data[$i])
  295. {
  296. case '\\':
  297. if ($i == $len-2)
  298. {
  299. break;
  300. }
  301. switch($data[$i+1])
  302. {
  303. case 'b':
  304. case 'f':
  305. case 'n':
  306. case 'r':
  307. case 't':
  308. case 'v':
  309. $outdata .= $GLOBALS['ecma262_entities'][$data[$i+1]];
  310. $i++;
  311. break;
  312. case 'u':
  313. // most likely unicode code point
  314. if ($dest_encoding == 'UTF-8')
  315. {
  316. /// @todo see if this is faster / works in all cases
  317. //$outdata .= utf8_encode(chr(hexdec(substr($data, $i+4, 2))));
  318. // encode the UTF code point into utf-8...
  319. $ii = hexdec(substr($data, $i+2, 4));
  320. if ($ii < 0x80)
  321. {
  322. $outdata .= chr($ii);
  323. }
  324. else if ($ii <= 0x800)
  325. {
  326. $outdata .= chr(0xc0 | $ii >> 6) . chr(0x80 | ($ii & 0x3f));
  327. }
  328. else if ($ii <= 0x10000)
  329. {
  330. $outdata .= chr(0xe0 | $ii >> 12) . chr(0x80 | ($ii >> 6 & 0x3f)) . chr(0x80 | ($ii & 0x3f));
  331. }
  332. else
  333. {
  334. $outdata .= chr(0xf0 | $ii >> 20) . chr(0x80 | ($ii >> 12 & 0x3f)) . chr(0x80 | ($ii >> 6 & 0x3f)) . chr(0x80 | ($ii & 0x3f));
  335. }
  336. $i += 5;
  337. }
  338. else
  339. {
  340. // Note: we only decode code points below 256, so we take the last 2 chars of the unicode representation
  341. $outdata .= chr(hexdec(substr($data, $i+4, 2)));
  342. $i += 5;
  343. }
  344. break;
  345. case 'x':
  346. // most likely unicode code point in hexadecimal
  347. // Note: the json spec omits this case, but ECMA-262 does not...
  348. if ($dest_encoding == 'UTF-8')
  349. {
  350. // encode the UTF code point into utf-8...
  351. $ii = hexdec(substr($data, $i+2, 2));
  352. if ($ii < 0x80)
  353. {
  354. $outdata .= chr($ii);
  355. }
  356. else if ($ii <= 0x800)
  357. {
  358. $outdata .= chr(0xc0 | $ii >> 6) . chr(0x80 | ($ii & 0x3f));
  359. }
  360. else if ($ii <= 0x10000)
  361. {
  362. $outdata .= chr(0xe0 | $ii >> 12) . chr(0x80 | ($ii >> 6 & 0x3f)) . chr(0x80 | ($ii & 0x3f));
  363. }
  364. else
  365. {
  366. $outdata .= chr(0xf0 | $ii >> 20) . chr(0x80 | ($ii >> 12 & 0x3f)) . chr(0x80 | ($ii >> 6 & 0x3f)) . chr(0x80 | ($ii & 0x3f));
  367. }
  368. $i += 3;
  369. }
  370. else
  371. {
  372. $outdata .= chr(hexdec(substr($data, $i+2, 2)));
  373. $i += 3;
  374. }
  375. break;
  376. case '0':
  377. case '1':
  378. case '2':
  379. case '3':
  380. case '4':
  381. case '5':
  382. case '6':
  383. case '7':
  384. case '8':
  385. case '9':
  386. // Note: ECMA-262 forbids these escapes, we just skip it...
  387. break;
  388. default:
  389. // Note: Javascript 1.5 on http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide
  390. // mentions syntax /XXX with X octal number, but ECMA262
  391. // explicitly forbids it...
  392. $outdata .= $data[$i+1];
  393. $i++;
  394. } // end of switch on slash char found
  395. break;
  396. case $delim:
  397. // found unquoted end of string in middle of string
  398. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (unescaped quote char inside string?)';
  399. return false;
  400. case "\n":
  401. case "\r":
  402. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (line terminator char inside string?)';
  403. return false;
  404. default:
  405. $outdata .= $data[$i];
  406. }
  407. } // end of loop on string chars
  408. //echo "Found a string\n";
  409. $GLOBALS['_xh']['vt'] = 'string';
  410. $GLOBALS['_xh']['value'] = $outdata;
  411. }
  412. else
  413. {
  414. // string without a terminating quote
  415. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (string missing closing quote?)';
  416. return false;
  417. }
  418. break;
  419. case '[':
  420. case '{':
  421. $len = strlen($data);
  422. // object and array notation: use the same parsing code
  423. if ($data[0] == '[')
  424. {
  425. if ($data[$len-1] != ']')
  426. {
  427. // invalid array
  428. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (array missing closing bracket?)';
  429. return false;
  430. }
  431. $GLOBALS['_xh']['vt'] = 'array';
  432. }
  433. else
  434. {
  435. if ($data[$len-1] != '}')
  436. {
  437. // invalid object
  438. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (object missing closing bracket?)';
  439. return false;
  440. }
  441. $GLOBALS['_xh']['vt'] = 'struct';
  442. }
  443. $data = trim(substr($data, 1, -1));
  444. //echo "Parsing array/obj (".$data.")\n";
  445. if ($data == '')
  446. {
  447. // empty array/object
  448. $GLOBALS['_xh']['value'] = array();
  449. }
  450. else
  451. {
  452. $valuestack = array();
  453. $last = array('type' => 'sl', 'start' => 0);
  454. $len = strlen($data);
  455. $value = array();
  456. $keypos = null;
  457. //$ac = '';
  458. $vt = '';
  459. //$start = 0;
  460. for ($i = 0; $i <= $len; $i++)
  461. {
  462. if ($i == $len || ($data[$i] == ',' && $last['type'] == 'sl'))
  463. {
  464. // end of element: push it onto array
  465. $slice = substr($data, $last['start'], ($i - $last['start']));
  466. //$slice = trim($slice); useless here, sincewe trim it on sub-elementparsing
  467. //echo "Found slice (".$slice.")\n";
  468. //$valuestack[] = $last; // necessario ???
  469. //$last = array('type' => 'sl', 'start' => ($i + 1));
  470. if ($GLOBALS['_xh']['vt'] == 'array')
  471. {
  472. if ($slice == '')
  473. {
  474. // 'elided' element: ecma supports it, so do we
  475. // what should happen here in fact is that
  476. // "array index is augmented and element is undefined"
  477. // NOTE: Firefox's js engine does not create
  478. // trailing undefined elements, while IE does...
  479. //if ($i < $len)
  480. //{
  481. if ($return_phpvals)
  482. {
  483. $value[] = null;
  484. }
  485. else
  486. {
  487. $value[] = new jsonrpcval(null, 'null');
  488. }
  489. //}
  490. }
  491. else
  492. {
  493. if (!json_parse($slice, $return_phpvals, $src_encoding, $dest_encoding))
  494. {
  495. return false;
  496. }
  497. else
  498. {
  499. $value[] = $GLOBALS['_xh']['value'];
  500. $GLOBALS['_xh']['vt'] = 'array';
  501. }
  502. }
  503. }
  504. else
  505. {
  506. if (!$keypos)
  507. {
  508. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (missing object member name?)';
  509. return false;
  510. }
  511. else
  512. {
  513. if (!json_parse(substr($data, $last['start'], $keypos-$last['start']), true, $src_encoding, $dest_encoding) ||
  514. $GLOBALS['_xh']['vt'] != 'string')
  515. {
  516. // object member name received unquoted: what to do???
  517. // be tolerant as much as we can. ecma tolerates numbers as identifiers, too...
  518. $key = trim(substr($data, $last['start'], $keypos-$last['start']));
  519. }
  520. else
  521. {
  522. $key = $GLOBALS['_xh']['value'];
  523. }
  524. //echo "Use extension: $use_extension\n";
  525. if (!json_parse(substr($data, $keypos+1, $i-$keypos-1), $return_phpvals, $src_encoding, $dest_encoding))
  526. {
  527. return false;
  528. }
  529. $value[$key] = $GLOBALS['_xh']['value'];
  530. $GLOBALS['_xh']['vt'] = 'struct';
  531. $keypos = null;
  532. }
  533. }
  534. $last['start'] = $i + 1;
  535. $vt = ''; // reset type of val found
  536. }
  537. else if ($data[$i] == '"' || $data[$i] == "'")
  538. {
  539. // found beginning of string: run till end
  540. $ok = false;
  541. for ($j = $i+1; $j < $len; $j++)
  542. {
  543. if ($data[$j] == $data[$i])
  544. {
  545. $ok = true;
  546. break;
  547. }
  548. else if($data[$j] == '\\')
  549. {
  550. $j++;
  551. }
  552. }
  553. if ($ok)
  554. {
  555. $i = $j; // advance pointer to end of string
  556. $vt = 'st';
  557. }
  558. else
  559. {
  560. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (string missing closing quote?)';
  561. return false;
  562. }
  563. }
  564. else if ($data[$i] == "[")
  565. {
  566. $valuestack[] = $last;
  567. $last = array('type' => 'ar', 'start' => $i);
  568. }
  569. else if ($data[$i] == '{')
  570. {
  571. $valuestack[] = $last;
  572. $last = array('type' => 'ob', 'start' => $i);
  573. }
  574. else if ($data[$i] == "]")
  575. {
  576. if ($last['type'] == 'ar')
  577. {
  578. $last = array_pop($valuestack);
  579. $vt = 'ar';
  580. }
  581. else
  582. {
  583. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (unmatched array closing bracket?)';
  584. return false;
  585. }
  586. }
  587. else if ($data[$i] == '}')
  588. {
  589. if ($last['type'] == 'ob')
  590. {
  591. $last = array_pop($valuestack);
  592. $vt = 'ob';
  593. }
  594. else
  595. {
  596. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (unmatched object closing bracket?)';
  597. return false;
  598. }
  599. }
  600. else if ($data[$i] == ':' && $last['type'] == 'sl' && !$keypos)
  601. {
  602. //echo "Found key stop at pos. $i\n";
  603. $keypos = $i;
  604. }
  605. else if ($data[$i] == '/' && $i < $len-1 && $data[$i+1] == "*")
  606. {
  607. // found beginning of comment: run till end
  608. $ok = false;
  609. for ($j = $i+2; $j < $len-1; $j++)
  610. {
  611. if ($data[$j] == '*' && $data[$j+1] == '/')
  612. {
  613. $ok = true;
  614. break;
  615. }
  616. }
  617. if ($ok)
  618. {
  619. $i = $j+1; // advance pointer to end of string
  620. }
  621. else
  622. {
  623. $GLOBALS['_xh']['isf_reason'] = 'Invalid data (comment missing closing tag?)';
  624. return false;
  625. }
  626. }
  627. }
  628. $GLOBALS['_xh']['value'] = $value;
  629. }
  630. //return true;
  631. break;
  632. default:
  633. //echo "Found a scalar val (not string): '$data'\n";
  634. // be tolerant of uppercase chars in numbers/booleans/null
  635. $data = strtolower($data);
  636. if ($data == "true")
  637. {
  638. //echo "Found a true\n";
  639. $GLOBALS['_xh']['value'] = true;
  640. $GLOBALS['_xh']['vt'] = 'boolean';
  641. }
  642. else if ($data == "false")
  643. {
  644. //echo "Found a false\n";
  645. $GLOBALS['_xh']['value'] = false;
  646. $GLOBALS['_xh']['vt'] = 'boolean';
  647. }
  648. else if ($data == "null")
  649. {
  650. //echo "Found a null\n";
  651. $GLOBALS['_xh']['value'] = null;
  652. $GLOBALS['_xh']['vt'] = 'null';
  653. }
  654. // we could use is_numeric here, but rules are slightly different,
  655. // e.g. 012 is NOT valid according to JSON or ECMA, but browsers inetrpret it as octal
  656. /// @todo add support for .5
  657. /// @todo add support for numbers in octal notation, eg. 010
  658. else if (preg_match("#^-?(0|[1-9][0-9]*)(\.[0-9]*)?([e][+-]?[0-9]+)?$#" ,$data))
  659. {
  660. if (preg_match('#[.e]#', $data))
  661. {
  662. //echo "Found a double\n";
  663. // floating point
  664. $GLOBALS['_xh']['value'] = (double)$data;
  665. $GLOBALS['_xh']['vt'] = 'double';
  666. }
  667. else
  668. {
  669. //echo "Found an int\n";
  670. //integer
  671. $GLOBALS['_xh']['value'] = (int)$data;
  672. $GLOBALS['_xh']['vt'] = 'int';
  673. }
  674. //return true;
  675. }
  676. else if (preg_match("#^0x[0-9a-f]+$#", $data))
  677. {
  678. // int in hex notation: not in JSON, but in ECMA...
  679. $GLOBALS['_xh']['vt'] = 'int';
  680. $GLOBALS['_xh']['value'] = hexdec(substr($data, 2));
  681. }
  682. else
  683. {
  684. $GLOBALS['_xh']['isf_reason'] = 'Invalid data';
  685. return false;
  686. }
  687. } // switch $data[0]
  688. if (!$return_phpvals)
  689. {
  690. $GLOBALS['_xh']['value'] = new jsonrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
  691. }
  692. return true;
  693. }
  694. /**
  695. * Used in place of json_parse to take advantage of native json decoding when available:
  696. * it parses either a jsonrpc request or a response.
  697. * NB: php native decoding of json balks anyway at anything but array / struct as top level element
  698. * @access private
  699. * @bug unicode chars are handled differently from this and json_parse...
  700. * @todo add support for src and dest encoding!!!
  701. */
  702. function json_parse_native($data)
  703. {
  704. //echo "Parsing string - internal way (".$data.")\n";
  705. $out = json_decode($data, true);
  706. if (!is_array($out))
  707. {
  708. //$GLOBALS['_xh']['isf'] = 2;
  709. $GLOBALS['_xh']['isf_reason'] = 'JSON parsing failed';
  710. return false;
  711. }
  712. // decoding will be fine for a jsonrpc error response, so we have to
  713. // check for it by hand here...
  714. //else if (array_key_exists('error', $out) && $out['error'] != null)
  715. //{
  716. // $GLOBALS['_xh']['isf'] = 1;
  717. //$GLOBALS['_xh']['value'] = $out['error'];
  718. //}
  719. else
  720. {
  721. $GLOBALS['_xh']['value'] = $out;
  722. return true;
  723. }
  724. }
  725. /**
  726. * Parse a json string, expected to be jsonrpc request format
  727. * @access private
  728. */
  729. function jsonrpc_parse_req($data, $return_phpvals=false, $use_extension=false, $src_encoding='')
  730. {
  731. $GLOBALS['_xh']['isf']=0;
  732. $GLOBALS['_xh']['isf_reason']='';
  733. $GLOBALS['_xh']['pt'] = array();
  734. if ($return_phpvals && $use_extension)
  735. {
  736. $ok = json_parse_native($data);
  737. }
  738. else
  739. {
  740. $ok = json_parse($data, $return_phpvals, $src_encoding);
  741. }
  742. if ($ok)
  743. {
  744. if (!$return_phpvals)
  745. $GLOBALS['_xh']['value'] = @$GLOBALS['_xh']['value']->me['struct'];
  746. if (!is_array($GLOBALS['_xh']['value']) || !array_key_exists('method', $GLOBALS['_xh']['value'])
  747. || !array_key_exists('params', $GLOBALS['_xh']['value']) || !array_key_exists('id', $GLOBALS['_xh']['value']))
  748. {
  749. $GLOBALS['_xh']['isf_reason'] = 'JSON parsing did not return correct jsonrpc request object';
  750. return false;
  751. }
  752. else
  753. {
  754. $GLOBALS['_xh']['method'] = $GLOBALS['_xh']['value']['method'];
  755. $GLOBALS['_xh']['params'] = $GLOBALS['_xh']['value']['params'];
  756. $GLOBALS['_xh']['id'] = $GLOBALS['_xh']['value']['id'];
  757. if (!$return_phpvals)
  758. {
  759. /// @todo we should check for appropriate type for method name and params array...
  760. $GLOBALS['_xh']['method'] = $GLOBALS['_xh']['method']->scalarval();
  761. $GLOBALS['_xh']['params'] = $GLOBALS['_xh']['params']->me['array'];
  762. $GLOBALS['_xh']['id'] = php_jsonrpc_decode($GLOBALS['_xh']['id']);
  763. }
  764. else
  765. {
  766. // to allow 'phpvals' type servers to work, we need to rebuild $GLOBALS['_xh']['pt'] too
  767. foreach($GLOBALS['_xh']['params'] as $val)
  768. {
  769. // since we rebuild this after converting json values to php,
  770. // we've lost the info about array/struct, and we try to rebuild it
  771. /// @bug empty objects will be recognized as empty arrays
  772. /// @bug an object with keys '0', '1', ... 'n' will be recognized as an array
  773. $typ = gettype($val);
  774. if ($typ == 'array' && count($val) && count(array_diff_key($val, array_fill(0, count($val), null))) !== 0)
  775. {
  776. $typ = 'object';
  777. }
  778. $GLOBALS['_xh']['pt'][] = php_2_jsonrpc_type($typ);
  779. }
  780. }
  781. return true;
  782. }
  783. }
  784. else
  785. {
  786. return false;
  787. }
  788. }
  789. /**
  790. * Parse a json string, expected to be in json-rpc response format.
  791. * @access private
  792. * @todo checks missing:
  793. * - no extra members in response
  794. * - no extra members in error struct
  795. * - resp. ID validation
  796. */
  797. function jsonrpc_parse_resp($data, $return_phpvals=false, $use_extension=false, $src_encoding='')
  798. {
  799. $GLOBALS['_xh']['isf']=0;
  800. $GLOBALS['_xh']['isf_reason']='';
  801. if ($return_phpvals && $use_extension)
  802. {
  803. $ok = json_parse_native($data);
  804. }
  805. else
  806. {
  807. $ok = json_parse($data, $return_phpvals, $src_encoding);
  808. }
  809. if ($ok)
  810. {
  811. if (!$return_phpvals)
  812. {
  813. $GLOBALS['_xh']['value'] = @$GLOBALS['_xh']['value']->me['struct'];
  814. }
  815. if (!is_array($GLOBALS['_xh']['value']) || !array_key_exists('result', $GLOBALS['_xh']['value'])
  816. || !array_key_exists('error', $GLOBALS['_xh']['value']) || !array_key_exists('id', $GLOBALS['_xh']['value']))
  817. {
  818. //$GLOBALS['_xh']['isf'] = 2;
  819. $GLOBALS['_xh']['isf_reason'] = 'JSON parsing did not return correct jsonrpc response object';
  820. return false;
  821. }
  822. if (!$return_phpvals)
  823. {
  824. $d_error = php_jsonrpc_decode($GLOBALS['_xh']['value']['error']);
  825. $GLOBALS['_xh']['value']['id'] = php_jsonrpc_decode($GLOBALS['_xh']['value']['id']);
  826. }
  827. else
  828. {
  829. $d_error = $GLOBALS['_xh']['value']['error'];
  830. }
  831. $GLOBALS['_xh']['id'] = $GLOBALS['_xh']['value']['id'];
  832. if ($d_error != null)
  833. {
  834. $GLOBALS['_xh']['isf'] = 1;
  835. //$GLOBALS['_xh']['value'] = $d_error;
  836. if (is_array($d_error) && array_key_exists('faultCode', $d_error)
  837. && array_key_exists('faultString', $d_error))
  838. {
  839. if($d_error['faultCode'] == 0)
  840. {
  841. // FAULT returned, errno needs to reflect that
  842. $d_error['faultCode'] = -1;
  843. }
  844. $GLOBALS['_xh']['value'] = $d_error;
  845. }
  846. // NB: what about jsonrpc servers that do NOT respect
  847. // the faultCode/faultString convention???
  848. // we force the error into a string. regardless of type...
  849. else //if (is_string($GLOBALS['_xh']['value']))
  850. {
  851. if ($return_phpvals)
  852. {
  853. $GLOBALS['_xh']['value'] = array('faultCode' => -1, 'faultString' => var_export($GLOBALS['_xh']['value']['error'], true));
  854. }
  855. else
  856. {
  857. $GLOBALS['_xh']['value'] = array('faultCode' => -1, 'faultString' => serialize_jsonrpcval($GLOBALS['_xh']['value']['error']));
  858. }
  859. }
  860. }
  861. else
  862. {
  863. $GLOBALS['_xh']['value'] = $GLOBALS['_xh']['value']['result'];
  864. }
  865. return true;
  866. }
  867. else
  868. {
  869. return false;
  870. }
  871. }
  872. class jsonrpc_client extends xmlrpc_client
  873. {
  874. // by default, no multicall exists for JSON-RPC, so do not try it
  875. var $no_multicall = true;
  876. // default return type of calls to json-rpc servers: jsonrpcvals
  877. var $return_type = 'jsonrpcvals';
  878. /*
  879. function jsonrpc_client($path, $server='', $port='', $method='')
  880. {
  881. $this->xmlrpc_client($path, $server, $port, $method);
  882. // we need to override the list of std supported encodings, since
  883. // according to ECMA-262, the standard charset is UTF-16
  884. $this->accepted_charset_encodings = array('UTF-16', 'UTF-8', 'ISO-8859-1', 'US-ASCII');
  885. }
  886. */
  887. }
  888. class jsonrpcmsg extends xmlrpcmsg
  889. {
  890. var $id = null; // used to store request ID internally
  891. var $content_type = 'application/json';
  892. /**
  893. * @param string $meth the name of the method to invoke
  894. * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
  895. * @param mixed $id the id of the jsonrpc request
  896. */
  897. function jsonrpcmsg($meth, $pars=0, $id=null)
  898. {
  899. // NB: a NULL id is allowed and has a very definite meaning!
  900. $this->id = $id;
  901. $this->xmlrpcmsg($meth, $pars);
  902. }
  903. /**
  904. * @access private
  905. */
  906. function createPayload($charset_encoding='')
  907. {
  908. if ($charset_encoding != '')
  909. $this->content_type = 'application/json; charset=' . $charset_encoding;
  910. else
  911. $this->content_type = 'application/json';
  912. // @ todo: verify if all chars are allowed for method names or can
  913. // we just skip the js encoding on it?
  914. $this->payload = "{\n\"method\": \"" . json_encode_entities($this->methodname, '', $charset_encoding) . "\",\n\"params\": [ ";
  915. for($i = 0; $i < sizeof($this->params); $i++)
  916. {
  917. $p = $this->params[$i];
  918. // MB: we try to force serialization as json even though the object
  919. // param might be a plain xmlrpcval object.
  920. // This way we do not need to override addParam, aren't we lazy?
  921. $this->payload .= "\n " . serialize_jsonrpcval($p, $charset_encoding) .
  922. ",";
  923. }
  924. $this->payload = substr($this->payload, 0, -1) . "\n],\n\"id\": ";
  925. switch (true)
  926. {
  927. case $this->id === null:
  928. $this->payload .= 'null';
  929. break;
  930. case is_string($this->id):
  931. $this->payload .= '"'.json_encode_entities($this->id, '', $charset_encoding).'"';
  932. break;
  933. case is_bool($this->id):
  934. $this->payload .= ($this->id ? 'true' : 'false');
  935. break;
  936. default:
  937. $this->payload .= $this->id;
  938. }
  939. $this->payload .= "\n}\n";
  940. }
  941. /**
  942. * Parse the jsonrpc response contained in the string $data and return a jsonrpcresp object.
  943. * @param string $data the xmlrpc response, eventually including http headers
  944. * @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and conseuqent decoding
  945. * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
  946. * @return jsonrpcresp
  947. * @access private
  948. */
  949. function &parseResponse($data='', $headers_processed=false, $return_type='jsonrpcvals')
  950. {
  951. if($this->debug)
  952. {
  953. print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
  954. }
  955. if($data == '')
  956. {
  957. error_log('XML-RPC: '.__METHOD__.': no response received from server.');
  958. $r = new jsonrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
  959. return $r;
  960. }
  961. $GLOBALS['_xh']=array();
  962. $raw_data = $data;
  963. // parse the HTTP headers of the response, if present, and separate them from data
  964. if(substr($data, 0, 4) == 'HTTP')
  965. {
  966. $r =& $this->parseResponseHeaders($data, $headers_processed);
  967. if ($r)
  968. {
  969. // parent class implementation of parseResponseHeaders returns in case
  970. // of error an object of the wrong type: recode it into correct object
  971. $rj = new jsonrpcresp(0, $r->faultCode(), $r->faultString());
  972. $rj->raw_data = $data;
  973. return $rj;
  974. }
  975. }
  976. else
  977. {
  978. $GLOBALS['_xh']['headers'] = array();
  979. $GLOBALS['_xh']['cookies'] = array();
  980. }
  981. if($this->debug)
  982. {
  983. $start = strpos($data, '/* SERVER DEBUG INFO (BASE64 ENCODED):');
  984. if ($start !== false)
  985. {
  986. $start += strlen('/* SERVER DEBUG INFO (BASE64 ENCODED):');
  987. $end = strpos($data, '*/', $start);
  988. $comments = substr($data, $start, $end-$start);
  989. print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
  990. }
  991. }
  992. // be tolerant of extra whitespace in response body
  993. $data = trim($data);
  994. // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
  995. $end = strrpos($data, '}');
  996. if ($end)
  997. {
  998. $data = substr($data, 0, $end+1);
  999. }
  1000. // if user wants back raw json, give it to him
  1001. if ($return_type == 'json')
  1002. {
  1003. $r = new jsonrpcresp($data, 0, '', 'json');
  1004. $r->hdrs = $GLOBALS['_xh']['headers'];
  1005. $r->_cookies = $GLOBALS['_xh']['cookies'];
  1006. $r->raw_data = $raw_data;
  1007. return $r;
  1008. }
  1009. // @todo shall we try to check for non-unicode json received ???
  1010. if (!jsonrpc_parse_resp($data, $return_type=='phpvals'))
  1011. {
  1012. if ($this->debug)
  1013. {
  1014. /// @todo echo something for user?
  1015. }
  1016. $r = new jsonrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
  1017. $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
  1018. }
  1019. //elseif ($return_type == 'jsonrpcvals' && !is_object($GLOBALS['_xh']['value']))
  1020. //{
  1021. // then something odd has happened
  1022. // and it's time to generate a client side error
  1023. // indicating something odd went on
  1024. // $r = new jsonrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
  1025. // $GLOBALS['xmlrpcstr']['invalid_return']);
  1026. //}
  1027. else
  1028. {
  1029. $v = $GLOBALS['_xh']['value'];
  1030. if ($this->debug)
  1031. {
  1032. print "<PRE>---PARSED---\n" ;
  1033. var_export($v);
  1034. print "\n---END---</PRE>";
  1035. }
  1036. if($GLOBALS['_xh']['isf'])
  1037. {
  1038. $r = new jsonrpcresp(0, $v['faultCode'], $v['faultString']);
  1039. }
  1040. else
  1041. {
  1042. $r = new jsonrpcresp($v, 0, '', $return_type);
  1043. }
  1044. $r->id = $GLOBALS['_xh']['id'];
  1045. }
  1046. $r->hdrs = $GLOBALS['_xh']['headers'];
  1047. $r->_cookies = $GLOBALS['_xh']['cookies'];
  1048. $r->raw_data = $raw_data;
  1049. return $r;
  1050. }
  1051. }
  1052. class jsonrpcresp extends xmlrpcresp
  1053. {
  1054. var $content_type = 'application/json'; // NB: forces us to send US-ASCII over http
  1055. var $id = null;
  1056. /// @todo override creator, to set proper valtyp and id!
  1057. /**
  1058. * Returns json representation of the response.
  1059. * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
  1060. * @return string the json representation of the response
  1061. * @access public
  1062. */
  1063. function serialize($charset_encoding='')
  1064. {
  1065. if ($charset_encoding != '')
  1066. $this->content_type = 'application/json; charset=' . $charset_encoding;
  1067. else
  1068. $this->content_type = 'application/json';
  1069. $this->payload = serialize_jsonrpcresp($this, $this->id, $charset_encoding);
  1070. return $this->payload;
  1071. }
  1072. }
  1073. class jsonrpcval extends xmlrpcval
  1074. {
  1075. /**
  1076. * Returns json representation of the value.
  1077. * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
  1078. * @return string
  1079. * @access public
  1080. */
  1081. function serialize($charset_encoding='')
  1082. {
  1083. return serialize_jsonrpcval($this, $charset_encoding);
  1084. }
  1085. }
  1086. /**
  1087. * Takes a json value in PHP jsonrpcval object format
  1088. * and translates it into native PHP types.
  1089. *
  1090. * @param jsonrpcval $jsonrpc_val
  1091. * @param array $options if 'decode_php_objs' is set in the options array, jsonrpc objects can be decoded into php objects
  1092. * @return mixed
  1093. * @access public
  1094. */
  1095. function php_jsonrpc_decode($jsonrpc_val, $options=array())
  1096. {
  1097. $kind = $jsonrpc_val->kindOf();
  1098. if($kind == 'scalar')
  1099. {
  1100. return $jsonrpc_val->scalarval();
  1101. }
  1102. elseif($kind == 'array')
  1103. {
  1104. $size = $jsonrpc_val->arraysize();
  1105. $arr = array();
  1106. for($i = 0; $i < $size; $i++)
  1107. {
  1108. $arr[] = php_jsonrpc_decode($jsonrpc_val->arraymem($i), $options);
  1109. }
  1110. return $arr;
  1111. }
  1112. elseif($kind == 'struct')
  1113. {
  1114. $jsonrpc_val->structreset();
  1115. // If user said so, try to rebuild php objects for specific struct vals.
  1116. /// @todo should we raise a warning for class not found?
  1117. // shall we check for proper subclass of xmlrpcval instead of
  1118. // presence of _php_class to detect what we can do?
  1119. if (in_array('decode_php_objs', $options))
  1120. {
  1121. if( $jsonrpc_val->_php_class != ''
  1122. && class_exists($jsonrpc_val->_php_class))
  1123. {
  1124. $obj = @new $jsonrpc_val->_php_class;
  1125. }
  1126. else
  1127. {
  1128. $obj = new stdClass();
  1129. }
  1130. while(list($key,$value) = $jsonrpc_val->structeach())
  1131. {
  1132. $obj->$key = php_jsonrpc_decode($value, $options);
  1133. }
  1134. return $obj;
  1135. }
  1136. else
  1137. {
  1138. $arr = array();
  1139. while(list($key,$value) = $jsonrpc_val->structeach())
  1140. {
  1141. $arr[$key] = php_jsonrpc_decode($value, $options);
  1142. }
  1143. return $arr;
  1144. }
  1145. }
  1146. }
  1147. /**
  1148. * Takes native php types and encodes them into jsonrpc PHP object format.
  1149. * It will not re-encode jsonrpcval objects.
  1150. *
  1151. * @param mixed $php_val the value to be converted into a jsonrpcval object
  1152. * @param array $options can include 'encode_php_objs'
  1153. * @return jsonrpcval
  1154. * @access public
  1155. */
  1156. function php_jsonrpc_encode($php_val, $options='')
  1157. {
  1158. $type = gettype($php_val);
  1159. switch($type)
  1160. {
  1161. case 'string':
  1162. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcString']);
  1163. break;
  1164. case 'integer':
  1165. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcInt']);
  1166. break;
  1167. case 'double':
  1168. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcDouble']);
  1169. break;
  1170. case 'boolean':
  1171. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
  1172. break;
  1173. case 'resource': // for compat with php json extension...
  1174. case 'NULL':
  1175. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcNull']);
  1176. break;
  1177. case 'array':
  1178. // PHP arrays can be encoded to either objects or arrays,
  1179. // depending on wheter they are hashes or plain 0..n integer indexed
  1180. // A shorter one-liner would be
  1181. // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
  1182. // but execution time skyrockets!
  1183. $j = 0;
  1184. $arr = array();
  1185. $ko = false;
  1186. foreach($php_val as $key => $val)
  1187. {
  1188. $arr[$key] = php_jsonrpc_encode($val, $options);
  1189. if(!$ko && $key !== $j)
  1190. {
  1191. $ko = true;
  1192. }
  1193. $j++;
  1194. }
  1195. if($ko)
  1196. {
  1197. $jsonrpc_val = new jsonrpcval($arr, $GLOBALS['xmlrpcStruct']);
  1198. }
  1199. else
  1200. {
  1201. $jsonrpc_val = new jsonrpcval($arr, $GLOBALS['xmlrpcArray']);
  1202. }
  1203. break;
  1204. case 'object':
  1205. if(is_a($php_val, 'jsonrpcval'))
  1206. {
  1207. $jsonrpc_val = $php_val;
  1208. }
  1209. else
  1210. {
  1211. $arr = array();
  1212. reset($php_val);
  1213. while(list($k,$v) = each($php_val))
  1214. {
  1215. $arr[$k] = php_jsonrpc_encode($v, $options);
  1216. }
  1217. $jsonrpc_val = new jsonrpcval($arr, $GLOBALS['xmlrpcStruct']);
  1218. if (in_array('encode_php_objs', $options))
  1219. {
  1220. // let's save original class name into xmlrpcval:
  1221. // might be useful later on...
  1222. $jsonrpc_val->_php_class = get_class($php_val);
  1223. }
  1224. }
  1225. break;
  1226. // catch "user function", "unknown type"
  1227. default:
  1228. $jsonrpc_val = new jsonrpcval();
  1229. break;
  1230. }
  1231. return $jsonrpc_val;
  1232. }
  1233. /**
  1234. * Convert the json representation of a jsonrpc method call, jsonrpc method response
  1235. * or single json value into the appropriate object (a.k.a. deserialize).
  1236. * Please note that there is no way to distinguish the serialized representation
  1237. * of a single json val of type object which has the 3 appropriate members from
  1238. * the serialization of a method call or method response.
  1239. * In such a case, the function will return a jsonrpcresp or jsonrpcmsg
  1240. * @param string $json_val
  1241. * @param array $options
  1242. * @return mixed false on error, or an instance of jsonrpcval, jsonrpcresp or jsonrpcmsg
  1243. * @access public
  1244. * @todo add options controlling character set encodings
  1245. */
  1246. function php_jsonrpc_decode_json($json_val, $options=array())
  1247. {
  1248. $src_encoding = array_key_exists('src_encoding', $options) ? $options['src_encoding'] : $GLOBALS['xmlrpc_defencoding'];
  1249. $dest_encoding = array_key_exists('dest_encoding', $options) ? $options['dest_encoding'] : $GLOBALS['xmlrpc_internalencoding'];
  1250. //$GLOBALS['_xh'] = array();
  1251. $GLOBALS['_xh']['isf'] = 0;
  1252. if (!json_parse($json_val, false, $src_encoding, $dest_encoding))
  1253. {
  1254. error_log($GLOBALS['_xh']['isf_reason']);
  1255. return false;
  1256. }
  1257. else
  1258. {
  1259. $val = $GLOBALS['_xh']['value']; // shortcut
  1260. if ($GLOBALS['_xh']['value']->kindOf() == 'struct')
  1261. {
  1262. if ($GLOBALS['_xh']['value']->structSize() == 3)
  1263. {
  1264. if ($GLOBALS['_xh']['value']->structMemExists('method') &&
  1265. $GLOBALS['_xh']['value']->structMemExists('params') &&
  1266. $GLOBALS['_xh']['value']->structMemExists('id'))
  1267. {
  1268. /// @todo we do not check for correct type of 'method', 'params' struct members...
  1269. $method = $GLOBALS['_xh']['value']->structMem('method');
  1270. $msg = new jsonrpcmsg($method->scalarval(), null, php_jsonrpc_decode($GLOBALS['_xh']['value']->structMem('id')));
  1271. $params = $GLOBALS['_xh']['value']->structMem('params');
  1272. for($i = 0; $i < $params->arraySize(); ++$i)
  1273. {
  1274. $msg->addparam($params->arrayMem($i));
  1275. }
  1276. return $msg;
  1277. }
  1278. else
  1279. if ($GLOBALS['_xh']['value']->structMemExists('result') &&
  1280. $GLOBALS['_xh']['value']->structMemExists('error') &&
  1281. $GLOBALS['_xh']['value']->structMemExists('id'))
  1282. {
  1283. $id = php_jsonrpc_decode($GLOBALS['_xh']['value']->structMem('id'));
  1284. $err = php_jsonrpc_decode($GLOBALS['_xh']['value']->structMem('error'));
  1285. if ($err == null)
  1286. {
  1287. $resp = new jsonrpcresp($GLOBALS['_xh']['value']->structMem('result'));
  1288. }
  1289. else
  1290. {
  1291. if (is_array($err) && array_key_exists('faultCode', $err)
  1292. && array_key_exists('faultString', $err))
  1293. {
  1294. if($err['faultCode'] == 0)
  1295. {
  1296. // FAULT returned, errno needs to reflect that
  1297. $err['faultCode'] = -1;
  1298. }
  1299. }
  1300. // NB: what about jsonrpc servers that do NOT respect
  1301. // the faultCode/faultString convention???
  1302. // we force the error into a string. regardless of type...
  1303. else //if (is_string($GLOBALS['_xh']['value']))
  1304. {
  1305. $err = array('faultCode' => -1, 'faultString' => serialize_jsonrpcval($GLOBALS['_xh']['value']->structMem('error')));
  1306. }
  1307. $resp = new jsonrpcresp(0, $err['faultCode'], $err['faultString']);
  1308. }
  1309. $resp->id = $id;
  1310. return $resp;
  1311. }
  1312. }
  1313. }
  1314. // not a request msg nor a response: a plain jsonrpcval obj
  1315. return $GLOBALS['_xh']['value'];
  1316. }
  1317. }
  1318. /**
  1319. * Serialize a jsonrpcresp (or xmlrpcresp) as json.
  1320. * Moved outside of the corresponding class to ease multi-serialization of
  1321. * xmlrpcresp objects
  1322. * @param xmlrpcresp or jsonrpcresp $resp
  1323. * @param mixed $id
  1324. * @return string
  1325. * @access private
  1326. */
  1327. function serialize_jsonrpcresp($resp, $id=null, $charset_encoding='')
  1328. {
  1329. $result = "{\n\"id\": ";
  1330. switch (true)
  1331. {
  1332. case $id === null:
  1333. $result .= 'null';
  1334. break;
  1335. case is_string($id):
  1336. $result .= '"'.json_encode_entities($id, '', $charset_encoding).'"';
  1337. break;
  1338. case is_bool($id):
  1339. $result .= ($id ? 'true' : 'false');
  1340. break;
  1341. default:
  1342. $result .= $id;
  1343. }
  1344. $result .= ", ";
  1345. if($resp->errno)
  1346. {
  1347. // let non-ASCII response messages be tolerated by clients
  1348. // by encoding non ascii chars
  1349. $result .= "\"error\": { \"faultCode\": " . $resp->errno . ", \"faultString\": \"" . json_encode_entities($resp->errstr, null, $charset_encoding) . "\" }, \"result\": null";
  1350. }
  1351. else
  1352. {
  1353. if(!is_object($resp->val) || !is_a($resp->val, 'xmlrpcval'))
  1354. {
  1355. if (is_string($resp->val) && $resp->valtyp == 'json')
  1356. {
  1357. $result .= "\"error\": null, \"result\": " . $resp->val;
  1358. }
  1359. else
  1360. {
  1361. /// @todo try to build something serializable?
  1362. die('cannot serialize jsonrpcresp objects whose content is native php values');
  1363. }
  1364. }
  1365. else
  1366. {
  1367. $result .= "\"error\": null, \"result\": " .
  1368. serialize_jsonrpcval($resp->val, $charset_encoding);
  1369. }
  1370. }
  1371. $result .= "\n}";
  1372. return $result;
  1373. }
  1374. /**
  1375. * Serialize a jsonrpcval (or xmlrpcval) as json.
  1376. * Moved outside of the corresponding class to ease multi-serialization of
  1377. * xmlrpcval objects
  1378. * @param xmlrpcval or jsonrpcval $value
  1379. * @string $charset_encoding
  1380. * @access private
  1381. */
  1382. function serialize_jsonrpcval($value, $charset_encoding='')
  1383. {
  1384. reset($value->me);
  1385. list($typ, $val) = each($value->me);
  1386. $rs = '';
  1387. switch(@$GLOBALS['xmlrpcTypes'][$typ])
  1388. {
  1389. case 1:
  1390. switch($typ)
  1391. {
  1392. case $GLOBALS['xmlrpcString']:
  1393. $rs .= '"' . json_encode_entities($val, null, $charset_encoding). '"';
  1394. break;
  1395. case $GLOBALS['xmlrpcI4']:
  1396. case $GLOBALS['xmlrpcInt']:
  1397. $rs .= (int)$val;
  1398. break;
  1399. case $GLOBALS['xmlrpcDateTime']:
  1400. // quote date as a json string.
  1401. // assumes date format is valid and will not break js...
  1402. $rs .= '"' . $val . '"';
  1403. break;
  1404. case $GLOBALS['xmlrpcDouble']:
  1405. // add a .0 in case value is integer.
  1406. // This helps us carrying around floats in js, and keep them separated from ints
  1407. $sval = strval((double)$val); // convert to string
  1408. // fix usage of comma, in case of eg. german locale
  1409. $sval = str_replace(',', '.', $sval);
  1410. if (strpos($sval, '.') !== false || strpos($sval, 'e') !== false)
  1411. {
  1412. $rs .= $sval;
  1413. }
  1414. else
  1415. {
  1416. $rs .= $val.'.0';
  1417. }
  1418. break;
  1419. case $GLOBALS['xmlrpcBoolean']:
  1420. $rs .= ($val ? 'true' : 'false');
  1421. break;
  1422. case $GLOBALS['xmlrpcBase64']:
  1423. // treat base 64 values as strings ???
  1424. $rs .= '"' . base64_encode($val) . '"';
  1425. break;
  1426. default:
  1427. $rs .= "null";
  1428. }
  1429. break;
  1430. case 2:
  1431. // array
  1432. $rs .= "[";
  1433. $len = sizeof($val);
  1434. if ($len)
  1435. {
  1436. for($i = 0; $i < $len; $i++)
  1437. {
  1438. $rs .= serialize_jsonrpcval($val[$i], $charset_encoding);
  1439. $rs .= ",";
  1440. }
  1441. $rs = substr($rs, 0, -1) . "]";
  1442. }
  1443. else
  1444. {
  1445. $rs .= "]";
  1446. }
  1447. break;
  1448. case 3:
  1449. // struct
  1450. //if ($value->_php_class)
  1451. //{
  1452. /// @todo implement json-rpc extension for object serialization
  1453. //$rs.='<struct php_class="' . $this->_php_class . "\">\n";
  1454. //}
  1455. //else
  1456. //{
  1457. //}
  1458. foreach($val as $key2 => $val2)
  1459. {
  1460. $rs .= ',"'.json_encode_entities($key2, null, $charset_encoding).'":';
  1461. $rs .= serialize_jsonrpcval($val2, $charset_encoding);
  1462. }
  1463. $rs = '{' . substr($rs, 1) . '}';
  1464. break;
  1465. case 0:
  1466. // let uninitialized jsonrpcval objects serialize to an empty string, as they do in xmlrpc land
  1467. $rs = '""';
  1468. break;
  1469. default:
  1470. break;
  1471. }
  1472. return $rs;
  1473. }
  1474. /**
  1475. * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
  1476. * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
  1477. * NB: for php 'resource' types returns empty string, since resources cannot be serialized;
  1478. * for php class names returns 'struct', since php objects can be serialized as json structs;
  1479. * for php arrays always retutn 'array', even though arrays sometiles serialize as json structs
  1480. * @param string $phptype
  1481. * @return string
  1482. */
  1483. function php_2_jsonrpc_type($phptype)
  1484. {
  1485. switch(strtolower($phptype))
  1486. {
  1487. case 'string':
  1488. return $GLOBALS['xmlrpcString'];
  1489. case 'integer':
  1490. case $GLOBALS['xmlrpcInt']: // 'int'
  1491. case $GLOBALS['xmlrpcI4']:
  1492. return $GLOBALS['xmlrpcInt'];
  1493. case 'double':
  1494. return $GLOBALS['xmlrpcDouble'];
  1495. case 'boolean':
  1496. return $GLOBALS['xmlrpcBoolean'];
  1497. case 'array':
  1498. return $GLOBALS['xmlrpcArray'];
  1499. case 'object':
  1500. return $GLOBALS['xmlrpcStruct'];
  1501. //case $GLOBALS['xmlrpcBase64']:
  1502. case $GLOBALS['xmlrpcStruct']:
  1503. return strtolower($phptype);
  1504. case 'resource':
  1505. return '';
  1506. default:
  1507. if(class_exists($phptype))
  1508. {
  1509. return $GLOBALS['xmlrpcStruct'];
  1510. }
  1511. else
  1512. {
  1513. // unknown: might be any 'extended' jsonrpc type
  1514. return $GLOBALS['xmlrpcValue'];
  1515. }
  1516. }
  1517. }
  1518. ?>