程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> PHP的json_encode分析

PHP的json_encode分析

編輯:關於PHP編程

 json的優點就不說了, 有個習慣,我在輸出json的時候,喜歡用 sprintf 拼成json格式,   前兩天被朋友說不標准,必須要用json_encode生成的才是標准的json格式,我當然很郁悶啦,   用了這麼多年了,剛知道 這樣做不標准,既然說我不標准,那上面才是標准的json格式?       {a : 'abc'}  {'a' : 'abc'}  {a : "abc"}  {"a" : "abc"}  那都知道,只有第四種才是標准的json格式。   我這麼做       $ret_json='{"%s":"%s"}';  echo json_encode($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 <= 0) {//如果len小於0 說明出錯。如果用json_encode處理GBK的編碼,就會在這裡掛掉。                  if (utf16) {                          efree(utf16);                  }                  if (len < 0) {                          JSON_G(error_code) = PHP_JSON_ERROR_UTF8;                          if (!PG(display_errors)) {                                  php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");                          }                          smart_str_appendl(buf, "null", 4);                  } else {                          smart_str_appendl(buf, "\"\"", 2);                  }                  return;          }             smart_str_appendc(buf, '"'); //輸入 \"     //下面這一段代碼就是將一些特殊字符轉義如 雙引號,反斜線等等          while (pos < len)          {                  us = utf16[pos++];                     switch (us)                  {                          case '"':                                  if (options & PHP_JSON_HEX_QUOT) {                                          smart_str_appendl(buf, "\\u0022", 6);                                  } else {                                          smart_str_appendl(buf, "\\\"", 2);                                  }                                  break;                             case '\\':                                  smart_str_appendl(buf, "\\\\", 2);                                  break;  case '/':                                  smart_str_appendl(buf, "\\/", 2);                                  break;                             case '\b':                                  smart_str_appendl(buf, "\\b", 2);                                  break;                             case '\f':                                  smart_str_appendl(buf, "\\f", 2);                                  break;                             case '\n':                                  smart_str_appendl(buf, "\\n", 2);                                  break;                             case '\r':                                  smart_str_appendl(buf, "\\r", 2);                                  break;                             case '\t':                                  smart_str_appendl(buf, "\\t", 2);                                  break;                             case '<':                                  if (options & PHP_JSON_HEX_TAG) {                                          smart_str_appendl(buf, "\\u003C", 6);                                  } else {                                          smart_str_appendc(buf, '<');                                  }                                  break;                             case '>':                                  if (options & PHP_JSON_HEX_TAG) {                                          smart_str_appendl(buf, "\\u003E", 6);                                  } else {                                          smart_str_appendc(buf, '>');  }                                  break;                             case '&':                                  if (options & PHP_JSON_HEX_AMP) {                                          smart_str_appendl(buf, "\\u0026", 6);                                  } else {                                          smart_str_appendc(buf, '&');                                  }                                  break;                             case '\'':                                  if (options & PHP_JSON_HEX_APOS) {                                          smart_str_appendl(buf, "\\u0027", 6);                                  } else {                                          smart_str_appendc(buf, '\'');                                  }                                  break;                             default: //一直到這裡,沒有特殊字符就會把值append到buf中                                  if (us >= ' ' && (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) - 1)]);                                          us >>= 4;                                          smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);                                          us >>= 4;                                          smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);                                          us >>= 4;                                          smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);                                  }                                  break;                  }          }          smart_str_appendc(buf, '"'); //結束 雙引號。          efree(utf16);  }  再來看看數組和對象,也很簡單,       static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */  {          int i, r;          HashTable *myht;             if (Z_TYPE_PP(val) == IS_ARRAY) {                  myht = HASH_OF(*val);                  r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val TSRMLS_CC);          } else {                  myht = Z_OBJPROP_PP(val);                  r = PHP_JSON_OUTPUT_OBJECT;          }                if (myht && myht->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的方法其實是一樣的,都是拼接字符串,

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved