
json的优点就不说了,
有个习惯,我在输出json的时候,喜欢用 sprintf 拼成json格式,
前两天被朋友说不标准,必须要用json_encode生成的才是标准的json格式,我当然很郁闷啦,
用了这么多年了,刚知道 这样做不标准,既然说我不标准,那上面才是标准的json格式?
1.{a : 'abc'}
2.{'a' : 'abc'}
3.{a : "abc"}
4.{"a" : "abc"}
那都知道,只有第四种才是标准的json格式。
我这么做
1.$ret_json='{"%s":"%s"}';
2.echo json_encode($ret_json,"a
必然也符合标准。
既然如此,那我就要刨根问底,json_encode生成的json格式究竟有什么不同?
上代码
1.static PHP_FUNCTION(json_encode)
2.{
3. zval *parameter;
4. smart_str buf = {0};
5. long options = 0;
6.
7. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", ¶meter, &options) == FAILURE) {
8. return;
9. }
10.
11. JSON_G(error_code) = PHP_JSON_ERROR_NONE;
12.
13. php_json_encode(&buf, parameter, options TSRMLS_CC);
14.
15. ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
16.
17. smart_str_free(&buf);
18.}
JSON_G(error_code) = PHP_JSON_ERROR_NONE;
是定义的json错误,该错误可以通过json_last_error函数获取,你用过吗?反正我没用过。
php_json_encode是主要的操作
1.PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
2.{
3. switch (Z_TYPE_P(val))
4. {
5. case IS_NULL:
6. smart_str_appendl(buf, "null", 4); //输出NULL
7. break;
8.
9. case IS_BOOL:
10. if (Z_BVAL_P(val)) {
11. smart_str_appendl(buf, "true", 4);//输出true
12. } else {
13. smart_str_appendl(buf, "false", 5);//输出false
14. }
15. break;
16.
17. case IS_LONG:
18. smart_str_append_long(buf, Z_LVAL_P(val));//输出长整形的值
19. break;
20.
21. case IS_DOUBLE:
22. {
23. char *d = NULL;
24. int len;
25. double dbl = Z_DVAL_P(val);
26.
27. if (!zend_isinf(dbl) && !zend_isnan(dbl)) {//非无穷尽
28. len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
29. smart_str_appendl(buf, d, len);
30. efree(d);
31. } else {
32. php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", dbl);
33. smart_str_appendc(buf, '0');
34. }
35. }
36. break;
37.
38. case IS_STRING://字符串
39. json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC);
40. break;
41.
42. case IS_ARRAY://数组和对象
43. case IS_OBJECT:
44. json_encode_array(buf, &val, options TSRMLS_CC);
45. break;
46.
47. default:
48. php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, encoded as null");
49. smart_str_appendl(buf, "null", 4);
50. break;
51. }
52.
53. return;
54.}
很明显,根据不同的类型,会有相应的case。
最复杂的是 字符串 、数组 、对象这三种类型,数组和对象是同一种操作。
先看看字符串吧,很长,注释直接写在代码里。
1.//options应该是5.3版本之后才支持的,由以下常量组成的二进制掩码: JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT, JSON_UNESCAPED_UNICODE.虽然我没用过。。
2.static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
3.{
4. int pos = 0;
5. unsigned short us;
6. unsigned short *utf16;
7.
8. if (len == 0) {//如果长度为0,则直接返回 双引号 ""
9. smart_str_appendl(buf, "\\"\\"", 2);
10. return;
11. }
12.
13. if (options & PHP_JSON_NUMERIC_CHECK) {//检测是否为0-9的数字,如果是数字,那么就会直接把数据作为long或double类型返回。
14. double d;
15. int type;
16. long p;
17.
18. if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
19. if (type == IS_LONG) {
20. smart_str_append_long(buf, p);
21. } else if (type == IS_DOUBLE) {
22. if (!zend_isinf(d) && !zend_isnan(d)) {
23. char *tmp;
24. int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d);
25. smart_str_appendl(buf, tmp, l);
26. efree(tmp);
27. } else {
28. php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", d);
29. smart_str_appendc(buf, '0');
30. }
31. }
32. return;
33. }
34.
35. }
36.
37. utf16 = (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
38. len = utf8_to_utf16(utf16, s, len); //这里会对你输入的值一次处理转成对应的Dec码,比如1是49,a是97这样的,保存到utf16中。
39. if (len <= 0) {//如果len小于0 说明出错。如果用json_encode处理GBK的编码,就会在这里挂掉。
40. if (utf16) {
41. efree(utf16);
42. }
43. if (len < 0) {
44. JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
45. if (!PG(display_errors)) {
46. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
47. }
48. smart_str_appendl(buf, "null", 4);
49. } else {
50. smart_str_appendl(buf, "\\"\\"", 2);
51. }
52. return;
53. }
54.
55. smart_str_appendc(buf, '"'); //输入 \\"
56.
57.//下面这一段代码就是将一些特殊字符转义如 双引号,反斜线等等
58. while (pos < len)
59. {
60. us = utf16[pos++];
61.
62. switch (us)
63. {
. case '"':
65. if (options & PHP_JSON_HEX_QUOT) {
66. smart_str_appendl(buf, "\\\"", 6);
67. } else {
68. smart_str_appendl(buf, "\\\\\\"", 2);
69. }
70. break;
71.
72. case '\\\\':
73. smart_str_appendl(buf, "\\\\\\\\", 2);
74. break;
75.case '/':
76. smart_str_appendl(buf, "\\\/", 2);
77. break;
78.
79. case '\\b':
80. smart_str_appendl(buf, "\\\\b", 2);
81. break;
82.
83. case '\\f':
84. smart_str_appendl(buf, "\\\\f", 2);
85. break;
86.
87. case '\\n':
88. smart_str_appendl(buf, "\\\\n", 2);
. break;
90.
91. case '\\r':
92. smart_str_appendl(buf, "\\\\r", 2);
93. break;
94.
95. case '\':
96. smart_str_appendl(buf, "\\\", 2);
97. break;
98.
99. case '<':
100. if (options & PHP_JSON_HEX_TAG) {
101. smart_str_appendl(buf, "\\\<", 6);
102. } else {
103. smart_str_appendc(buf, '<');
104. }
105. break;
106.
107. case '>':
108. if (options & PHP_JSON_HEX_TAG) {
109. smart_str_appendl(buf, "\\\>", 6);
110. } else {
111. smart_str_appendc(buf, '>');
112.}
113. break;
114.
115. case '&':
116. if (options & PHP_JSON_HEX_AMP) {
117. smart_str_appendl(buf, "\\\&", 6);
118. } else {
119. smart_str_appendc(buf, '&');
120. }
121. break;
122.
123. case '\\'':
124. if (options & PHP_JSON_HEX_APOS) {
125. smart_str_appendl(buf, "\\\'", 6);
126. } else {
127. smart_str_appendc(buf, '\\'');
128. }
129. break;
130.
131. default: //一直到这里,没有特殊字符就会把值append到buf中
132. if (us >= ' ' && (us & 127) == us) {
133. smart_str_appendc(buf, (unsigned char) us);
134. } else {
135. smart_str_appendl(buf, "\\\);
136. us = REVERSE16(us);
137.
138. smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
139. us >>= 4;
140. smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
141. us >>= 4;
142. smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
143. us >>= 4;
144. smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
145. }
146. break;
147. }
148. }
149. smart_str_appendc(buf, '"'); //结束 双引号。
150. efree(utf16);
151.}
再来看看数组和对象,也很简单,
1.static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */
2.{
3. int i, r;
4. HashTable *myht;
5.
6. if (Z_TYPE_PP(val) == IS_ARRAY) {
7. myht = HASH_OF(*val);
8. r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val TSRMLS_CC);
9. } else {
10. myht = Z_OBJPROP_PP(val);
11. r = PHP_JSON_OUTPUT_OBJECT;
12. }
13.
14. if (myht && myht->nApplyCount > 1) {
15. php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
16. smart_str_appendl(buf, "null", 4);
17. return;
18. }
19.//开始标签
20. if (r == PHP_JSON_OUTPUT_ARRAY) {
21. smart_str_appendc(buf, '[');
22. } else {
23. smart_str_appendc(buf, '{');
24. }
25.
26. i = myht ? zend_hash_num_elements(myht) : 0;
27.
28. if (i > 0)
29. {
30. char *key;
31. zval **data;
32. ulong index;
33. uint key_len;
34. HashPosition pos;
35. HashTable *tmp_ht;
36. int need_comma = 0;
37.
38. zend_hash_internal_pointer_reset_ex(myht, &pos);
39.//便利哈希表
40. for (;; zend_hash_move_forward_ex(myht, &pos)) {
41. i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
42. if (i == HASH_KEY_NON_EXISTANT)
43. break;
44.
45. if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
46. tmp_ht = HASH_OF(*data);
47. if (tmp_ht) {
48. tmp_ht->nApplyCount++;
49. }
50.
51. if (r == PHP_JSON_OUTPUT_ARRAY) {
52. if (need_comma) {
53. smart_str_appendc(buf, ',');
54. } else {
55. need_comma = 1;
56. }
57.//将值append到 buf中
58. php_json_encode(buf, *data, options TSRMLS_CC);
59. } else if (r == PHP_JSON_OUTPUT_OBJECT) {
60. if (i == HASH_KEY_IS_STRING) {
61. if (key[0] == '\\0' && Z_TYPE_PP(val) == IS_OBJECT) {
62. /* Skip protected and private members. */
63. if (tmp_ht) {
. tmp_ht->nApplyCount--;
65. }
66. continue;
67. }
68.
69. if (need_comma) {
70. smart_str_appendc(buf, ',');
71. } else {
72. need_comma = 1;
73. }
74.
75. json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
76. smart_str_appendc(buf, ':');
77.
78. php_json_encode(buf, *data, options TSRMLS_CC);
79. } else {
80. if (need_comma) {
81. smart_str_appendc(buf, ',');
82. } else {
83. need_comma = 1;
84. }
85.
86. smart_str_appendc(buf, '"');
87. smart_str_append_long(buf, (long) index);
88. smart_str_appendc(buf, '"');
. smart_str_appendc(buf, ':');
90.
91. php_json_encode(buf, *data, options TSRMLS_CC);
92. }
93. }
94.
95. if (tmp_ht) {
96. tmp_ht->nApplyCount--;
97. }
98. }
99. }
100. }
101.//结束标签
102. if (r == PHP_JSON_OUTPUT_ARRAY) {
103. smart_str_appendc(buf, ']');
104. } else {
105. smart_str_appendc(buf, '}');
106. }
107.}
通过简单分析,证明了一个问题,跟我上面用sprintf的方法其实是一样的,都是拼接字符串,
而且 为了性能,更应该鼓励用sprintf来拼接json格式,
因为 json_encode会进行很多 循环操作,而且所消耗的性能是线性的 O(n)。
本文由http://www.focustar.net 整理分享。
