PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/json - json.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 339 358 94.7 %
Date: 2014-04-10 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   +----------------------------------------------------------------------+
       3             :   | PHP Version 5                                                        |
       4             :   +----------------------------------------------------------------------+
       5             :   | Copyright (c) 1997-2014 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             :   +----------------------------------------------------------------------+
      17             : */
      18             : 
      19             : /* $Id$ */
      20             : 
      21             : #ifdef HAVE_CONFIG_H
      22             : #include "config.h"
      23             : #endif
      24             : 
      25             : #include "php.h"
      26             : #include "php_ini.h"
      27             : #include "ext/standard/info.h"
      28             : #include "ext/standard/html.h"
      29             : #include "ext/standard/php_smart_str.h"
      30             : #include "JSON_parser.h"
      31             : #include "php_json.h"
      32             : #include <zend_exceptions.h>
      33             : 
      34             : static PHP_MINFO_FUNCTION(json);
      35             : static PHP_FUNCTION(json_encode);
      36             : static PHP_FUNCTION(json_decode);
      37             : static PHP_FUNCTION(json_last_error);
      38             : 
      39             : static const char digits[] = "0123456789abcdef";
      40             : 
      41             : PHP_JSON_API zend_class_entry *php_json_serializable_ce;
      42             : 
      43             : ZEND_DECLARE_MODULE_GLOBALS(json)
      44             : 
      45             : /* {{{ arginfo */
      46             : ZEND_BEGIN_ARG_INFO_EX(arginfo_json_encode, 0, 0, 1)
      47             :         ZEND_ARG_INFO(0, value)
      48             :         ZEND_ARG_INFO(0, options)
      49             : ZEND_END_ARG_INFO()
      50             : 
      51             : ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1)
      52             :         ZEND_ARG_INFO(0, json)
      53             :         ZEND_ARG_INFO(0, assoc)
      54             :         ZEND_ARG_INFO(0, depth)
      55             :         ZEND_ARG_INFO(0, options)
      56             : ZEND_END_ARG_INFO()
      57             : 
      58             : ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0)
      59             : ZEND_END_ARG_INFO()
      60             : /* }}} */
      61             : 
      62             : /* {{{ json_functions[] */
      63             : static const zend_function_entry json_functions[] = {
      64             :         PHP_FE(json_encode, arginfo_json_encode)
      65             :         PHP_FE(json_decode, arginfo_json_decode)
      66             :         PHP_FE(json_last_error, arginfo_json_last_error)
      67             :         PHP_FE_END
      68             : };
      69             : /* }}} */
      70             : 
      71             : /* {{{ JsonSerializable methods */
      72             : ZEND_BEGIN_ARG_INFO(json_serialize_arginfo, 0)
      73             :         /* No arguments */
      74             : ZEND_END_ARG_INFO();
      75             : 
      76             : static const zend_function_entry json_serializable_interface[] = {
      77             :         PHP_ABSTRACT_ME(JsonSerializable, jsonSerialize, json_serialize_arginfo)
      78             :         PHP_FE_END
      79             : };
      80             : /* }}} */
      81             : 
      82             : /* {{{ MINIT */
      83       20105 : static PHP_MINIT_FUNCTION(json)
      84             : {
      85             :         zend_class_entry ce;
      86             : 
      87       20105 :         INIT_CLASS_ENTRY(ce, "JsonSerializable", json_serializable_interface);
      88       20105 :         php_json_serializable_ce = zend_register_internal_interface(&ce TSRMLS_CC);
      89             : 
      90       20105 :         REGISTER_LONG_CONSTANT("JSON_HEX_TAG",  PHP_JSON_HEX_TAG,  CONST_CS | CONST_PERSISTENT);
      91       20105 :         REGISTER_LONG_CONSTANT("JSON_HEX_AMP",  PHP_JSON_HEX_AMP,  CONST_CS | CONST_PERSISTENT);
      92       20105 :         REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | CONST_PERSISTENT);
      93       20105 :         REGISTER_LONG_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT, CONST_CS | CONST_PERSISTENT);
      94       20105 :         REGISTER_LONG_CONSTANT("JSON_FORCE_OBJECT", PHP_JSON_FORCE_OBJECT, CONST_CS | CONST_PERSISTENT);
      95       20105 :         REGISTER_LONG_CONSTANT("JSON_NUMERIC_CHECK", PHP_JSON_NUMERIC_CHECK, CONST_CS | CONST_PERSISTENT);
      96       20105 :         REGISTER_LONG_CONSTANT("JSON_UNESCAPED_SLASHES", PHP_JSON_UNESCAPED_SLASHES, CONST_CS | CONST_PERSISTENT);
      97       20105 :         REGISTER_LONG_CONSTANT("JSON_PRETTY_PRINT", PHP_JSON_PRETTY_PRINT, CONST_CS | CONST_PERSISTENT);
      98       20105 :         REGISTER_LONG_CONSTANT("JSON_UNESCAPED_UNICODE", PHP_JSON_UNESCAPED_UNICODE, CONST_CS | CONST_PERSISTENT);
      99             : 
     100       20105 :         REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT);
     101       20105 :         REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT);
     102       20105 :         REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT);
     103       20105 :         REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT);
     104       20105 :         REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT);
     105       20105 :         REGISTER_LONG_CONSTANT("JSON_ERROR_UTF8", PHP_JSON_ERROR_UTF8, CONST_CS | CONST_PERSISTENT);
     106             : 
     107       20105 :         REGISTER_LONG_CONSTANT("JSON_OBJECT_AS_ARRAY",                PHP_JSON_OBJECT_AS_ARRAY,               CONST_CS | CONST_PERSISTENT);
     108       20105 :         REGISTER_LONG_CONSTANT("JSON_BIGINT_AS_STRING",               PHP_JSON_BIGINT_AS_STRING,              CONST_CS | CONST_PERSISTENT);
     109             : 
     110       20105 :         return SUCCESS;
     111             : }
     112             : /* }}} */
     113             : 
     114             : /* {{{ PHP_GINIT_FUNCTION
     115             : */
     116       20105 : static PHP_GINIT_FUNCTION(json)
     117             : {
     118       20105 :         json_globals->encoder_depth = 0;
     119       20105 :         json_globals->error_code = 0;
     120       20105 : }
     121             : /* }}} */
     122             : 
     123             : 
     124             : /* {{{ json_module_entry
     125             :  */
     126             : zend_module_entry json_module_entry = {
     127             :         STANDARD_MODULE_HEADER,
     128             :         "json",
     129             :         json_functions,
     130             :         PHP_MINIT(json),
     131             :         NULL,
     132             :         NULL,
     133             :         NULL,
     134             :         PHP_MINFO(json),
     135             :         PHP_JSON_VERSION,
     136             :         PHP_MODULE_GLOBALS(json),
     137             :         PHP_GINIT(json),
     138             :         NULL,
     139             :         NULL,
     140             :         STANDARD_MODULE_PROPERTIES_EX
     141             : };
     142             : /* }}} */
     143             : 
     144             : #ifdef COMPILE_DL_JSON
     145             : ZEND_GET_MODULE(json)
     146             : #endif
     147             : 
     148             : /* {{{ PHP_MINFO_FUNCTION
     149             :  */
     150         148 : static PHP_MINFO_FUNCTION(json)
     151             : {
     152         148 :         php_info_print_table_start();
     153         148 :         php_info_print_table_row(2, "json support", "enabled");
     154         148 :         php_info_print_table_row(2, "json version", PHP_JSON_VERSION);
     155         148 :         php_info_print_table_end();
     156         148 : }
     157             : /* }}} */
     158             : 
     159             : static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC);
     160             : 
     161         131 : static int json_determine_array_type(zval **val TSRMLS_DC) /* {{{ */
     162             : {
     163             :         int i;
     164         131 :         HashTable *myht = HASH_OF(*val);
     165             : 
     166         131 :         i = myht ? zend_hash_num_elements(myht) : 0;
     167         131 :         if (i > 0) {
     168             :                 char *key;
     169             :                 ulong index, idx;
     170             :                 uint key_len;
     171             :                 HashPosition pos;
     172             : 
     173         116 :                 zend_hash_internal_pointer_reset_ex(myht, &pos);
     174         116 :                 idx = 0;
     175         305 :                 for (;; zend_hash_move_forward_ex(myht, &pos)) {
     176         421 :                         i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
     177         421 :                         if (i == HASH_KEY_NON_EXISTANT) {
     178          97 :                                 break;
     179             :                         }
     180             : 
     181         324 :                         if (i == HASH_KEY_IS_STRING) {
     182          16 :                                 return 1;
     183             :                         } else {
     184         308 :                                 if (index != idx) {
     185           3 :                                         return 1;
     186             :                                 }
     187             :                         }
     188         305 :                         idx++;
     189         305 :                 }
     190             :         }
     191             : 
     192         112 :         return PHP_JSON_OUTPUT_ARRAY;
     193             : }
     194             : /* }}} */
     195             : 
     196             : /* {{{ Pretty printing support functions */
     197             : 
     198        1073 : static inline void json_pretty_print_char(smart_str *buf, int options, char c TSRMLS_DC) /* {{{ */
     199             : {
     200        1073 :         if (options & PHP_JSON_PRETTY_PRINT) {
     201          22 :                 smart_str_appendc(buf, c);
     202             :         }
     203        1073 : }
     204             : /* }}} */
     205             : 
     206         766 : static inline void json_pretty_print_indent(smart_str *buf, int options TSRMLS_DC) /* {{{ */
     207             : {
     208             :         int i;
     209             : 
     210         766 :         if (options & PHP_JSON_PRETTY_PRINT) {
     211          40 :                 for (i = 0; i < JSON_G(encoder_depth); ++i) {
     212          22 :                         smart_str_appendl(buf, "    ", 4);
     213             :                 }
     214             :         }
     215         766 : }
     216             : /* }}} */
     217             : 
     218             : /* }}} */
     219             : 
     220         202 : static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */
     221             : {
     222             :         int i, r;
     223             :         HashTable *myht;
     224             : 
     225         202 :         if (Z_TYPE_PP(val) == IS_ARRAY) {
     226         135 :                 myht = HASH_OF(*val);
     227         135 :                 r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val TSRMLS_CC);
     228             :         } else {
     229          67 :                 myht = Z_OBJPROP_PP(val);
     230          67 :                 r = PHP_JSON_OUTPUT_OBJECT;
     231             :         }
     232             : 
     233         202 :         if (myht && myht->nApplyCount > 1) {
     234           3 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
     235           3 :                 smart_str_appendl(buf, "null", 4);
     236           3 :                 return;
     237             :         }
     238             : 
     239         199 :         if (r == PHP_JSON_OUTPUT_ARRAY) {
     240         111 :                 smart_str_appendc(buf, '[');
     241             :         } else {
     242          88 :                 smart_str_appendc(buf, '{');
     243             :         }
     244             : 
     245         199 :         json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
     246         199 :         ++JSON_G(encoder_depth);
     247             : 
     248         199 :         i = myht ? zend_hash_num_elements(myht) : 0;
     249             : 
     250         199 :         if (i > 0)
     251             :         {
     252             :                 char *key;
     253             :                 zval **data;
     254             :                 ulong index;
     255             :                 uint key_len;
     256             :                 HashPosition pos;
     257             :                 HashTable *tmp_ht;
     258         157 :                 int need_comma = 0;
     259             : 
     260         157 :                 zend_hash_internal_pointer_reset_ex(myht, &pos);
     261         568 :                 for (;; zend_hash_move_forward_ex(myht, &pos)) {
     262         725 :                         i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
     263         725 :                         if (i == HASH_KEY_NON_EXISTANT)
     264         157 :                                 break;
     265             : 
     266         568 :                         if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
     267         568 :                                 tmp_ht = HASH_OF(*data);
     268         568 :                                 if (tmp_ht) {
     269         144 :                                         tmp_ht->nApplyCount++;
     270             :                                 }
     271             : 
     272         568 :                                 if (r == PHP_JSON_OUTPUT_ARRAY) {
     273         303 :                                         if (need_comma) {
     274         207 :                                                 smart_str_appendc(buf, ',');
     275         207 :                                                 json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
     276             :                                         } else {
     277          96 :                                                 need_comma = 1;
     278             :                                         }
     279             : 
     280         303 :                                         json_pretty_print_indent(buf, options TSRMLS_CC);
     281         303 :                                         php_json_encode(buf, *data, options TSRMLS_CC);
     282         265 :                                 } else if (r == PHP_JSON_OUTPUT_OBJECT) {
     283         265 :                                         if (i == HASH_KEY_IS_STRING) {
     284         251 :                                                 if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) {
     285             :                                                         /* Skip protected and private members. */
     286           1 :                                                         if (tmp_ht) {
     287           1 :                                                                 tmp_ht->nApplyCount--;
     288             :                                                         }
     289           1 :                                                         continue;
     290             :                                                 }
     291             : 
     292         250 :                                                 if (need_comma) {
     293         196 :                                                         smart_str_appendc(buf, ',');
     294         196 :                                                         json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
     295             :                                                 } else {
     296          54 :                                                         need_comma = 1;
     297             :                                                 }
     298             : 
     299         250 :                                                 json_pretty_print_indent(buf, options TSRMLS_CC);
     300             : 
     301         250 :                                                 json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
     302         250 :                                                 smart_str_appendc(buf, ':');
     303             : 
     304         250 :                                                 json_pretty_print_char(buf, options, ' ' TSRMLS_CC);
     305             : 
     306         250 :                                                 php_json_encode(buf, *data, options TSRMLS_CC);
     307             :                                         } else {
     308          14 :                                                 if (need_comma) {
     309           8 :                                                         smart_str_appendc(buf, ',');
     310           8 :                                                         json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
     311             :                                                 } else {
     312           6 :                                                         need_comma = 1;
     313             :                                                 }
     314             : 
     315          14 :                                                 json_pretty_print_indent(buf, options TSRMLS_CC);
     316             : 
     317          14 :                                                 smart_str_appendc(buf, '"');
     318          14 :                                                 smart_str_append_long(buf, (long) index);
     319          14 :                                                 smart_str_appendc(buf, '"');
     320          14 :                                                 smart_str_appendc(buf, ':');
     321             : 
     322          14 :                                                 json_pretty_print_char(buf, options, ' ' TSRMLS_CC);
     323             : 
     324          14 :                                                 php_json_encode(buf, *data, options TSRMLS_CC);
     325             :                                         }
     326             :                                 }
     327             : 
     328         567 :                                 if (tmp_ht) {
     329         143 :                                         tmp_ht->nApplyCount--;
     330             :                                 }
     331             :                         }
     332         568 :                 }
     333             :         }
     334             : 
     335         199 :         --JSON_G(encoder_depth);
     336         199 :         json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
     337         199 :         json_pretty_print_indent(buf, options TSRMLS_CC);
     338             : 
     339         199 :         if (r == PHP_JSON_OUTPUT_ARRAY) {
     340         111 :                 smart_str_appendc(buf, ']');
     341             :         } else {
     342          88 :                 smart_str_appendc(buf, '}');
     343             :         }
     344             : }
     345             : /* }}} */
     346             : 
     347         621 : static int json_utf8_to_utf16(unsigned short *utf16, char utf8[], int len) /* {{{ */
     348             : {
     349         621 :         size_t pos = 0, us;
     350             :         int j, status;
     351             : 
     352         621 :         if (utf16) {
     353             :                 /* really convert the utf8 string */
     354       20084 :                 for (j=0 ; pos < len ; j++) {
     355       19474 :                         us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
     356       19474 :                         if (status != SUCCESS) {
     357           9 :                                 return -1;
     358             :                         }
     359             :                         /* From http://en.wikipedia.org/wiki/UTF16 */
     360       19465 :                         if (us >= 0x10000) {
     361          18 :                                 us -= 0x10000;
     362          18 :                                 utf16[j++] = (unsigned short)((us >> 10) | 0xd800);
     363          18 :                                 utf16[j] = (unsigned short)((us & 0x3ff) | 0xdc00);
     364             :                         } else {
     365       19447 :                                 utf16[j] = (unsigned short)us;
     366             :                         }
     367             :                 }
     368             :         } else {
     369             :                 /* Only check if utf8 string is valid, and compute utf16 lenght */
     370          79 :                 for (j=0 ; pos < len ; j++) {
     371          78 :                         us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
     372          78 :                         if (status != SUCCESS) {
     373           1 :                                 return -1;
     374             :                         }
     375          77 :                         if (us >= 0x10000) {
     376           1 :                                 j++;
     377             :                         }
     378             :                 }
     379             :         }
     380         611 :         return j;
     381             : }
     382             : /* }}} */
     383             : 
     384             : 
     385         456 : static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
     386             : {
     387         456 :         int pos = 0, ulen = 0;
     388             :         unsigned short us;
     389             :         unsigned short *utf16;
     390             :         size_t newlen;
     391             : 
     392         456 :         if (len == 0) {
     393          11 :                 smart_str_appendl(buf, "\"\"", 2);
     394          11 :                 return;
     395             :         }
     396             : 
     397         445 :         if (options & PHP_JSON_NUMERIC_CHECK) {
     398             :                 double d;
     399             :                 int type;
     400             :                 long p;
     401             : 
     402           5 :                 if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
     403           5 :                         if (type == IS_LONG) {
     404           3 :                                 smart_str_append_long(buf, p);
     405           2 :                         } else if (type == IS_DOUBLE) {
     406           4 :                                 if (!zend_isinf(d) && !zend_isnan(d)) {
     407             :                                         char *tmp;
     408           2 :                                         int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d);
     409           2 :                                         smart_str_appendl(buf, tmp, l);
     410           2 :                                         efree(tmp);
     411             :                                 } else {
     412           0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", d);
     413           0 :                                         smart_str_appendc(buf, '0');
     414             :                                 }
     415             :                         }
     416           5 :                         return;
     417             :                 }
     418             : 
     419             :         }
     420             : 
     421         440 :         utf16 = (options & PHP_JSON_UNESCAPED_UNICODE) ? NULL : (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
     422         440 :         ulen = json_utf8_to_utf16(utf16, s, len);
     423         440 :         if (ulen <= 0) {
     424          10 :                 if (utf16) {
     425           9 :                         efree(utf16);
     426             :                 }
     427          10 :                 if (ulen < 0) {
     428          10 :                         JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
     429          10 :                         if (!PG(display_errors)) {
     430           0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
     431             :                         }
     432          10 :                         smart_str_appendl(buf, "null", 4);
     433             :                 } else {
     434           0 :                         smart_str_appendl(buf, "\"\"", 2);
     435             :                 }
     436          10 :                 return;
     437             :         }
     438         430 :         if (!(options & PHP_JSON_UNESCAPED_UNICODE)) {
     439         429 :                 len = ulen;
     440             :         }
     441             : 
     442             :         /* pre-allocate for string length plus 2 quotes */
     443         430 :         smart_str_alloc(buf, len+2, 0);
     444         430 :         smart_str_appendc(buf, '"');
     445             : 
     446        4275 :         while (pos < len)
     447             :         {
     448        3415 :                 us = (options & PHP_JSON_UNESCAPED_UNICODE) ? s[pos++] : utf16[pos++];
     449             : 
     450        3415 :                 switch (us)
     451             :                 {
     452             :                         case '"':
     453          44 :                                 if (options & PHP_JSON_HEX_QUOT) {
     454           4 :                                         smart_str_appendl(buf, "\\u0022", 6);
     455             :                                 } else {
     456          40 :                                         smart_str_appendl(buf, "\\\"", 2);
     457             :                                 }
     458          44 :                                 break;
     459             : 
     460             :                         case '\\':
     461           8 :                                 smart_str_appendl(buf, "\\\\", 2);
     462           8 :                                 break;
     463             : 
     464             :                         case '/':
     465          52 :                                 if (options & PHP_JSON_UNESCAPED_SLASHES) {
     466           1 :                                         smart_str_appendc(buf, '/');
     467             :                                 } else {
     468          51 :                                         smart_str_appendl(buf, "\\/", 2);
     469             :                                 }
     470          52 :                                 break;
     471             : 
     472             :                         case '\b':
     473          10 :                                 smart_str_appendl(buf, "\\b", 2);
     474          10 :                                 break;
     475             : 
     476             :                         case '\f':
     477           8 :                                 smart_str_appendl(buf, "\\f", 2);
     478           8 :                                 break;
     479             : 
     480             :                         case '\n':
     481          11 :                                 smart_str_appendl(buf, "\\n", 2);
     482          11 :                                 break;
     483             : 
     484             :                         case '\r':
     485           8 :                                 smart_str_appendl(buf, "\\r", 2);
     486           8 :                                 break;
     487             : 
     488             :                         case '\t':
     489          10 :                                 smart_str_appendl(buf, "\\t", 2);
     490          10 :                                 break;
     491             : 
     492             :                         case '<':
     493          20 :                                 if (options & PHP_JSON_HEX_TAG) {
     494           2 :                                         smart_str_appendl(buf, "\\u003C", 6);
     495             :                                 } else {
     496          18 :                                         smart_str_appendc(buf, '<');
     497             :                                 }
     498          20 :                                 break;
     499             : 
     500             :                         case '>':
     501          20 :                                 if (options & PHP_JSON_HEX_TAG) {
     502           2 :                                         smart_str_appendl(buf, "\\u003E", 6);
     503             :                                 } else {
     504          18 :                                         smart_str_appendc(buf, '>');
     505             :                                 }
     506          20 :                                 break;
     507             : 
     508             :                         case '&':
     509          32 :                                 if (options & PHP_JSON_HEX_AMP) {
     510           4 :                                         smart_str_appendl(buf, "\\u0026", 6);
     511             :                                 } else {
     512          28 :                                         smart_str_appendc(buf, '&');
     513             :                                 }
     514          32 :                                 break;
     515             : 
     516             :                         case '\'':
     517          20 :                                 if (options & PHP_JSON_HEX_APOS) {
     518           4 :                                         smart_str_appendl(buf, "\\u0027", 6);
     519             :                                 } else {
     520          16 :                                         smart_str_appendc(buf, '\'');
     521             :                                 }
     522          20 :                                 break;
     523             : 
     524             :                         default:
     525        6186 :                                 if (us >= ' ' && ((options & PHP_JSON_UNESCAPED_UNICODE) || (us & 127) == us)) {
     526        3014 :                                         smart_str_appendc(buf, (unsigned char) us);
     527             :                                 } else {
     528         158 :                                         smart_str_appendl(buf, "\\u", 2);
     529         158 :                                         smart_str_appendc(buf, digits[(us & 0xf000) >> 12]);
     530         158 :                                         smart_str_appendc(buf, digits[(us & 0xf00)  >> 8]);
     531         158 :                                         smart_str_appendc(buf, digits[(us & 0xf0)   >> 4]);
     532         158 :                                         smart_str_appendc(buf, digits[(us & 0xf)]);
     533             :                                 }
     534             :                                 break;
     535             :                 }
     536             :         }
     537             : 
     538         430 :         smart_str_appendc(buf, '"');
     539         430 :         if (utf16) {
     540         429 :                 efree(utf16);
     541             :         }
     542             : }
     543             : /* }}} */
     544             : 
     545             : 
     546          12 : static void json_encode_serializable_object(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
     547             : {
     548          12 :         zend_class_entry *ce = Z_OBJCE_P(val);
     549          12 :         zval *retval = NULL, fname;
     550             :         HashTable* myht;
     551             : 
     552          12 :         if (Z_TYPE_P(val) == IS_ARRAY) {
     553           0 :                 myht = HASH_OF(val);
     554             :         } else {
     555          12 :                 myht = Z_OBJPROP_P(val);
     556             :         }
     557             : 
     558          12 :         if (myht && myht->nApplyCount > 1) {
     559           1 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
     560           1 :                 smart_str_appendl(buf, "null", 4);
     561           1 :                 return;
     562             :         }
     563             : 
     564          11 :         ZVAL_STRING(&fname, "jsonSerialize", 0);
     565             : 
     566          11 :         if (FAILURE == call_user_function_ex(EG(function_table), &val, &fname, &retval, 0, NULL, 1, NULL TSRMLS_CC) || !retval) {
     567           0 :                 zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Failed calling %s::jsonSerialize()", ce->name);
     568           0 :                 smart_str_appendl(buf, "null", sizeof("null") - 1);
     569           0 :                 return;
     570             :     }
     571             : 
     572          11 :         if (EG(exception)) {
     573             :                 /* Error already raised */
     574           0 :                 zval_ptr_dtor(&retval);
     575           0 :                 smart_str_appendl(buf, "null", sizeof("null") - 1);
     576           0 :                 return;
     577             :         }
     578             : 
     579          18 :         if ((Z_TYPE_P(retval) == IS_OBJECT) &&
     580           4 :                 (Z_OBJ_HANDLE_P(retval) == Z_OBJ_HANDLE_P(val))) {
     581             :                 /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
     582           3 :                 json_encode_array(buf, &retval, options TSRMLS_CC);
     583             :         } else {
     584             :                 /* All other types, encode as normal */
     585           8 :                 php_json_encode(buf, retval, options TSRMLS_CC);
     586             :         }
     587             : 
     588          11 :         zval_ptr_dtor(&retval);
     589             : }
     590             : /* }}} */
     591             : 
     592         699 : PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
     593             : {
     594         699 :         switch (Z_TYPE_P(val))
     595             :         {
     596             :                 case IS_NULL:
     597          26 :                         smart_str_appendl(buf, "null", 4);
     598          26 :                         break;
     599             : 
     600             :                 case IS_BOOL:
     601          34 :                         if (Z_BVAL_P(val)) {
     602          12 :                                 smart_str_appendl(buf, "true", 4);
     603             :                         } else {
     604          22 :                                 smart_str_appendl(buf, "false", 5);
     605             :                         }
     606          34 :                         break;
     607             : 
     608             :                 case IS_LONG:
     609         172 :                         smart_str_append_long(buf, Z_LVAL_P(val));
     610         172 :                         break;
     611             : 
     612             :                 case IS_DOUBLE:
     613             :                         {
     614          49 :                                 char *d = NULL;
     615             :                                 int len;
     616          49 :                                 double dbl = Z_DVAL_P(val);
     617             : 
     618          94 :                                 if (!zend_isinf(dbl) && !zend_isnan(dbl)) {
     619          45 :                                         len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
     620          45 :                                         smart_str_appendl(buf, d, len);
     621          45 :                                         efree(d);
     622             :                                 } else {
     623           4 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", dbl);
     624           4 :                                         smart_str_appendc(buf, '0');
     625             :                                 }
     626             :                         }
     627          49 :                         break;
     628             : 
     629             :                 case IS_STRING:
     630         206 :                         json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC);
     631         206 :                         break;
     632             : 
     633             :                 case IS_OBJECT:
     634          76 :                         if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce TSRMLS_CC)) {
     635          12 :                                 json_encode_serializable_object(buf, val, options TSRMLS_CC);
     636          12 :                                 break;
     637             :                         }
     638             :                         /* fallthrough -- Non-serializable object */
     639             :                 case IS_ARRAY:
     640         199 :                         json_encode_array(buf, &val, options TSRMLS_CC);
     641         199 :                         break;
     642             : 
     643             :                 default:
     644           1 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, encoded as null");
     645           1 :                         smart_str_appendl(buf, "null", 4);
     646             :                         break;
     647             :         }
     648             : 
     649         699 :         return;
     650             : }
     651             : /* }}} */
     652             : 
     653         181 : PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */
     654             : {
     655             :         int utf16_len;
     656             :         zval *z;
     657             :         unsigned short *utf16;
     658             :         JSON_parser jp;
     659             : 
     660         181 :         utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1);
     661             : 
     662         181 :         utf16_len = json_utf8_to_utf16(utf16, str, str_len);
     663         181 :         if (utf16_len <= 0) {
     664           0 :                 if (utf16) {
     665           0 :                         efree(utf16);
     666             :                 }
     667           0 :                 JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
     668           0 :                 RETURN_NULL();
     669             :         }
     670             : 
     671         181 :         if (depth <= 0) {
     672           0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Depth must be greater than zero");
     673           0 :                 efree(utf16);
     674           0 :                 RETURN_NULL();
     675             :         }
     676             : 
     677         181 :         ALLOC_INIT_ZVAL(z);
     678         181 :         jp = new_JSON_parser(depth);
     679         181 :         if (parse_JSON_ex(jp, z, utf16, utf16_len, options TSRMLS_CC)) {
     680          64 :                 *return_value = *z;
     681             :         }
     682             :         else
     683             :         {
     684             :                 double d;
     685             :                 int type, overflow_info;
     686             :                 long p;
     687         117 :                 char *trim = str;
     688         117 :                 int trim_len = str_len;
     689             : 
     690             :                 /* Increment trimmed string pointer to strip leading whitespace */
     691             :                 /* JSON RFC says to consider as whitespace: space, tab, LF or CR */
     692         254 :                 while (trim_len && (*trim == ' ' || *trim == '\t' || *trim == '\n' || *trim == '\r')) {
     693          20 :                         trim++;
     694          20 :                         trim_len--;
     695             :                 }
     696             : 
     697             :                 /* Decrement trimmed string length to strip trailing whitespace */
     698         254 :                 while (trim_len && (trim[trim_len - 1] == ' ' || trim[trim_len - 1] == '\t' || trim[trim_len - 1] == '\n' || trim[trim_len - 1] == '\r')) {
     699          20 :                         trim_len--;
     700             :                 }
     701             : 
     702         117 :                 RETVAL_NULL();
     703         117 :                 if (trim_len == 4) {
     704          34 :                         if (!strncasecmp(trim, "null", trim_len)) {
     705             :                                 /* We need to explicitly clear the error because its an actual NULL and not an error */
     706           2 :                                 jp->error_code = PHP_JSON_ERROR_NONE;
     707           2 :                                 RETVAL_NULL();
     708          32 :                         } else if (!strncasecmp(trim, "true", trim_len)) {
     709          26 :                                 RETVAL_BOOL(1);
     710             :                         }
     711          83 :                 } else if (trim_len == 5 && !strncasecmp(trim, "false", trim_len)) {
     712           2 :                         RETVAL_BOOL(0);
     713             :                 }
     714             : 
     715         117 :                 if ((type = is_numeric_string_ex(trim, trim_len, &p, &d, 0, &overflow_info)) != 0) {
     716          25 :                         if (type == IS_LONG) {
     717          14 :                                 RETVAL_LONG(p);
     718          11 :                         } else if (type == IS_DOUBLE) {
     719          15 :                                 if (options & PHP_JSON_BIGINT_AS_STRING && overflow_info) {
     720             :                                         /* Within an object or array, a numeric literal is assumed
     721             :                                          * to be an integer if and only if it's entirely made up of
     722             :                                          * digits (exponent notation will result in the number
     723             :                                          * being treated as a double). We'll match that behaviour
     724             :                                          * here. */
     725             :                                         int i;
     726           4 :                                         zend_bool is_float = 0;
     727             : 
     728         124 :                                         for (i = (trim[0] == '-' ? 1 : 0); i < trim_len; i++) {
     729             :                                                 /* Not using isdigit() because it's locale specific,
     730             :                                                  * but we expect JSON input to always be UTF-8. */
     731         122 :                                                 if (trim[i] < '0' || trim[i] > '9') {
     732           2 :                                                         is_float = 1;
     733           2 :                                                         break;
     734             :                                                 }
     735             :                                         }
     736             : 
     737           4 :                                         if (is_float) {
     738           2 :                                                 RETVAL_DOUBLE(d);
     739             :                                         } else {
     740           2 :                                                 RETVAL_STRINGL(trim, trim_len, 1);
     741             :                                         }
     742             :                                 } else {
     743           7 :                                         RETVAL_DOUBLE(d);
     744             :                                 }
     745             :                         }
     746             :                 }
     747             : 
     748         117 :                 if (Z_TYPE_P(return_value) != IS_NULL) {
     749          53 :                         jp->error_code = PHP_JSON_ERROR_NONE;
     750             :                 }
     751             : 
     752             :                 zval_dtor(z);
     753             :         }
     754         181 :         FREE_ZVAL(z);
     755         181 :         efree(utf16);
     756         181 :         JSON_G(error_code) = jp->error_code;
     757         181 :         free_JSON_parser(jp);
     758             : }
     759             : /* }}} */
     760             : 
     761             : 
     762             : /* {{{ proto string json_encode(mixed data [, int options])
     763             :    Returns the JSON representation of a value */
     764         126 : static PHP_FUNCTION(json_encode)
     765             : {
     766             :         zval *parameter;
     767         126 :         smart_str buf = {0};
     768         126 :         long options = 0;
     769             : 
     770         126 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &parameter, &options) == FAILURE) {
     771           2 :                 return;
     772             :         }
     773             : 
     774         124 :         JSON_G(error_code) = PHP_JSON_ERROR_NONE;
     775             : 
     776         124 :         php_json_encode(&buf, parameter, options TSRMLS_CC);
     777             : 
     778         124 :         ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
     779             : 
     780         124 :         smart_str_free(&buf);
     781             : }
     782             : /* }}} */
     783             : 
     784             : /* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]])
     785             :    Decodes the JSON representation into a PHP value */
     786         190 : static PHP_FUNCTION(json_decode)
     787             : {
     788             :         char *str;
     789             :         int str_len;
     790         190 :         zend_bool assoc = 0; /* return JS objects as PHP objects by default */
     791         190 :         long depth = JSON_PARSER_DEFAULT_DEPTH;
     792         190 :         long options = 0;
     793             : 
     794         190 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) {
     795           3 :                 return;
     796             :         }
     797             : 
     798         187 :         JSON_G(error_code) = 0;
     799             : 
     800         187 :         if (!str_len) {
     801           6 :                 RETURN_NULL();
     802             :         }
     803             : 
     804             :         /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */
     805         181 :         if (assoc) {
     806          56 :                 options |=  PHP_JSON_OBJECT_AS_ARRAY;
     807             :         } else {
     808         125 :                 options &= ~PHP_JSON_OBJECT_AS_ARRAY;
     809             :         }
     810             : 
     811         181 :         php_json_decode_ex(return_value, str, str_len, options, depth TSRMLS_CC);
     812             : }
     813             : /* }}} */
     814             : 
     815             : /* {{{ proto int json_last_error()
     816             :    Returns the error code of the last json_decode(). */
     817          37 : static PHP_FUNCTION(json_last_error)
     818             : {
     819          37 :         if (zend_parse_parameters_none() == FAILURE) {
     820           0 :                 return;
     821             :         }
     822             : 
     823          37 :         RETURN_LONG(JSON_G(error_code));
     824             : }
     825             : /* }}} */
     826             : 
     827             : /*
     828             :  * Local variables:
     829             :  * tab-width: 4
     830             :  * c-basic-offset: 4
     831             :  * End:
     832             :  * vim600: noet sw=4 ts=4 fdm=marker
     833             :  * vim<600: noet sw=4 ts=4
     834             :  */

Generated by: LCOV version 1.10

Generated at Thu, 10 Apr 2014 08:53:51 +0000 (9 days ago)

Copyright © 2005-2014 The PHP Group
All rights reserved.