pollita Fri, 21 May 2010 22:59:58 +0000 Revision: http://svn.php.net/viewvc?view=revision&revision=299605
Log: Add JSON_BIGINT_AS_STRING for json_decode() to parse large numbers as strings rather than casting to double and loosing precision. Changed paths: U php/php-src/trunk/NEWS U php/php-src/trunk/ext/json/JSON_parser.c U php/php-src/trunk/ext/json/JSON_parser.h U php/php-src/trunk/ext/json/json.c U php/php-src/trunk/ext/json/php_json.h A php/php-src/trunk/ext/json/tests/008.phpt U php/php-src/trunk/ext/json/tests/json_decode_error.phpt
Modified: php/php-src/trunk/NEWS =================================================================== --- php/php-src/trunk/NEWS 2010-05-21 22:55:18 UTC (rev 299604) +++ php/php-src/trunk/NEWS 2010-05-21 22:59:58 UTC (rev 299605) @@ -31,6 +31,7 @@ - Added closure $this support back. (Stas) - Added SplObjectStorage::getHash() hook. (Etienne) - Added JsonSerializable interface (Sara) +- Added JSON_BIGINT_AS_STRING, extended json_decode() sig with $options. (Sara) - Added support for storing upload progress feedback in session data. (Arnaud) - Added support for CURLOPT_MAX_RECV_SPEED_LARGE and CURLOPT_MAX_SEND_SPEED_LARGE. FR #51815. (Pierrick) Modified: php/php-src/trunk/ext/json/JSON_parser.c =================================================================== --- php/php-src/trunk/ext/json/JSON_parser.c 2010-05-21 22:55:18 UTC (rev 299604) +++ php/php-src/trunk/ext/json/JSON_parser.c 2010-05-21 22:59:58 UTC (rev 299605) @@ -291,12 +291,14 @@ } -static void json_create_zval(zval **z, smart_str *buf, int type) +static void json_create_zval(zval **z, smart_str *buf, int type, int options) { ALLOC_INIT_ZVAL(*z); if (type == IS_LONG) { + zend_bool bigint = 0; + if (buf->c[0] == '-') { buf->len--; } @@ -306,9 +308,22 @@ int cmp = strcmp(buf->c + (buf->c[0] == '-'), long_min_digits); if (!(cmp < 0 || (cmp == 0 && buf->c[0] == '-'))) { - goto use_double; + bigint = 1; } } else { + bigint = 1; + } + } + + if (bigint) { + /* value too large to represent as a long */ + if (options & PHP_JSON_BIGINT_AS_STRING) { + if (buf->c[0] == '-') { + /* Restore last char consumed above */ + buf->len++; + } + goto use_string; + } else { goto use_double; } } @@ -322,6 +337,7 @@ } else if (type == IS_STRING) { +use_string: ZVAL_STRINGL(*z, buf->c, buf->len, 1); } else if (type == IS_BOOL) @@ -420,12 +436,13 @@ machine with a stack. */ int -parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int assoc TSRMLS_DC) +parse_JSON_ex(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int options TSRMLS_DC) { int next_char; /* the next character */ int next_class; /* the next character class */ int next_state; /* the next state */ int the_index; + int assoc = options & PHP_JSON_OBJECT_AS_ARRAY; smart_str buf = {0}; smart_str key = {0}; @@ -530,7 +547,7 @@ zval *mval; smart_str_0(&buf); - json_create_zval(&mval, &buf, type); + json_create_zval(&mval, &buf, type, options); if (!assoc) { add_property_zval_ex(jp->the_zstack[jp->top], (key.len ? key.c : "_empty_"), (key.len ? (key.len + 1) : sizeof("_empty_")), mval TSRMLS_CC); @@ -558,7 +575,7 @@ zval *mval; smart_str_0(&buf); - json_create_zval(&mval, &buf, type); + json_create_zval(&mval, &buf, type, options); add_next_index_zval(jp->the_zstack[jp->top], mval); buf.len = 0; JSON_RESET_TYPE(); @@ -669,7 +686,7 @@ jp->stack[jp->top] == MODE_ARRAY)) { smart_str_0(&buf); - json_create_zval(&mval, &buf, type); + json_create_zval(&mval, &buf, type, options); } switch (jp->stack[jp->top]) { Modified: php/php-src/trunk/ext/json/JSON_parser.h =================================================================== --- php/php-src/trunk/ext/json/JSON_parser.h 2010-05-21 22:55:18 UTC (rev 299604) +++ php/php-src/trunk/ext/json/JSON_parser.h 2010-05-21 22:59:58 UTC (rev 299605) @@ -5,6 +5,7 @@ #include "php.h" #include "ext/standard/php_smart_str.h" +#include "php_json.h" #define JSON_PARSER_DEFAULT_DEPTH 512 @@ -28,6 +29,12 @@ }; extern JSON_parser new_JSON_parser(int depth); -extern int parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int assoc TSRMLS_DC); +extern int parse_JSON_ex(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int options TSRMLS_DC); extern int free_JSON_parser(JSON_parser jp); + +static inline int parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int assoc TSRMLS_DC) +{ + parse_JSON_ex(jp, z, utf16_json, length, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0 TSRMLS_CC); +} + #endif Modified: php/php-src/trunk/ext/json/json.c =================================================================== --- php/php-src/trunk/ext/json/json.c 2010-05-21 22:55:18 UTC (rev 299604) +++ php/php-src/trunk/ext/json/json.c 2010-05-21 22:59:58 UTC (rev 299605) @@ -52,6 +52,7 @@ ZEND_ARG_INFO(0, json) ZEND_ARG_INFO(0, assoc) ZEND_ARG_INFO(0, depth) + ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0) @@ -99,6 +100,9 @@ REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_UTF8", PHP_JSON_ERROR_UTF8, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_OBJECT_AS_ARRAY", PHP_JSON_OBJECT_AS_ARRAY, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_BIGINT_AS_STRING", PHP_JSON_BIGINT_AS_STRING, CONST_CS | CONST_PERSISTENT); + return SUCCESS; } /* }}} */ @@ -542,7 +546,7 @@ } /* }}} */ -PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) /* {{{ */ +PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */ { int utf16_len; zval *z; @@ -567,7 +571,7 @@ ALLOC_INIT_ZVAL(z); jp = new_JSON_parser(depth); - if (parse_JSON(jp, z, utf16, utf16_len, assoc TSRMLS_CC)) { + if (parse_JSON_ex(jp, z, utf16, utf16_len, options TSRMLS_CC)) { *return_value = *z; } else @@ -610,6 +614,7 @@ } /* }}} */ + /* {{{ proto string json_encode(mixed data [, int options]) Returns the JSON representation of a value */ static PHP_FUNCTION(json_encode) @@ -638,8 +643,9 @@ int str_len; zend_bool assoc = 0; /* return JS objects as PHP objects by default */ long depth = JSON_PARSER_DEFAULT_DEPTH; + long options = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &str, &str_len, &assoc, &depth) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) { return; } @@ -647,7 +653,14 @@ RETURN_NULL(); } - php_json_decode(return_value, str, str_len, assoc, depth TSRMLS_CC); + /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */ + if (assoc) { + options |= PHP_JSON_OBJECT_AS_ARRAY; + } else { + options &= ~PHP_JSON_OBJECT_AS_ARRAY; + } + + php_json_decode_ex(return_value, str, str_len, options, depth TSRMLS_CC); } /* }}} */ Modified: php/php-src/trunk/ext/json/php_json.h =================================================================== --- php/php-src/trunk/ext/json/php_json.h 2010-05-21 22:55:18 UTC (rev 299604) +++ php/php-src/trunk/ext/json/php_json.h 2010-05-21 22:59:58 UTC (rev 299605) @@ -48,9 +48,11 @@ #endif PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC); -PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC); +PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC); extern zend_class_entry *php_json_serializable_ce; + +/* json_encode() options */ #define PHP_JSON_HEX_TAG (1<<0) #define PHP_JSON_HEX_AMP (1<<1) #define PHP_JSON_HEX_APOS (1<<2) @@ -58,9 +60,20 @@ #define PHP_JSON_FORCE_OBJECT (1<<4) #define PHP_JSON_NUMERIC_CHECK (1<<5) -#define PHP_JSON_OUTPUT_ARRAY 0 -#define PHP_JSON_OUTPUT_OBJECT 1 +/* Internal flags */ +#define PHP_JSON_OUTPUT_ARRAY 0 +#define PHP_JSON_OUTPUT_OBJECT 1 +/* json_decode() options */ +#define PHP_JSON_OBJECT_AS_ARRAY (1<<0) +#define PHP_JSON_BIGINT_AS_STRING (1<<1) + +static inline php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) +{ + php_json_decode_ex(return_value, str, str_len, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0, depth TSRMLS_CC); +} + + #endif /* PHP_JSON_H */ /* Added: php/php-src/trunk/ext/json/tests/008.phpt =================================================================== --- php/php-src/trunk/ext/json/tests/008.phpt (rev 0) +++ php/php-src/trunk/ext/json/tests/008.phpt 2010-05-21 22:59:58 UTC (rev 299605) @@ -0,0 +1,17 @@ +--TEST-- +json_decode() with large integers +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +$json = '{"largenum":123456789012345678901234567890}'; +$x = json_decode($json); +var_dump($x->largenum); +$x = json_decode($json, false, 512, JSON_BIGINT_AS_STRING); +var_dump($x->largenum); +echo "Done\n"; +?> +--EXPECT-- +float(1.2345678901235E+29) +string(30) "123456789012345678901234567890" +Done Modified: php/php-src/trunk/ext/json/tests/json_decode_error.phpt =================================================================== --- php/php-src/trunk/ext/json/tests/json_decode_error.phpt 2010-05-21 22:55:18 UTC (rev 299604) +++ php/php-src/trunk/ext/json/tests/json_decode_error.phpt 2010-05-21 22:59:58 UTC (rev 299605) @@ -20,7 +20,7 @@ echo "\n-- Testing json_decode() function with more than expected no. of arguments --\n"; $extra_arg = 10; -var_dump( json_decode('"abc"', TRUE, 512, $extra_arg) ); +var_dump( json_decode('"abc"', TRUE, 512, 0, $extra_arg) ); ?> ===Done=== @@ -34,6 +34,6 @@ -- Testing json_decode() function with more than expected no. of arguments -- -Warning: json_decode() expects at most 3 parameters, 4 given in %s on line %d +Warning: json_decode() expects at most 4 parameters, 5 given in %s on line %d NULL ===Done===
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php