summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorSara Golemon <pollita@php.net>2010-05-21 22:59:58 (GMT)
committerSara Golemon <pollita@php.net>2010-05-21 22:59:58 (GMT)
commit6eb4218433e5b2af2974648b31ca434b1aa4e314 (patch)
tree7107a79d39863c12df805228fc20dd27329c306c /ext
parenta25482105ec71b40f14d1f6bf1ce0ee72622047b (diff)
downloadphp-6eb4218433e5b2af2974648b31ca434b1aa4e314.tar.gz
Add JSON_BIGINT_AS_STRING for json_decode() to parse large numbers
as strings rather than casting to double and loosing precision.
Diffstat (limited to 'ext')
-rw-r--r--ext/json/JSON_parser.c29
-rw-r--r--ext/json/JSON_parser.h9
-rw-r--r--ext/json/json.c21
-rw-r--r--ext/json/php_json.h19
-rw-r--r--ext/json/tests/008.phpt17
-rw-r--r--ext/json/tests/json_decode_error.phpt4
6 files changed, 83 insertions, 16 deletions
diff --git a/ext/json/JSON_parser.c b/ext/json/JSON_parser.c
index 259b77b..ef05f3b 100644
--- a/ext/json/JSON_parser.c
+++ b/ext/json/JSON_parser.c
@@ -291,12 +291,14 @@ static int dehexchar(char c)
}
-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,8 +308,21 @@ static void json_create_zval(zval **z, smart_str *buf, int type)
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 @@ use_double:
}
else if (type == IS_STRING)
{
+use_string:
ZVAL_STRINGL(*z, buf->c, buf->len, 1);
}
else if (type == IS_BOOL)
@@ -420,12 +436,13 @@ static void attach_zval(JSON_parser jp, int up, int cur, smart_str *key, int ass
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 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int
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 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int
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 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int
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]) {
diff --git a/ext/json/JSON_parser.h b/ext/json/JSON_parser.h
index 746190b..e02c9fc 100644
--- a/ext/json/JSON_parser.h
+++ b/ext/json/JSON_parser.h
@@ -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 @@ enum error_codes {
};
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
diff --git a/ext/json/json.c b/ext/json/json.c
index 93445d6..667774a 100644
--- a/ext/json/json.c
+++ b/ext/json/json.c
@@ -52,6 +52,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1)
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 @@ static PHP_MINIT_FUNCTION(json)
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_encode(smart_str *buf, zval *val, int options TSRMLS_
}
/* }}} */
-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 @@ PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, ze
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 @@ PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, ze
}
/* }}} */
+
/* {{{ proto string json_encode(mixed data [, int options])
Returns the JSON representation of a value */
static PHP_FUNCTION(json_encode)
@@ -638,8 +643,9 @@ static PHP_FUNCTION(json_decode)
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 @@ static PHP_FUNCTION(json_decode)
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);
}
/* }}} */
diff --git a/ext/json/php_json.h b/ext/json/php_json.h
index 5ff8d1b..7b67c10 100644
--- a/ext/json/php_json.h
+++ b/ext/json/php_json.h
@@ -48,9 +48,11 @@ ZEND_END_MODULE_GLOBALS(json)
#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,8 +60,19 @@ extern zend_class_entry *php_json_serializable_ce;
#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 */
diff --git a/ext/json/tests/008.phpt b/ext/json/tests/008.phpt
new file mode 100644
index 0000000..f2354d3
--- /dev/null
+++ b/ext/json/tests/008.phpt
@@ -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
diff --git a/ext/json/tests/json_decode_error.phpt b/ext/json/tests/json_decode_error.phpt
index f3387c2..4d5d4e4 100644
--- a/ext/json/tests/json_decode_error.phpt
+++ b/ext/json/tests/json_decode_error.phpt
@@ -20,7 +20,7 @@ var_dump( json_decode() );
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 @@ NULL
-- 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===