设为首页 收藏本站
查看: 897|回复: 0

[经验分享] PHP的json_encode分析

[复制链接]

尚未签到

发表于 2018-12-20 09:26:28 | 显示全部楼层 |阅读模式
  json的优点就不说了,
有个习惯,我在输出json的时候,喜欢用 sprintf 拼成json格式,
前两天被朋友说不标准,必须要用json_encode生成的才是标准的json格式,我当然很郁闷啦,
用了这么多年了,刚知道 这样做不标准,既然说我不标准,那上面才是标准的json格式?


  • {a : 'abc'}
  • {'a' : 'abc'}
  • {a : "abc"}
  • {"a" : "abc"}


那都知道,只有第四种才是标准的json格式。
我这么做


  • $ret_json='{"%s":"%s"}';
  • echo sprintf($ret_json,"a","abc");


必然也符合标准。
既然如此,那我就要刨根问底,json_encode生成的json格式究竟有什么不同?

上代码



  • static PHP_FUNCTION(json_encode)
  • {
  •         zval *parameter;
  •         smart_str buf = {0};
  •         long options = 0;
  •   
  •         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &parameter, &options) == FAILURE) {
  •                 return;
  •         }   
  •   
  •         JSON_G(error_code) = PHP_JSON_ERROR_NONE;
  •   
  •         php_json_encode(&buf, parameter, options TSRMLS_CC);
  •   
  •         ZVAL_STRINGL(return_value, buf.c, buf.len, 1);  
  •   
  •         smart_str_free(&buf);
  • }



JSON_G(error_code) = PHP_JSON_ERROR_NONE;

是定义的json错误,该错误可以通过json_last_error函数获取,你用过吗?反正我没用过。

php_json_encode是主要的操作



  • PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
  • {
  •         switch (Z_TYPE_P(val))
  •         {
  •                 case IS_NULL:
  •                         smart_str_appendl(buf, "null", 4); //输出NULL
  •                         break;
  •   
  •                 case IS_BOOL:
  •                         if (Z_BVAL_P(val)) {
  •                                 smart_str_appendl(buf, "true", 4);//输出true
  •                         } else {
  •                                 smart_str_appendl(buf, "false", 5);//输出false
  •                         }
  •                         break;
  •   
  •                 case IS_LONG:
  •                         smart_str_append_long(buf, Z_LVAL_P(val));//输出长整形的值
  •                         break;
  •   
  •                 case IS_DOUBLE:
  •                         {
  •                                 char *d = NULL;
  •                                 int len;
  •                                 double dbl = Z_DVAL_P(val);
  •   
  •                                 if (!zend_isinf(dbl) && !zend_isnan(dbl)) {//非无穷尽
  •                                         len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
  •                                         smart_str_appendl(buf, d, len);
  •                                         efree(d);
  •                                 } else {
  •                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", dbl);
  •                                         smart_str_appendc(buf, '0');
  •                                 }
  •                        }
  •                         break;
  •   
  •                 case IS_STRING://字符串
  •                         json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC);
  •                         break;
  •   
  •                 case IS_ARRAY://数组和对象
  •                 case IS_OBJECT:
  •                         json_encode_array(buf, &val, options TSRMLS_CC);
  •                         break;
  •   
  •                 default:
  •                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, encoded as null");
  •                         smart_str_appendl(buf, "null", 4);
  •                         break;
  •         }
  •   
  •         return;
  • }

很明显,根据不同的类型,会有相应的case。

最复杂的是 字符串 、数组 、对象这三种类型,数组和对象是同一种操作。

先看看字符串吧,很长,注释直接写在代码里。



  • //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.虽然我没用过。。。
  • static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
  • {
  •         int pos = 0;
  •         unsigned short us;
  •         unsigned short *utf16;
  •   
  •         if (len == 0) {//如果长度为0,则直接返回 双引号 ""
  •                 smart_str_appendl(buf, "\"\"", 2);
  •                 return;
  •         }
  •   
  •         if (options & PHP_JSON_NUMERIC_CHECK) {//检测是否为0-9的数字,如果是数字,那么就会直接把数据作为long或double类型返回。
  •                 double d;
  •                 int type;
  •                 long p;
  •   
  •                 if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
  •                         if (type == IS_LONG) {
  •                                 smart_str_append_long(buf, p);
  •                         } else if (type == IS_DOUBLE) {
  •                                 if (!zend_isinf(d) && !zend_isnan(d)) {
  •                                         char *tmp;
  •                                         int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d);
  •                                         smart_str_appendl(buf, tmp, l);
  •                                         efree(tmp);
  •                                 } else {
  •                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", d);
  •                                         smart_str_appendc(buf, '0');
  •                                 }
  •                         }
  •                         return;
  •                 }
  •   
  •         }
  •   
  •         utf16 = (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
  •         len = utf8_to_utf16(utf16, s, len); //这里会对你输入的值一次处理转成对应的Dec码,比如1是49,a是97这样的,保存到utf16中。
  •         if (len = ' ' && (us & 127) == us) {
  •                                         smart_str_appendc(buf, (unsigned char) us);
  •                                 } else {
  •                                         smart_str_appendl(buf, "\\u", 2);
  •                                         us = REVERSE16(us);
  •   
  •                                         smart_str_appendc(buf, digits[us & ((1 >= 4;
  •                                         smart_str_appendc(buf, digits[us & ((1 >= 4;
  •                                         smart_str_appendc(buf, digits[us & ((1 >= 4;
  •                                         smart_str_appendc(buf, digits[us & ((1 nApplyCount > 1) {
  •                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
  •                 smart_str_appendl(buf, "null", 4);
  •                 return;
  •         }
  • //开始标签
  •         if (r == PHP_JSON_OUTPUT_ARRAY) {
  •                 smart_str_appendc(buf, '[');
  •         } else {
  •                 smart_str_appendc(buf, '{');
  •         }   
  •   
  •         i = myht ? zend_hash_num_elements(myht) : 0;
  •   
  •         if (i > 0)
  •         {
  •                 char *key;
  •                 zval **data;
  •                 ulong index;
  •                 uint key_len;
  •                 HashPosition pos;
  •                 HashTable *tmp_ht;
  •                 int need_comma = 0;
  •   
  •                 zend_hash_internal_pointer_reset_ex(myht, &pos);
  • //便利哈希表
  •                 for (;; zend_hash_move_forward_ex(myht, &pos)) {
  •                         i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
  •                         if (i == HASH_KEY_NON_EXISTANT)
  •                                 break;
  •   
  •                         if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
  •                                 tmp_ht = HASH_OF(*data);
  •                                 if (tmp_ht) {
  •                                         tmp_ht->nApplyCount++;
  •                                 }
  •   
  •                                 if (r == PHP_JSON_OUTPUT_ARRAY) {
  •                                         if (need_comma) {
  •                                                 smart_str_appendc(buf, ',');
  •                                         } else {
  •                                                 need_comma = 1;
  •                                         }
  • //将值append到 buf中
  •                                         php_json_encode(buf, *data, options TSRMLS_CC);
  •                                 } else if (r == PHP_JSON_OUTPUT_OBJECT) {
  •                                         if (i == HASH_KEY_IS_STRING) {
  •                                                 if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) {
  •                                                         /* Skip protected and private members. */
  •                                                         if (tmp_ht) {
  •                                                                 tmp_ht->nApplyCount--;
  •                                                         }
  •                                                         continue;
  •                                                 }
  •   
  •                                                 if (need_comma) {
  •                                                         smart_str_appendc(buf, ',');
  •                                                 } else {
  •                                                         need_comma = 1;
  •                                                 }
  •   
  •                                                 json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
  •                                                 smart_str_appendc(buf, ':');
  •   
  •                                                 php_json_encode(buf, *data, options TSRMLS_CC);
  •                                         } else {
  •                                                 if (need_comma) {
  •                                                         smart_str_appendc(buf, ',');
  •                                                 } else {
  •                                                         need_comma = 1;
  •                                                 }
  •   
  •                                                 smart_str_appendc(buf, '"');
  •                                                 smart_str_append_long(buf, (long) index);
  •                                                 smart_str_appendc(buf, '"');
  •                                                 smart_str_appendc(buf, ':');
  •   
  •                                                 php_json_encode(buf, *data, options TSRMLS_CC);
  •                                         }
  •                                 }
  •   
  •                                 if (tmp_ht) {
  •                                         tmp_ht->nApplyCount--;
  •                                 }
  •                         }
  •                 }
  •         }
  • //结束标签
  •         if (r == PHP_JSON_OUTPUT_ARRAY) {
  •                 smart_str_appendc(buf, ']');
  •         } else {
  •                 smart_str_appendc(buf, '}');
  •         }
  • }


通过简单分析,证明了一个问题,跟我上面用sprintf的方法其实是一样的,都是拼接字符串,
而且 为了性能,更应该鼓励用sprintf来拼接json格式,
因为 json_encode会进行很多 循环操作,而且所消耗的性能是线性的 O(n)。 ^.^

原文出处:http://www.imsiren.com/archives/717




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-653438-1-1.html 上篇帖子: /usr/local/php 下篇帖子: GDB分析PHP连接Memcached 导致coredump问题
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表