json_encoder.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2018 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Author: Omar Kilani <omar@php.net> |
  16. | Jakub Zelenka <bukka@php.net> |
  17. +----------------------------------------------------------------------+
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include "config.h"
  21. #endif
  22. #include "php.h"
  23. #include "php_ini.h"
  24. #include "ext/standard/info.h"
  25. #include "ext/standard/html.h"
  26. #include "zend_smart_str.h"
  27. #include "php_json.h"
  28. #include "php_json_encoder.h"
  29. #include <zend_exceptions.h>
  30. static const char digits[] = "0123456789abcdef";
  31. static int php_json_escape_string(
  32. smart_str *buf, const char *s, size_t len,
  33. int options, php_json_encoder *encoder);
  34. static int php_json_determine_array_type(zval *val) /* {{{ */
  35. {
  36. int i;
  37. HashTable *myht = Z_ARRVAL_P(val);
  38. i = myht ? zend_hash_num_elements(myht) : 0;
  39. if (i > 0) {
  40. zend_string *key;
  41. zend_ulong index, idx;
  42. if (HT_IS_PACKED(myht) && HT_IS_WITHOUT_HOLES(myht)) {
  43. return PHP_JSON_OUTPUT_ARRAY;
  44. }
  45. idx = 0;
  46. ZEND_HASH_FOREACH_KEY(myht, index, key) {
  47. if (key) {
  48. return PHP_JSON_OUTPUT_OBJECT;
  49. } else {
  50. if (index != idx) {
  51. return PHP_JSON_OUTPUT_OBJECT;
  52. }
  53. }
  54. idx++;
  55. } ZEND_HASH_FOREACH_END();
  56. }
  57. return PHP_JSON_OUTPUT_ARRAY;
  58. }
  59. /* }}} */
  60. /* {{{ Pretty printing support functions */
  61. static inline void php_json_pretty_print_char(smart_str *buf, int options, char c) /* {{{ */
  62. {
  63. if (options & PHP_JSON_PRETTY_PRINT) {
  64. smart_str_appendc(buf, c);
  65. }
  66. }
  67. /* }}} */
  68. static inline void php_json_pretty_print_indent(smart_str *buf, int options, php_json_encoder *encoder) /* {{{ */
  69. {
  70. int i;
  71. if (options & PHP_JSON_PRETTY_PRINT) {
  72. for (i = 0; i < encoder->depth; ++i) {
  73. smart_str_appendl(buf, " ", 4);
  74. }
  75. }
  76. }
  77. /* }}} */
  78. /* }}} */
  79. static inline int php_json_is_valid_double(double d) /* {{{ */
  80. {
  81. return !zend_isinf(d) && !zend_isnan(d);
  82. }
  83. /* }}} */
  84. static inline void php_json_encode_double(smart_str *buf, double d, int options) /* {{{ */
  85. {
  86. size_t len;
  87. char num[PHP_DOUBLE_MAX_LENGTH];
  88. php_gcvt(d, (int)PG(serialize_precision), '.', 'e', num);
  89. len = strlen(num);
  90. if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < PHP_DOUBLE_MAX_LENGTH - 2) {
  91. num[len++] = '.';
  92. num[len++] = '0';
  93. num[len] = '\0';
  94. }
  95. smart_str_appendl(buf, num, len);
  96. }
  97. /* }}} */
  98. #define PHP_JSON_HASH_PROTECT_RECURSION(_tmp_ht) \
  99. do { \
  100. if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \
  101. GC_PROTECT_RECURSION(_tmp_ht); \
  102. } \
  103. } while (0)
  104. #define PHP_JSON_HASH_UNPROTECT_RECURSION(_tmp_ht) \
  105. do { \
  106. if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \
  107. GC_UNPROTECT_RECURSION(_tmp_ht); \
  108. } \
  109. } while (0)
  110. static int php_json_encode_array(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
  111. {
  112. int i, r, need_comma = 0;
  113. HashTable *myht;
  114. if (Z_TYPE_P(val) == IS_ARRAY) {
  115. myht = Z_ARRVAL_P(val);
  116. r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : php_json_determine_array_type(val);
  117. } else {
  118. myht = Z_OBJPROP_P(val);
  119. r = PHP_JSON_OUTPUT_OBJECT;
  120. }
  121. if (myht && GC_IS_RECURSIVE(myht)) {
  122. encoder->error_code = PHP_JSON_ERROR_RECURSION;
  123. smart_str_appendl(buf, "null", 4);
  124. return FAILURE;
  125. }
  126. PHP_JSON_HASH_PROTECT_RECURSION(myht);
  127. if (r == PHP_JSON_OUTPUT_ARRAY) {
  128. smart_str_appendc(buf, '[');
  129. } else {
  130. smart_str_appendc(buf, '{');
  131. }
  132. ++encoder->depth;
  133. i = myht ? zend_hash_num_elements(myht) : 0;
  134. if (i > 0) {
  135. zend_string *key;
  136. zval *data;
  137. zend_ulong index;
  138. ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
  139. if (r == PHP_JSON_OUTPUT_ARRAY) {
  140. if (need_comma) {
  141. smart_str_appendc(buf, ',');
  142. } else {
  143. need_comma = 1;
  144. }
  145. php_json_pretty_print_char(buf, options, '\n');
  146. php_json_pretty_print_indent(buf, options, encoder);
  147. } else if (r == PHP_JSON_OUTPUT_OBJECT) {
  148. if (key) {
  149. if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) {
  150. /* Skip protected and private members. */
  151. continue;
  152. }
  153. if (need_comma) {
  154. smart_str_appendc(buf, ',');
  155. } else {
  156. need_comma = 1;
  157. }
  158. php_json_pretty_print_char(buf, options, '\n');
  159. php_json_pretty_print_indent(buf, options, encoder);
  160. if (php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key),
  161. options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE &&
  162. (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) &&
  163. buf->s) {
  164. ZSTR_LEN(buf->s) -= 4;
  165. smart_str_appendl(buf, "\"\"", 2);
  166. }
  167. } else {
  168. if (need_comma) {
  169. smart_str_appendc(buf, ',');
  170. } else {
  171. need_comma = 1;
  172. }
  173. php_json_pretty_print_char(buf, options, '\n');
  174. php_json_pretty_print_indent(buf, options, encoder);
  175. smart_str_appendc(buf, '"');
  176. smart_str_append_long(buf, (zend_long) index);
  177. smart_str_appendc(buf, '"');
  178. }
  179. smart_str_appendc(buf, ':');
  180. php_json_pretty_print_char(buf, options, ' ');
  181. }
  182. if (php_json_encode_zval(buf, data, options, encoder) == FAILURE &&
  183. !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
  184. PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
  185. return FAILURE;
  186. }
  187. } ZEND_HASH_FOREACH_END();
  188. }
  189. PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
  190. if (encoder->depth > encoder->max_depth) {
  191. encoder->error_code = PHP_JSON_ERROR_DEPTH;
  192. if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
  193. return FAILURE;
  194. }
  195. }
  196. --encoder->depth;
  197. /* Only keep closing bracket on same line for empty arrays/objects */
  198. if (need_comma) {
  199. php_json_pretty_print_char(buf, options, '\n');
  200. php_json_pretty_print_indent(buf, options, encoder);
  201. }
  202. if (r == PHP_JSON_OUTPUT_ARRAY) {
  203. smart_str_appendc(buf, ']');
  204. } else {
  205. smart_str_appendc(buf, '}');
  206. }
  207. return SUCCESS;
  208. }
  209. /* }}} */
  210. static int php_json_escape_string(
  211. smart_str *buf, const char *s, size_t len,
  212. int options, php_json_encoder *encoder) /* {{{ */
  213. {
  214. int status;
  215. unsigned int us;
  216. size_t pos, checkpoint;
  217. char *dst;
  218. if (len == 0) {
  219. smart_str_appendl(buf, "\"\"", 2);
  220. return SUCCESS;
  221. }
  222. if (options & PHP_JSON_NUMERIC_CHECK) {
  223. double d;
  224. int type;
  225. zend_long p;
  226. if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
  227. if (type == IS_LONG) {
  228. smart_str_append_long(buf, p);
  229. return SUCCESS;
  230. } else if (type == IS_DOUBLE && php_json_is_valid_double(d)) {
  231. php_json_encode_double(buf, d, options);
  232. return SUCCESS;
  233. }
  234. }
  235. }
  236. pos = 0;
  237. checkpoint = buf->s ? ZSTR_LEN(buf->s) : 0;
  238. /* pre-allocate for string length plus 2 quotes */
  239. smart_str_alloc(buf, len+2, 0);
  240. smart_str_appendc(buf, '"');
  241. do {
  242. us = (unsigned char)s[pos];
  243. if (UNEXPECTED(us >= 0x80)) {
  244. if (pos) {
  245. smart_str_appendl(buf, s, pos);
  246. s += pos;
  247. pos = 0;
  248. }
  249. us = php_next_utf8_char((unsigned char *)s, len, &pos, &status);
  250. len -= pos;
  251. /* check whether UTF8 character is correct */
  252. if (UNEXPECTED(status != SUCCESS)) {
  253. s += pos;
  254. pos = 0;
  255. if (options & PHP_JSON_INVALID_UTF8_IGNORE) {
  256. /* ignore invalid UTF8 character */
  257. continue;
  258. } else if (options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) {
  259. /* Use Unicode character 'REPLACEMENT CHARACTER' (U+FFFD) */
  260. if (options & PHP_JSON_UNESCAPED_UNICODE) {
  261. smart_str_appendl(buf, "\xef\xbf\xbd", 3);
  262. } else {
  263. smart_str_appendl(buf, "\\ufffd", 6);
  264. }
  265. continue;
  266. } else {
  267. ZSTR_LEN(buf->s) = checkpoint;
  268. encoder->error_code = PHP_JSON_ERROR_UTF8;
  269. if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
  270. smart_str_appendl(buf, "null", 4);
  271. }
  272. return FAILURE;
  273. }
  274. /* Escape U+2028/U+2029 line terminators, UNLESS both
  275. JSON_UNESCAPED_UNICODE and
  276. JSON_UNESCAPED_LINE_TERMINATORS were provided */
  277. } else if ((options & PHP_JSON_UNESCAPED_UNICODE)
  278. && ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS)
  279. || us < 0x2028 || us > 0x2029)) {
  280. smart_str_appendl(buf, s, pos);
  281. s += pos;
  282. pos = 0;
  283. continue;
  284. }
  285. /* From http://en.wikipedia.org/wiki/UTF16 */
  286. if (us >= 0x10000) {
  287. unsigned int next_us;
  288. us -= 0x10000;
  289. next_us = (unsigned short)((us & 0x3ff) | 0xdc00);
  290. us = (unsigned short)((us >> 10) | 0xd800);
  291. dst = smart_str_extend(buf, 6);
  292. dst[0] = '\\';
  293. dst[1] = 'u';
  294. dst[2] = digits[(us >> 12) & 0xf];
  295. dst[3] = digits[(us >> 8) & 0xf];
  296. dst[4] = digits[(us >> 4) & 0xf];
  297. dst[5] = digits[us & 0xf];
  298. us = next_us;
  299. }
  300. dst = smart_str_extend(buf, 6);
  301. dst[0] = '\\';
  302. dst[1] = 'u';
  303. dst[2] = digits[(us >> 12) & 0xf];
  304. dst[3] = digits[(us >> 8) & 0xf];
  305. dst[4] = digits[(us >> 4) & 0xf];
  306. dst[5] = digits[us & 0xf];
  307. s += pos;
  308. pos = 0;
  309. } else {
  310. static const uint32_t charmap[4] = {
  311. 0xffffffff, 0x500080c4, 0x10000000, 0x00000000};
  312. len--;
  313. if (EXPECTED(!ZEND_BIT_TEST(charmap, us))) {
  314. pos++;
  315. } else {
  316. if (pos) {
  317. smart_str_appendl(buf, s, pos);
  318. s += pos;
  319. pos = 0;
  320. }
  321. s++;
  322. switch (us) {
  323. case '"':
  324. if (options & PHP_JSON_HEX_QUOT) {
  325. smart_str_appendl(buf, "\\u0022", 6);
  326. } else {
  327. smart_str_appendl(buf, "\\\"", 2);
  328. }
  329. break;
  330. case '\\':
  331. smart_str_appendl(buf, "\\\\", 2);
  332. break;
  333. case '/':
  334. if (options & PHP_JSON_UNESCAPED_SLASHES) {
  335. smart_str_appendc(buf, '/');
  336. } else {
  337. smart_str_appendl(buf, "\\/", 2);
  338. }
  339. break;
  340. case '\b':
  341. smart_str_appendl(buf, "\\b", 2);
  342. break;
  343. case '\f':
  344. smart_str_appendl(buf, "\\f", 2);
  345. break;
  346. case '\n':
  347. smart_str_appendl(buf, "\\n", 2);
  348. break;
  349. case '\r':
  350. smart_str_appendl(buf, "\\r", 2);
  351. break;
  352. case '\t':
  353. smart_str_appendl(buf, "\\t", 2);
  354. break;
  355. case '<':
  356. if (options & PHP_JSON_HEX_TAG) {
  357. smart_str_appendl(buf, "\\u003C", 6);
  358. } else {
  359. smart_str_appendc(buf, '<');
  360. }
  361. break;
  362. case '>':
  363. if (options & PHP_JSON_HEX_TAG) {
  364. smart_str_appendl(buf, "\\u003E", 6);
  365. } else {
  366. smart_str_appendc(buf, '>');
  367. }
  368. break;
  369. case '&':
  370. if (options & PHP_JSON_HEX_AMP) {
  371. smart_str_appendl(buf, "\\u0026", 6);
  372. } else {
  373. smart_str_appendc(buf, '&');
  374. }
  375. break;
  376. case '\'':
  377. if (options & PHP_JSON_HEX_APOS) {
  378. smart_str_appendl(buf, "\\u0027", 6);
  379. } else {
  380. smart_str_appendc(buf, '\'');
  381. }
  382. break;
  383. default:
  384. ZEND_ASSERT(us < ' ');
  385. dst = smart_str_extend(buf, 6);
  386. dst[0] = '\\';
  387. dst[1] = 'u';
  388. dst[2] = '0';
  389. dst[3] = '0';
  390. dst[4] = digits[(us >> 4) & 0xf];
  391. dst[5] = digits[us & 0xf];
  392. break;
  393. }
  394. }
  395. }
  396. } while (len);
  397. if (EXPECTED(pos)) {
  398. smart_str_appendl(buf, s, pos);
  399. }
  400. smart_str_appendc(buf, '"');
  401. return SUCCESS;
  402. }
  403. /* }}} */
  404. static int php_json_encode_serializable_object(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
  405. {
  406. zend_class_entry *ce = Z_OBJCE_P(val);
  407. HashTable* myht = Z_OBJPROP_P(val);
  408. zval retval, fname;
  409. int return_code;
  410. if (myht && GC_IS_RECURSIVE(myht)) {
  411. encoder->error_code = PHP_JSON_ERROR_RECURSION;
  412. if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
  413. smart_str_appendl(buf, "null", 4);
  414. }
  415. return FAILURE;
  416. }
  417. PHP_JSON_HASH_PROTECT_RECURSION(myht);
  418. ZVAL_STRING(&fname, "jsonSerialize");
  419. if (FAILURE == call_user_function(EG(function_table), val, &fname, &retval, 0, NULL) || Z_TYPE(retval) == IS_UNDEF) {
  420. if (!EG(exception)) {
  421. zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name));
  422. }
  423. zval_ptr_dtor(&fname);
  424. if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
  425. smart_str_appendl(buf, "null", 4);
  426. }
  427. PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
  428. return FAILURE;
  429. }
  430. if (EG(exception)) {
  431. /* Error already raised */
  432. zval_ptr_dtor(&retval);
  433. zval_ptr_dtor(&fname);
  434. if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
  435. smart_str_appendl(buf, "null", 4);
  436. }
  437. PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
  438. return FAILURE;
  439. }
  440. if ((Z_TYPE(retval) == IS_OBJECT) &&
  441. (Z_OBJ(retval) == Z_OBJ_P(val))) {
  442. /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
  443. PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
  444. return_code = php_json_encode_array(buf, &retval, options, encoder);
  445. } else {
  446. /* All other types, encode as normal */
  447. return_code = php_json_encode_zval(buf, &retval, options, encoder);
  448. PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
  449. }
  450. zval_ptr_dtor(&retval);
  451. zval_ptr_dtor(&fname);
  452. return return_code;
  453. }
  454. /* }}} */
  455. int php_json_encode_zval(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
  456. {
  457. again:
  458. switch (Z_TYPE_P(val))
  459. {
  460. case IS_NULL:
  461. smart_str_appendl(buf, "null", 4);
  462. break;
  463. case IS_TRUE:
  464. smart_str_appendl(buf, "true", 4);
  465. break;
  466. case IS_FALSE:
  467. smart_str_appendl(buf, "false", 5);
  468. break;
  469. case IS_LONG:
  470. smart_str_append_long(buf, Z_LVAL_P(val));
  471. break;
  472. case IS_DOUBLE:
  473. if (php_json_is_valid_double(Z_DVAL_P(val))) {
  474. php_json_encode_double(buf, Z_DVAL_P(val), options);
  475. } else {
  476. encoder->error_code = PHP_JSON_ERROR_INF_OR_NAN;
  477. smart_str_appendc(buf, '0');
  478. }
  479. break;
  480. case IS_STRING:
  481. return php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options, encoder);
  482. case IS_OBJECT:
  483. if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
  484. return php_json_encode_serializable_object(buf, val, options, encoder);
  485. }
  486. /* fallthrough -- Non-serializable object */
  487. case IS_ARRAY: {
  488. /* Avoid modifications (and potential freeing) of the array through a reference when a
  489. * jsonSerialize() method is invoked. */
  490. zval zv;
  491. int res;
  492. ZVAL_COPY(&zv, val);
  493. res = php_json_encode_array(buf, &zv, options, encoder);
  494. zval_ptr_dtor_nogc(&zv);
  495. return res;
  496. }
  497. case IS_REFERENCE:
  498. val = Z_REFVAL_P(val);
  499. goto again;
  500. default:
  501. encoder->error_code = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
  502. if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
  503. smart_str_appendl(buf, "null", 4);
  504. }
  505. return FAILURE;
  506. }
  507. return SUCCESS;
  508. }
  509. /* }}} */
  510. /*
  511. * Local variables:
  512. * tab-width: 4
  513. * c-basic-offset: 4
  514. * End:
  515. * vim600: noet sw=4 ts=4 fdm=marker
  516. * vim<600: noet sw=4 ts=4
  517. */