jsonrpc.inc 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516
  1. <?php
  2. /**
  3. * @package modx
  4. * @subpackage jsonrpc
  5. */
  6. /**
  7. * JSON extension to the PHP-XMLRPC lib
  8. *
  9. * For more info see:
  10. * http://www.json.org/
  11. * http://json-rpc.org/
  12. *
  13. * @author Gaetano Giunta
  14. * @version $Id: jsonrpc.inc,v 1.30 2007/02/22 13:50:18 ggiunta Exp $
  15. * @copyright (c) 2005-2006 G. Giunta
  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 & 31);
  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 & 31);
  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. if ($return_phpvals && $use_extension)
  734. {
  735. $ok = json_parse_native($data);
  736. }
  737. else
  738. {
  739. $ok = json_parse($data, $return_phpvals, $src_encoding);
  740. }
  741. if ($ok)
  742. {
  743. if (!$return_phpvals)
  744. $GLOBALS['_xh']['value'] = @$GLOBALS['_xh']['value']->me['struct'];
  745. if (!is_array($GLOBALS['_xh']['value']) || !array_key_exists('method', $GLOBALS['_xh']['value'])
  746. || !array_key_exists('params', $GLOBALS['_xh']['value']) || !array_key_exists('id', $GLOBALS['_xh']['value']))
  747. {
  748. $GLOBALS['_xh']['isf_reason'] = 'JSON parsing did not return correct jsonrpc request object';
  749. return false;
  750. }
  751. else
  752. {
  753. $GLOBALS['_xh']['method'] = $GLOBALS['_xh']['value']['method'];
  754. $GLOBALS['_xh']['params'] = $GLOBALS['_xh']['value']['params'];
  755. $GLOBALS['_xh']['id'] = $GLOBALS['_xh']['value']['id'];
  756. if (!$return_phpvals)
  757. {
  758. /// @todo we should check for appropriate type for method name and params array...
  759. $GLOBALS['_xh']['method'] = $GLOBALS['_xh']['method']->scalarval();
  760. $GLOBALS['_xh']['params'] = $GLOBALS['_xh']['params']->me['array'];
  761. $GLOBALS['_xh']['id'] = php_jsonrpc_decode($GLOBALS['_xh']['id']);
  762. }
  763. return true;
  764. }
  765. }
  766. else
  767. {
  768. return false;
  769. }
  770. }
  771. /**
  772. * Parse a json string, expected to be in json-rpc response format.
  773. * @access private
  774. * @todo checks missing:
  775. * - no extra members in response
  776. * - no extra members in error struct
  777. * - resp. ID validation
  778. */
  779. function jsonrpc_parse_resp($data, $return_phpvals=false, $use_extension=false, $src_encoding='')
  780. {
  781. $GLOBALS['_xh']['isf']=0;
  782. $GLOBALS['_xh']['isf_reason']='';
  783. if ($return_phpvals && $use_extension)
  784. {
  785. $ok = json_parse_native($data);
  786. }
  787. else
  788. {
  789. $ok = json_parse($data, $return_phpvals, $src_encoding);
  790. }
  791. if ($ok)
  792. {
  793. if (!$return_phpvals)
  794. {
  795. $GLOBALS['_xh']['value'] = @$GLOBALS['_xh']['value']->me['struct'];
  796. }
  797. if (!is_array($GLOBALS['_xh']['value']) || !array_key_exists('result', $GLOBALS['_xh']['value'])
  798. || !array_key_exists('error', $GLOBALS['_xh']['value']) || !array_key_exists('id', $GLOBALS['_xh']['value']))
  799. {
  800. //$GLOBALS['_xh']['isf'] = 2;
  801. $GLOBALS['_xh']['isf_reason'] = 'JSON parsing did not return correct jsonrpc response object';
  802. return false;
  803. }
  804. if (!$return_phpvals)
  805. {
  806. $d_error = php_jsonrpc_decode($GLOBALS['_xh']['value']['error']);
  807. $GLOBALS['_xh']['value']['id'] = php_jsonrpc_decode($GLOBALS['_xh']['value']['id']);
  808. }
  809. else
  810. {
  811. $d_error = $GLOBALS['_xh']['value']['error'];
  812. }
  813. $GLOBALS['_xh']['id'] = $GLOBALS['_xh']['value']['id'];
  814. if ($d_error != null)
  815. {
  816. $GLOBALS['_xh']['isf'] = 1;
  817. //$GLOBALS['_xh']['value'] = $d_error;
  818. if (is_array($d_error) && array_key_exists('faultCode', $d_error)
  819. && array_key_exists('faultString', $d_error))
  820. {
  821. if($d_error['faultCode'] == 0)
  822. {
  823. // FAULT returned, errno needs to reflect that
  824. $d_error['faultCode'] = -1;
  825. }
  826. $GLOBALS['_xh']['value'] = $d_error;
  827. }
  828. // NB: what about jsonrpc servers that do NOT respect
  829. // the faultCode/faultString convention???
  830. // we force the error into a string. regardless of type...
  831. else //if (is_string($GLOBALS['_xh']['value']))
  832. {
  833. if ($return_phpvals)
  834. {
  835. $GLOBALS['_xh']['value'] = array('faultCode' => -1, 'faultString' => var_export($GLOBALS['_xh']['value']['error'], true));
  836. }
  837. else
  838. {
  839. $GLOBALS['_xh']['value'] = array('faultCode' => -1, 'faultString' => serialize_jsonrpcval($GLOBALS['_xh']['value']['error']));
  840. }
  841. }
  842. }
  843. else
  844. {
  845. $GLOBALS['_xh']['value'] = $GLOBALS['_xh']['value']['result'];
  846. }
  847. return true;
  848. }
  849. else
  850. {
  851. return false;
  852. }
  853. }
  854. class jsonrpc_client extends xmlrpc_client
  855. {
  856. // by default, no multicall exists for JSON-RPC, so do not try it
  857. var $no_multicall = true;
  858. // default return type of calls to json-rpc servers: jsonrpcvals
  859. var $return_type = 'jsonrpcvals';
  860. /*
  861. function jsonrpc_client($path, $server='', $port='', $method='')
  862. {
  863. $this->xmlrpc_client($path, $server, $port, $method);
  864. // we need to override the list of std supported encodings, since
  865. // according to ECMA-262, the standard charset is UTF-16
  866. $this->accepted_charset_encodings = array('UTF-16', 'UTF-8', 'ISO-8859-1', 'US-ASCII');
  867. }
  868. */
  869. }
  870. class jsonrpcmsg extends xmlrpcmsg
  871. {
  872. var $id = null; // used to store request ID internally
  873. var $content_type = 'application/json';
  874. /**
  875. * @param string $meth the name of the method to invoke
  876. * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
  877. * @param mixed $id the id of the jsonrpc request
  878. */
  879. function jsonrpcmsg($meth, $pars=0, $id=null)
  880. {
  881. // NB: a NULL id is allowed and has a very definite meaning!
  882. $this->id = $id;
  883. $this->xmlrpcmsg($meth, $pars);
  884. }
  885. /**
  886. * @access private
  887. */
  888. function createPayload($charset_encoding='')
  889. {
  890. if ($charset_encoding != '')
  891. $this->content_type = 'application/json; charset=' . $charset_encoding;
  892. else
  893. $this->content_type = 'application/json';
  894. // @ todo: verify if all chars are allowed for method names or can
  895. // we just skip the js encoding on it?
  896. $this->payload = "{\n\"method\": \"" . json_encode_entities($this->methodname, '', $charset_encoding) . "\",\n\"params\": [ ";
  897. for($i = 0; $i < sizeof($this->params); $i++)
  898. {
  899. $p = $this->params[$i];
  900. // MB: we try to force serialization as json even though the object
  901. // param might be a plain xmlrpcval object.
  902. // This way we do not need to override addParam, aren't we lazy?
  903. $this->payload .= "\n " . serialize_jsonrpcval($p, $charset_encoding) .
  904. ",";
  905. }
  906. $this->payload = substr($this->payload, 0, -1) . "\n],\n\"id\": ";
  907. switch (true)
  908. {
  909. case $this->id === null:
  910. $this->payload .= 'null';
  911. break;
  912. case is_string($this->id):
  913. $this->payload .= '"'.json_encode_entities($this->id, '', $charset_encoding).'"';
  914. break;
  915. case is_bool($this->id):
  916. $this->payload .= ($this->id ? 'true' : 'false');
  917. break;
  918. default:
  919. $this->payload .= $this->id;
  920. }
  921. $this->payload .= "\n}\n";
  922. }
  923. /**
  924. * Parse the jsonrpc response contained in the string $data and return a jsonrpcresp object.
  925. * @param string $data the xmlrpc response, eventually including http headers
  926. * @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and conseuqent decoding
  927. * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
  928. * @return jsonrpcresp
  929. * @access private
  930. */
  931. function &parseResponse($data='', $headers_processed=false, $return_type='jsonrpcvals')
  932. {
  933. if($this->debug)
  934. {
  935. print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
  936. }
  937. if($data == '')
  938. {
  939. error_log('XML-RPC: jsonrpcmsg::parseResponse: no response received from server.');
  940. $r = new jsonrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
  941. return $r;
  942. }
  943. $GLOBALS['_xh']=array();
  944. $raw_data = $data;
  945. // parse the HTTP headers of the response, if present, and separate them from data
  946. if(substr($data, 0, 4) == 'HTTP')
  947. {
  948. $r =& $this->parseResponseHeaders($data, $headers_processed);
  949. if ($r)
  950. {
  951. // parent class implementation of parseResponseHeaders returns in case
  952. // of error an object of the wrong type: recode it into correct object
  953. $rj = new jsonrpcresp(0, $r->faultCode(), $r->faultString());
  954. $rj->raw_data = $data;
  955. return $rj;
  956. }
  957. }
  958. else
  959. {
  960. $GLOBALS['_xh']['headers'] = array();
  961. $GLOBALS['_xh']['cookies'] = array();
  962. }
  963. if($this->debug)
  964. {
  965. $start = strpos($data, '/* SERVER DEBUG INFO (BASE64 ENCODED):');
  966. if ($start !== false)
  967. {
  968. $start += strlen('/* SERVER DEBUG INFO (BASE64 ENCODED):');
  969. $end = strpos($data, '*/', $start);
  970. $comments = substr($data, $start, $end-$start);
  971. print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
  972. }
  973. }
  974. // be tolerant of extra whitespace in response body
  975. $data = trim($data);
  976. // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
  977. $end = strrpos($data, '}');
  978. if ($end)
  979. {
  980. $data = substr($data, 0, $end+1);
  981. }
  982. // if user wants back raw json, give it to him
  983. if ($return_type == 'json')
  984. {
  985. $r = new jsonrpcresp($data, 0, '', 'json');
  986. $r->hdrs = $GLOBALS['_xh']['headers'];
  987. $r->_cookies = $GLOBALS['_xh']['cookies'];
  988. $r->raw_data = $raw_data;
  989. return $r;
  990. }
  991. // @todo shall we try to check for non-unicode json received ???
  992. if (!jsonrpc_parse_resp($data, $return_type=='phpvals'))
  993. {
  994. if ($this->debug)
  995. {
  996. /// @todo echo something for user?
  997. }
  998. $r = new jsonrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
  999. $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
  1000. }
  1001. //elseif ($return_type == 'jsonrpcvals' && !is_object($GLOBALS['_xh']['value']))
  1002. //{
  1003. // then something odd has happened
  1004. // and it's time to generate a client side error
  1005. // indicating something odd went on
  1006. // $r = & new jsonrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
  1007. // $GLOBALS['xmlrpcstr']['invalid_return']);
  1008. //}
  1009. else
  1010. {
  1011. $v = $GLOBALS['_xh']['value'];
  1012. if ($this->debug)
  1013. {
  1014. print "<PRE>---PARSED---\n" ;
  1015. var_export($v);
  1016. print "\n---END---</PRE>";
  1017. }
  1018. if($GLOBALS['_xh']['isf'])
  1019. {
  1020. $r = new jsonrpcresp(0, $v['faultCode'], $v['faultString']);
  1021. }
  1022. else
  1023. {
  1024. $r = new jsonrpcresp($v, 0, '', $return_type);
  1025. }
  1026. $r->id = $GLOBALS['_xh']['id'];
  1027. }
  1028. $r->hdrs = $GLOBALS['_xh']['headers'];
  1029. $r->_cookies = $GLOBALS['_xh']['cookies'];
  1030. $r->raw_data = $raw_data;
  1031. return $r;
  1032. }
  1033. }
  1034. class jsonrpcresp extends xmlrpcresp
  1035. {
  1036. var $content_type = 'application/json'; // NB: forces us to send US-ASCII over http
  1037. var $id = null;
  1038. /// @todo override creator, to set proper valtyp and id!
  1039. /**
  1040. * Returns json representation of the response.
  1041. * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
  1042. * @return string the json representation of the response
  1043. * @access public
  1044. */
  1045. function serialize($charset_encoding='')
  1046. {
  1047. if ($charset_encoding != '')
  1048. $this->content_type = 'application/json; charset=' . $charset_encoding;
  1049. else
  1050. $this->content_type = 'application/json';
  1051. $this->payload = serialize_jsonrpcresp($this, $this->id, $charset_encoding);
  1052. return $this->payload;
  1053. }
  1054. }
  1055. class jsonrpcval extends xmlrpcval
  1056. {
  1057. /**
  1058. * Returns json representation of the value.
  1059. * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
  1060. * @return string
  1061. * @access public
  1062. */
  1063. function serialize($charset_encoding='')
  1064. {
  1065. return serialize_jsonrpcval($this, $charset_encoding);
  1066. }
  1067. }
  1068. /**
  1069. * Takes a json value in PHP jsonrpcval object format
  1070. * and translates it into native PHP types.
  1071. *
  1072. * @param jsonrpcval $jsonrpc_val
  1073. * @param array $options if 'decode_php_objs' is set in the options array, jsonrpc objects can be decoded into php objects
  1074. * @return mixed
  1075. * @access public
  1076. */
  1077. function php_jsonrpc_decode($jsonrpc_val, $options=array())
  1078. {
  1079. $kind = $jsonrpc_val->kindOf();
  1080. if($kind == 'scalar')
  1081. {
  1082. return $jsonrpc_val->scalarval();
  1083. }
  1084. elseif($kind == 'array')
  1085. {
  1086. $size = $jsonrpc_val->arraysize();
  1087. $arr = array();
  1088. for($i = 0; $i < $size; $i++)
  1089. {
  1090. $arr[] = php_jsonrpc_decode($jsonrpc_val->arraymem($i), $options);
  1091. }
  1092. return $arr;
  1093. }
  1094. elseif($kind == 'struct')
  1095. {
  1096. $jsonrpc_val->structreset();
  1097. // If user said so, try to rebuild php objects for specific struct vals.
  1098. /// @todo should we raise a warning for class not found?
  1099. // shall we check for proper subclass of xmlrpcval instead of
  1100. // presence of _php_class to detect what we can do?
  1101. if (in_array('decode_php_objs', $options))
  1102. {
  1103. if( $jsonrpc_val->_php_class != ''
  1104. && class_exists($jsonrpc_val->_php_class))
  1105. {
  1106. $obj = @new $jsonrpc_val->_php_class;
  1107. }
  1108. else
  1109. {
  1110. $obj = new stdClass();
  1111. }
  1112. while(list($key,$value) = $jsonrpc_val->structeach())
  1113. {
  1114. $obj->$key = php_jsonrpc_decode($value, $options);
  1115. }
  1116. return $obj;
  1117. }
  1118. else
  1119. {
  1120. $arr = array();
  1121. while(list($key,$value) = $jsonrpc_val->structeach())
  1122. {
  1123. $arr[$key] = php_jsonrpc_decode($value, $options);
  1124. }
  1125. return $arr;
  1126. }
  1127. }
  1128. }
  1129. /**
  1130. * Takes native php types and encodes them into jsonrpc PHP object format.
  1131. * It will not re-encode jsonrpcval objects.
  1132. *
  1133. * @param mixed $php_val the value to be converted into a jsonrpcval object
  1134. * @param array $options can include 'encode_php_objs'
  1135. * @return jsonrpcval
  1136. * @access public
  1137. */
  1138. function &php_jsonrpc_encode($php_val, $options='')
  1139. {
  1140. $type = gettype($php_val);
  1141. switch($type)
  1142. {
  1143. case 'string':
  1144. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcString']);
  1145. break;
  1146. case 'integer':
  1147. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcInt']);
  1148. break;
  1149. case 'double':
  1150. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcDouble']);
  1151. break;
  1152. case 'boolean':
  1153. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
  1154. break;
  1155. case 'resource': // for compat with php json extension...
  1156. case 'NULL':
  1157. $jsonrpc_val = new jsonrpcval($php_val, $GLOBALS['xmlrpcNull']);
  1158. break;
  1159. case 'array':
  1160. // PHP arrays can be encoded to either objects or arrays,
  1161. // depending on wheter they are hashes or plain 0..n integer indexed
  1162. // A shorter one-liner would be
  1163. // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
  1164. // but execution time skyrockets!
  1165. $j = 0;
  1166. $arr = array();
  1167. $ko = false;
  1168. foreach($php_val as $key => $val)
  1169. {
  1170. $arr[$key] =& php_jsonrpc_encode($val, $options);
  1171. if(!$ko && $key !== $j)
  1172. {
  1173. $ko = true;
  1174. }
  1175. $j++;
  1176. }
  1177. if($ko)
  1178. {
  1179. $jsonrpc_val = new jsonrpcval($arr, $GLOBALS['xmlrpcStruct']);
  1180. }
  1181. else
  1182. {
  1183. $jsonrpc_val = new jsonrpcval($arr, $GLOBALS['xmlrpcArray']);
  1184. }
  1185. break;
  1186. case 'object':
  1187. if($php_val instanceof jsonrpcval)
  1188. {
  1189. $jsonrpc_val = $php_val;
  1190. }
  1191. else
  1192. {
  1193. $arr = array();
  1194. while(list($k,$v) = each($php_val))
  1195. {
  1196. $arr[$k] = php_jsonrpc_encode($v, $options);
  1197. }
  1198. $jsonrpc_val = new jsonrpcval($arr, $GLOBALS['xmlrpcStruct']);
  1199. if (in_array('encode_php_objs', $options))
  1200. {
  1201. // let's save original class name into xmlrpcval:
  1202. // might be useful later on...
  1203. $jsonrpc_val->_php_class = get_class($php_val);
  1204. }
  1205. }
  1206. break;
  1207. // catch "user function", "unknown type"
  1208. default:
  1209. $jsonrpc_val = new jsonrpcval();
  1210. break;
  1211. }
  1212. return $jsonrpc_val;
  1213. }
  1214. /**
  1215. * Convert the json representation of a jsonrpc method call, jsonrpc method response
  1216. * or single json value into the appropriate object (a.k.a. deserialize).
  1217. * Please note that there is no way to distinguish the serialized representation
  1218. * of a single json val of type object which has the 3 appropriate members from
  1219. * the serialization of a method call or method response.
  1220. * In such a case, the function will return a jsonrpcresp or jsonrpcmsg
  1221. * @param string $json_val
  1222. * @param array $options
  1223. * @return mixed false on error, or an instance of jsonrpcval, jsonrpcresp or jsonrpcmsg
  1224. * @access public
  1225. * @todo add options controlling character set encodings
  1226. */
  1227. function php_jsonrpc_decode_json($json_val, $options=array())
  1228. {
  1229. $src_encoding = array_key_exists('src_encoding', $options) ? $options['src_encoding'] : $GLOBALS['xmlrpc_defencoding'];
  1230. $dest_encoding = array_key_exists('dest_encoding', $options) ? $options['dest_encoding'] : $GLOBALS['xmlrpc_internalencoding'];
  1231. //$GLOBALS['_xh'] = array();
  1232. $GLOBALS['_xh']['isf'] = 0;
  1233. if (!json_parse($json_val, false, $src_encoding, $dest_encoding))
  1234. {
  1235. error_log($GLOBALS['_xh']['isf_reason']);
  1236. return false;
  1237. }
  1238. else
  1239. {
  1240. $val = $GLOBALS['_xh']['value']; // shortcut
  1241. if ($GLOBALS['_xh']['value']->kindOf() == 'struct')
  1242. {
  1243. if ($GLOBALS['_xh']['value']->structSize() == 3)
  1244. {
  1245. if ($GLOBALS['_xh']['value']->structMemExists('method') &&
  1246. $GLOBALS['_xh']['value']->structMemExists('params') &&
  1247. $GLOBALS['_xh']['value']->structMemExists('id'))
  1248. {
  1249. /// @todo we do not check for correct type of 'method', 'params' struct members...
  1250. $method = $GLOBALS['_xh']['value']->structMem('method');
  1251. $msg = new jsonrpcmsg($method->scalarval(), null, php_jsonrpc_decode($GLOBALS['_xh']['value']->structMem('id')));
  1252. $params = $GLOBALS['_xh']['value']->structMem('params');
  1253. for($i = 0; $i < $params->arraySize(); ++$i)
  1254. {
  1255. $msg->addparam($params->arrayMem($i));
  1256. }
  1257. return $msg;
  1258. }
  1259. else
  1260. if ($GLOBALS['_xh']['value']->structMemExists('result') &&
  1261. $GLOBALS['_xh']['value']->structMemExists('error') &&
  1262. $GLOBALS['_xh']['value']->structMemExists('id'))
  1263. {
  1264. $id = php_jsonrpc_decode($GLOBALS['_xh']['value']->structMem('id'));
  1265. $err = php_jsonrpc_decode($GLOBALS['_xh']['value']->structMem('error'));
  1266. if ($err == null)
  1267. {
  1268. $resp = new jsonrpcresp($GLOBALS['_xh']['value']->structMem('result'));
  1269. }
  1270. else
  1271. {
  1272. if (is_array($err) && array_key_exists('faultCode', $err)
  1273. && array_key_exists('faultString', $err))
  1274. {
  1275. if($err['faultCode'] == 0)
  1276. {
  1277. // FAULT returned, errno needs to reflect that
  1278. $err['faultCode'] = -1;
  1279. }
  1280. }
  1281. // NB: what about jsonrpc servers that do NOT respect
  1282. // the faultCode/faultString convention???
  1283. // we force the error into a string. regardless of type...
  1284. else //if (is_string($GLOBALS['_xh']['value']))
  1285. {
  1286. $err = array('faultCode' => -1, 'faultString' => serialize_jsonrpcval($GLOBALS['_xh']['value']->structMem('error')));
  1287. }
  1288. $resp = new jsonrpcresp(0, $err['faultCode'], $err['faultString']);
  1289. }
  1290. $resp->id = $id;
  1291. return $resp;
  1292. }
  1293. }
  1294. }
  1295. // not a request msg nor a response: a plain jsonrpcval obj
  1296. return $GLOBALS['_xh']['value'];
  1297. }
  1298. }
  1299. /**
  1300. * Serialize a jsonrpcresp (or xmlrpcresp) as json.
  1301. * Moved outside of the corresponding class to ease multi-serialization of
  1302. * xmlrpcresp objects
  1303. * @param xmlrpcresp or jsonrpcresp $resp
  1304. * @param mixed $id
  1305. * @return string
  1306. * @access private
  1307. */
  1308. function serialize_jsonrpcresp($resp, $id=null, $charset_encoding='')
  1309. {
  1310. $result = "{\n\"id\": ";
  1311. switch (true)
  1312. {
  1313. case $id === null:
  1314. $result .= 'null';
  1315. break;
  1316. case is_string($id):
  1317. $result .= '"'.json_encode_entities($id, '', $charset_encoding).'"';
  1318. break;
  1319. case is_bool($id):
  1320. $result .= ($id ? 'true' : 'false');
  1321. break;
  1322. default:
  1323. $result .= $id;
  1324. }
  1325. $result .= ", ";
  1326. if($resp->errno)
  1327. {
  1328. // let non-ASCII response messages be tolerated by clients
  1329. // by encoding non ascii chars
  1330. $result .= "\"error\": { \"faultCode\": " . $resp->errno . ", \"faultString\": \"" . json_encode_entities($resp->errstr, null, $charset_encoding) . "\" }, \"result\": null";
  1331. }
  1332. else
  1333. {
  1334. if(!is_object($resp->val) || !($resp->val instanceof xmlrpcval))
  1335. {
  1336. if (is_string($resp->val) && $resp->valtyp == 'json')
  1337. {
  1338. $result .= "\"error\": null, \"result\": " . $resp->val;
  1339. }
  1340. else
  1341. {
  1342. /// @todo try to build something serializable?
  1343. die('cannot serialize jsonrpcresp objects whose content is native php values');
  1344. }
  1345. }
  1346. else
  1347. {
  1348. $result .= "\"error\": null, \"result\": " .
  1349. serialize_jsonrpcval($resp->val, $charset_encoding);
  1350. }
  1351. }
  1352. $result .= "\n}";
  1353. return $result;
  1354. }
  1355. /**
  1356. * Serialize a jsonrpcval (or xmlrpcval) as json.
  1357. * Moved outside of the corresponding class to ease multi-serialization of
  1358. * xmlrpcval objects
  1359. * @param xmlrpcval or jsonrpcval $value
  1360. * @string $charset_encoding
  1361. * @access private
  1362. */
  1363. function serialize_jsonrpcval($value, $charset_encoding='')
  1364. {
  1365. reset($value->me);
  1366. list($typ, $val) = each($value->me);
  1367. $rs = '';
  1368. switch(@$GLOBALS['xmlrpcTypes'][$typ])
  1369. {
  1370. case 1:
  1371. switch($typ)
  1372. {
  1373. case $GLOBALS['xmlrpcString']:
  1374. $rs .= '"' . json_encode_entities($val, null, $charset_encoding). '"';
  1375. break;
  1376. case $GLOBALS['xmlrpcI4']:
  1377. case $GLOBALS['xmlrpcInt']:
  1378. $rs .= (int)$val;
  1379. break;
  1380. case $GLOBALS['xmlrpcDateTime']:
  1381. // quote date as a json string.
  1382. // assumes date format is valid and will not break js...
  1383. $rs .= '"' . $val . '"';
  1384. break;
  1385. case $GLOBALS['xmlrpcDouble']:
  1386. // add a .0 in case value is integer.
  1387. // This helps us carrying around floats in js, and keep them separated from ints
  1388. $sval = strval((double)$val); // convert to string
  1389. if (strpos($sval, '.') !== false || strpos($sval, 'e') !== false)
  1390. {
  1391. $rs .= $sval;
  1392. }
  1393. else
  1394. {
  1395. $rs .= $val.'.0';
  1396. }
  1397. break;
  1398. case $GLOBALS['xmlrpcBoolean']:
  1399. $rs .= ($val ? 'true' : 'false');
  1400. break;
  1401. case $GLOBALS['xmlrpcBase64']:
  1402. // treat base 64 values as strings ???
  1403. $rs .= '"' . base64_encode($val) . '"';
  1404. break;
  1405. default:
  1406. $rs .= "null";
  1407. }
  1408. break;
  1409. case 2:
  1410. // array
  1411. $rs .= "[";
  1412. $len = sizeof($val);
  1413. if ($len)
  1414. {
  1415. for($i = 0; $i < $len; $i++)
  1416. {
  1417. $rs .= serialize_jsonrpcval($val[$i], $charset_encoding);
  1418. $rs .= ",";
  1419. }
  1420. $rs = substr($rs, 0, -1) . "]";
  1421. }
  1422. else
  1423. {
  1424. $rs .= "]";
  1425. }
  1426. break;
  1427. case 3:
  1428. // struct
  1429. //if ($value->_php_class)
  1430. //{
  1431. /// @todo implement json-rpc extension for object serialization
  1432. //$rs.='<struct php_class="' . $this->_php_class . "\">\n";
  1433. //}
  1434. //else
  1435. //{
  1436. //}
  1437. if (is_array($val)) {
  1438. foreach($val as $key2 => $val2)
  1439. {
  1440. $rs .= ',"'.json_encode_entities($key2, null, $charset_encoding).'":';
  1441. $rs .= serialize_jsonrpcval($val2, $charset_encoding);
  1442. }
  1443. }
  1444. $rs = '{' . substr($rs, 1) . '}';
  1445. break;
  1446. default:
  1447. break;
  1448. }
  1449. return $rs;
  1450. }
  1451. ?>