JSON.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. <?php
  2. class Services_JSON
  3. {
  4. public function Services_JSON($use = 0)
  5. {
  6. $this->use = $use;
  7. }
  8. public function utf162utf8($utf16)
  9. {
  10. if (function_exists('mb_convert_encoding')) {
  11. return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
  12. }
  13. $bytes = ord($utf16[0]) << 8 | ord($utf16[1]);
  14. switch (true) {
  15. case (127 & $bytes) == $bytes:
  16. return chr(127 & $bytes);
  17. case (2047 & $bytes) == $bytes:
  18. return chr(192 | $bytes >> 6 & 31) . chr(128 | $bytes & 63);
  19. case (65535 & $bytes) == $bytes:
  20. return chr(224 | $bytes >> 12 & 15) . chr(128 | $bytes >> 6 & 63) . chr(128 | $bytes & 63);
  21. }
  22. return '';
  23. }
  24. public function utf82utf16($utf8)
  25. {
  26. if (function_exists('mb_convert_encoding')) {
  27. return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
  28. }
  29. switch (strlen($utf8)) {
  30. case 1:
  31. return $utf8;
  32. case 2:
  33. return chr(7 & ord($utf8[0]) >> 2) . chr(192 & ord($utf8[0]) << 6 | 63 & ord($utf8[1]));
  34. case 3:
  35. return chr(240 & ord($utf8[0]) << 4 | 15 & ord($utf8[1]) >> 2) . chr(192 & ord($utf8[1]) << 6 | 127 & ord($utf8[2]));
  36. }
  37. return '';
  38. }
  39. public function encode($var)
  40. {
  41. switch (gettype($var)) {
  42. case 'boolean':
  43. return $var ? 'true' : 'false';
  44. case 'NULL':
  45. return 'null';
  46. case 'integer':
  47. return (int) $var;
  48. case 'double':
  49. case 'float':
  50. return (double) $var;
  51. case 'string':
  52. $ascii = '';
  53. $strlen_var = strlen($var);
  54. for ($c = 0; $c < $strlen_var; ++$c) {
  55. $ord_var_c = ord($var[$c]);
  56. switch (true) {
  57. case $ord_var_c == 8:
  58. $ascii .= '\\b';
  59. break;
  60. case $ord_var_c == 9:
  61. $ascii .= '\\t';
  62. break;
  63. case $ord_var_c == 10:
  64. $ascii .= '\\n';
  65. break;
  66. case $ord_var_c == 12:
  67. $ascii .= '\\f';
  68. break;
  69. case $ord_var_c == 13:
  70. $ascii .= '\\r';
  71. break;
  72. case $ord_var_c == 34:
  73. case $ord_var_c == 47:
  74. case $ord_var_c == 92:
  75. $ascii .= '\\' . $var[$c];
  76. break;
  77. case 32 <= $ord_var_c && $ord_var_c <= 127:
  78. $ascii .= $var[$c];
  79. break;
  80. case ($ord_var_c & 224) == 192:
  81. $char = pack('C*', $ord_var_c, ord($var[$c + 1]));
  82. $c += 1;
  83. $utf16 = $this->utf82utf16($char);
  84. $ascii .= sprintf('\\u%04s', bin2hex($utf16));
  85. break;
  86. case ($ord_var_c & 240) == 224:
  87. $char = pack('C*', $ord_var_c, ord($var[$c + 1]), ord($var[$c + 2]));
  88. $c += 2;
  89. $utf16 = $this->utf82utf16($char);
  90. $ascii .= sprintf('\\u%04s', bin2hex($utf16));
  91. break;
  92. case ($ord_var_c & 248) == 240:
  93. $char = pack('C*', $ord_var_c, ord($var[$c + 1]), ord($var[$c + 2]), ord($var[$c + 3]));
  94. $c += 3;
  95. $utf16 = $this->utf82utf16($char);
  96. $ascii .= sprintf('\\u%04s', bin2hex($utf16));
  97. break;
  98. case ($ord_var_c & 252) == 248:
  99. $char = pack('C*', $ord_var_c, ord($var[$c + 1]), ord($var[$c + 2]), ord($var[$c + 3]), ord($var[$c + 4]));
  100. $c += 4;
  101. $utf16 = $this->utf82utf16($char);
  102. $ascii .= sprintf('\\u%04s', bin2hex($utf16));
  103. break;
  104. case ($ord_var_c & 254) == 252:
  105. $char = pack('C*', $ord_var_c, ord($var[$c + 1]), ord($var[$c + 2]), ord($var[$c + 3]), ord($var[$c + 4]), ord($var[$c + 5]));
  106. $c += 5;
  107. $utf16 = $this->utf82utf16($char);
  108. $ascii .= sprintf('\\u%04s', bin2hex($utf16));
  109. break;
  110. }
  111. }
  112. return '"' . $ascii . '"';
  113. case 'array':
  114. if (is_array($var) && count($var) && array_keys($var) !== range(0, sizeof($var) - 1)) {
  115. $properties = array_map(array($this, 'name_value'), array_keys($var), array_values($var));
  116. foreach ($properties as $property) {
  117. if (Services_JSON::isError($property)) {
  118. return $property;
  119. }
  120. }
  121. return '{' . join(',', $properties) . '}';
  122. }
  123. $elements = array_map(array($this, 'encode'), $var);
  124. foreach ($elements as $element) {
  125. if (Services_JSON::isError($element)) {
  126. return $element;
  127. }
  128. }
  129. return '[' . join(',', $elements) . ']';
  130. case 'object':
  131. $vars = get_object_vars($var);
  132. $properties = array_map(array($this, 'name_value'), array_keys($vars), array_values($vars));
  133. foreach ($properties as $property) {
  134. if (Services_JSON::isError($property)) {
  135. return $property;
  136. }
  137. }
  138. return '{' . join(',', $properties) . '}';
  139. default:
  140. return $this->use & SERVICES_JSON_SUPPRESS_ERRORS ? 'null' : new Services_JSON_Error(gettype($var) . ' can not be encoded as JSON string');
  141. }
  142. }
  143. public function name_value($name, $value)
  144. {
  145. $encoded_value = $this->encode($value);
  146. if (Services_JSON::isError($encoded_value)) {
  147. return $encoded_value;
  148. }
  149. return $this->encode(strval($name)) . ':' . $encoded_value;
  150. }
  151. public function reduce_string($str)
  152. {
  153. $str = preg_replace(array('#^\\s*//(.+)$#m', '#^\\s*/\\*(.+)\\*/#Us', '#/\\*(.+)\\*/\\s*$#Us'), '', $str);
  154. return trim($str);
  155. }
  156. public function decode($str)
  157. {
  158. $str = $this->reduce_string($str);
  159. switch (strtolower($str)) {
  160. case 'true':
  161. return true;
  162. case 'false':
  163. return false;
  164. case 'null':
  165. return NULL;
  166. default:
  167. $m = array();
  168. if (is_numeric($str)) {
  169. return (double) $str == (int) $str ? (int) $str : (double) $str;
  170. }
  171. else {
  172. if (preg_match('/^("|\').*(\\1)$/s', $str, $m) && $m[1] == $m[2]) {
  173. $delim = substr($str, 0, 1);
  174. $chrs = substr($str, 1, -1);
  175. $utf8 = '';
  176. $strlen_chrs = strlen($chrs);
  177. for ($c = 0; $c < $strlen_chrs; ++$c) {
  178. $substr_chrs_c_2 = substr($chrs, $c, 2);
  179. $ord_chrs_c = ord($chrs[$c]);
  180. switch (true) {
  181. case $substr_chrs_c_2 == '\\b':
  182. $utf8 .= chr(8);
  183. ++$c;
  184. break;
  185. case $substr_chrs_c_2 == '\\t':
  186. $utf8 .= chr(9);
  187. ++$c;
  188. break;
  189. case $substr_chrs_c_2 == '\\n':
  190. $utf8 .= chr(10);
  191. ++$c;
  192. break;
  193. case $substr_chrs_c_2 == '\\f':
  194. $utf8 .= chr(12);
  195. ++$c;
  196. break;
  197. case $substr_chrs_c_2 == '\\r':
  198. $utf8 .= chr(13);
  199. ++$c;
  200. break;
  201. case $substr_chrs_c_2 == '\\"':
  202. case $substr_chrs_c_2 == '\\\'':
  203. case $substr_chrs_c_2 == '\\\\':
  204. case $substr_chrs_c_2 == '\\/':
  205. if ($delim == '"' && $substr_chrs_c_2 != '\\\'' || $delim == '\'' && $substr_chrs_c_2 != '\\"') {
  206. $utf8 .= $chrs[++$c];
  207. }
  208. break;
  209. case preg_match('/\\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
  210. $utf16 = chr(hexdec(substr($chrs, $c + 2, 2))) . chr(hexdec(substr($chrs, $c + 4, 2)));
  211. $utf8 .= $this->utf162utf8($utf16);
  212. $c += 5;
  213. break;
  214. case 32 <= $ord_chrs_c && $ord_chrs_c <= 127:
  215. $utf8 .= $chrs[$c];
  216. break;
  217. case ($ord_chrs_c & 224) == 192:
  218. $utf8 .= substr($chrs, $c, 2);
  219. ++$c;
  220. break;
  221. case ($ord_chrs_c & 240) == 224:
  222. $utf8 .= substr($chrs, $c, 3);
  223. $c += 2;
  224. break;
  225. case ($ord_chrs_c & 248) == 240:
  226. $utf8 .= substr($chrs, $c, 4);
  227. $c += 3;
  228. break;
  229. case ($ord_chrs_c & 252) == 248:
  230. $utf8 .= substr($chrs, $c, 5);
  231. $c += 4;
  232. break;
  233. case ($ord_chrs_c & 254) == 252:
  234. $utf8 .= substr($chrs, $c, 6);
  235. $c += 5;
  236. break;
  237. }
  238. }
  239. return $utf8;
  240. }
  241. else {
  242. if (preg_match('/^\\[.*\\]$/s', $str) || preg_match('/^\\{.*\\}$/s', $str)) {
  243. if ($str[0] == '[') {
  244. $stk = array(SERVICES_JSON_IN_ARR);
  245. $arr = array();
  246. }
  247. else if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
  248. $stk = array(SERVICES_JSON_IN_OBJ);
  249. $obj = array();
  250. }
  251. else {
  252. $stk = array(SERVICES_JSON_IN_OBJ);
  253. $obj = new stdClass();
  254. }
  255. array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => 0, 'delim' => false));
  256. $chrs = substr($str, 1, -1);
  257. $chrs = $this->reduce_string($chrs);
  258. if ($chrs == '') {
  259. if (reset($stk) == SERVICES_JSON_IN_ARR) {
  260. return $arr;
  261. }
  262. else {
  263. return $obj;
  264. }
  265. }
  266. $strlen_chrs = strlen($chrs);
  267. for ($c = 0; $c <= $strlen_chrs; ++$c) {
  268. $top = end($stk);
  269. $substr_chrs_c_2 = substr($chrs, $c, 2);
  270. if ($c == $strlen_chrs || $chrs[$c] == ',' && $top['what'] == SERVICES_JSON_SLICE) {
  271. $slice = substr($chrs, $top['where'], $c - $top['where']);
  272. array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => $c + 1, 'delim' => false));
  273. if (reset($stk) == SERVICES_JSON_IN_ARR) {
  274. array_push($arr, $this->decode($slice));
  275. }
  276. else if (reset($stk) == SERVICES_JSON_IN_OBJ) {
  277. $parts = array();
  278. if (preg_match('/^\\s*(["\'].*[^\\\\]["\'])\\s*:\\s*(\\S.*),?$/Uis', $slice, $parts)) {
  279. $key = $this->decode($parts[1]);
  280. $val = $this->decode($parts[2]);
  281. if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
  282. $obj[$key] = $val;
  283. }
  284. else {
  285. $obj->{$key} = $val;
  286. }
  287. }
  288. else if (preg_match('/^\\s*(\\w+)\\s*:\\s*(\\S.*),?$/Uis', $slice, $parts)) {
  289. $key = $parts[1];
  290. $val = $this->decode($parts[2]);
  291. if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
  292. $obj[$key] = $val;
  293. }
  294. else {
  295. $obj->{$key} = $val;
  296. }
  297. }
  298. }
  299. }
  300. else {
  301. if (($chrs[$c] == '"' || $chrs[$c] == '\'') && $top['what'] != SERVICES_JSON_IN_STR) {
  302. array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c]));
  303. }
  304. else {
  305. if ($chrs[$c] == $top['delim'] && $top['what'] == SERVICES_JSON_IN_STR && (strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1) {
  306. array_pop($stk);
  307. }
  308. else {
  309. if ($chrs[$c] == '[' && in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
  310. array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
  311. }
  312. else {
  313. if ($chrs[$c] == ']' && $top['what'] == SERVICES_JSON_IN_ARR) {
  314. array_pop($stk);
  315. }
  316. else {
  317. if ($chrs[$c] == '{' && in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
  318. array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
  319. }
  320. else {
  321. if ($chrs[$c] == '}' && $top['what'] == SERVICES_JSON_IN_OBJ) {
  322. array_pop($stk);
  323. }
  324. else {
  325. if ($substr_chrs_c_2 == '/*' && in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
  326. array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
  327. $c++;
  328. }
  329. else {
  330. if ($substr_chrs_c_2 == '*/' && $top['what'] == SERVICES_JSON_IN_CMT) {
  331. array_pop($stk);
  332. $c++;
  333. for ($i = $top['where']; $i <= $c; ++$i) {
  334. $chrs = substr_replace($chrs, ' ', $i, 1);
  335. }
  336. }
  337. }
  338. }
  339. }
  340. }
  341. }
  342. }
  343. }
  344. }
  345. }
  346. if (reset($stk) == SERVICES_JSON_IN_ARR) {
  347. return $arr;
  348. }
  349. else if (reset($stk) == SERVICES_JSON_IN_OBJ) {
  350. return $obj;
  351. }
  352. }
  353. }
  354. }
  355. }
  356. }
  357. public function isError($data, $code = NULL)
  358. {
  359. if (class_exists('pear')) {
  360. return PEAR::isError($data, $code);
  361. }
  362. else {
  363. if (is_object($data) && (get_class($data) == 'services_json_error' || is_subclass_of($data, 'services_json_error'))) {
  364. return true;
  365. }
  366. }
  367. return false;
  368. }
  369. }
  370. define('SERVICES_JSON_SLICE', 1);
  371. define('SERVICES_JSON_IN_STR', 2);
  372. define('SERVICES_JSON_IN_ARR', 3);
  373. define('SERVICES_JSON_IN_OBJ', 4);
  374. define('SERVICES_JSON_IN_CMT', 5);
  375. define('SERVICES_JSON_LOOSE_TYPE', 16);
  376. define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
  377. if (class_exists('PEAR_Error')) {
  378. class Services_JSON_Error extends PEAR_Error
  379. {
  380. public function Services_JSON_Error($message = 'unknown error', $code = NULL, $mode = NULL, $options = NULL, $userinfo = NULL)
  381. {
  382. parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
  383. }
  384. }
  385. }
  386. else {
  387. class Services_JSON_Error
  388. {
  389. public function Services_JSON_Error($message = 'unknown error', $code = NULL, $mode = NULL, $options = NULL, $userinfo = NULL)
  390. {
  391. }
  392. }
  393. }
  394. ?>