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

Reply via email to