From ac7cfad3b54b04b7ff2d0e4bfd26e8b61d233613 Mon Sep 17 00:00:00 2001 From: Juan Basso <jrbasso@gmail.com> Date: Sat, 29 Mar 2014 19:41:48 -0400 Subject: [PATCH] Fixed bug #50224 where float without decimals were converted to integer --- NEWS | 4 +++ UPGRADING | 3 ++ ext/json/json.c | 37 +++++++++++++++++----- ext/json/php_json.h | 1 + ext/json/tests/bug50224.phpt | 60 ++++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 ext/json/tests/bug50224.phpt diff --git a/NEWS b/NEWS index 7c9e45571a..d8589ab699 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,10 @@ . Fixed bug #68750 (PDOMysql with mysqlnd does not allow the usage of named pipes). (steffenb198@aol.com) +- JSON: + . Fixed bug #50224 (json_encode() does not always encode a float as a float) + by adding JSON_PRESERVE_ZERO_FRACTION. (Juan Basso) + 22 Jan 2015, PHP 5.6.5 - Core: diff --git a/UPGRADING b/UPGRADING index 90e41631c8..9428add450 100644 --- a/UPGRADING +++ b/UPGRADING @@ -262,6 +262,9 @@ PHP 5.6 UPGRADE NOTES Added scanner mode INI_SCANNER_TYPED to yield typed .ini values. For PHP >= 5.6.1 +- JSON: + Added JSON_PRESERVE_ZERO_FRACTION option (PHP >= 5.6.5) + ======================================== 6. New Functions ======================================== diff --git a/ext/json/json.c b/ext/json/json.c index f9765ab3ff..a28f99e10e 100644 --- a/ext/json/json.c +++ b/ext/json/json.c @@ -31,6 +31,14 @@ #include "php_json.h" #include <zend_exceptions.h> +#include <float.h> +#if defined(DBL_MANT_DIG) && defined(DBL_MIN_EXP) +#define NUM_BUF_SIZE (3 + DBL_MANT_DIG - DBL_MIN_EXP) +#else +#define NUM_BUF_SIZE 1080 +#endif + + static PHP_MINFO_FUNCTION(json); static PHP_FUNCTION(json_encode); static PHP_FUNCTION(json_decode); @@ -103,6 +111,7 @@ static PHP_MINIT_FUNCTION(json) REGISTER_LONG_CONSTANT("JSON_PRETTY_PRINT", PHP_JSON_PRETTY_PRINT, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_UNESCAPED_UNICODE", PHP_JSON_UNESCAPED_UNICODE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_PARTIAL_OUTPUT_ON_ERROR", PHP_JSON_PARTIAL_OUTPUT_ON_ERROR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_PRESERVE_ZERO_FRACTION", PHP_JSON_PRESERVE_ZERO_FRACTION, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT); @@ -420,10 +429,17 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR 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); + char num[NUM_BUF_SIZE]; + int l; + + php_gcvt(d, EG(precision), '.', 'e', (char *)num); + l = strlen(num); + if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && l < NUM_BUF_SIZE - 2) { + num[l++] = '.'; + num[l++] = '0'; + num[l] = '\0'; + } + smart_str_appendl(buf, num, l); } else { JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN; smart_str_appendc(buf, '0'); @@ -624,14 +640,19 @@ PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_ case IS_DOUBLE: { - char *d = NULL; + char num[NUM_BUF_SIZE]; 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); + php_gcvt(dbl, EG(precision), '.', 'e', (char *)num); + len = strlen(num); + if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < NUM_BUF_SIZE - 2) { + num[len++] = '.'; + num[len++] = '0'; + num[len] = '\0'; + } + smart_str_appendl(buf, num, len); } else { JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN; smart_str_appendc(buf, '0'); diff --git a/ext/json/php_json.h b/ext/json/php_json.h index 7347ef7e06..efd872b5f4 100644 --- a/ext/json/php_json.h +++ b/ext/json/php_json.h @@ -65,6 +65,7 @@ extern PHP_JSON_API zend_class_entry *php_json_serializable_ce; #define PHP_JSON_PRETTY_PRINT (1<<7) #define PHP_JSON_UNESCAPED_UNICODE (1<<8) #define PHP_JSON_PARTIAL_OUTPUT_ON_ERROR (1<<9) +#define PHP_JSON_PRESERVE_ZERO_FRACTION (1<<10) /* Internal flags */ #define PHP_JSON_OUTPUT_ARRAY 0 diff --git a/ext/json/tests/bug50224.phpt b/ext/json/tests/bug50224.phpt new file mode 100644 index 0000000000..3408ac906f --- /dev/null +++ b/ext/json/tests/bug50224.phpt @@ -0,0 +1,60 @@ +--TEST-- +bug #50224 (json_encode() does not always encode a float as a float) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +echo "* Testing JSON output\n\n"; +var_dump(json_encode(12.3, JSON_PRESERVE_ZERO_FRACTION)); +var_dump(json_encode(12, JSON_PRESERVE_ZERO_FRACTION)); +var_dump(json_encode(12.0, JSON_PRESERVE_ZERO_FRACTION)); +var_dump(json_encode(0.0, JSON_PRESERVE_ZERO_FRACTION)); +var_dump(json_encode(array(12, 12.0, 12.3), JSON_PRESERVE_ZERO_FRACTION)); +var_dump(json_encode((object)array('float' => 12.0, 'integer' => 12), JSON_PRESERVE_ZERO_FRACTION)); + +echo "\n* Testing encode/decode symmetry\n\n"; + +var_dump(json_decode(json_encode(12.3, JSON_PRESERVE_ZERO_FRACTION))); +var_dump(json_decode(json_encode(12, JSON_PRESERVE_ZERO_FRACTION))); +var_dump(json_decode(json_encode(12.0, JSON_PRESERVE_ZERO_FRACTION))); +var_dump(json_decode(json_encode(0.0, JSON_PRESERVE_ZERO_FRACTION))); +var_dump(json_decode(json_encode(array(12, 12.0, 12.3), JSON_PRESERVE_ZERO_FRACTION))); +var_dump(json_decode(json_encode((object)array('float' => 12.0, 'integer' => 12), JSON_PRESERVE_ZERO_FRACTION))); +var_dump(json_decode(json_encode((object)array('float' => 12.0, 'integer' => 12), JSON_PRESERVE_ZERO_FRACTION), true)); +?> +--EXPECTF-- +* Testing JSON output + +string(4) "12.3" +string(2) "12" +string(4) "12.0" +string(3) "0.0" +string(14) "[12,12.0,12.3]" +string(27) "{"float":12.0,"integer":12}" + +* Testing encode/decode symmetry + +float(12.3) +int(12) +float(12) +float(0) +array(3) { + [0]=> + int(12) + [1]=> + float(12) + [2]=> + float(12.3) +} +object(stdClass)#%d (2) { + ["float"]=> + float(12) + ["integer"]=> + int(12) +} +array(2) { + ["float"]=> + float(12) + ["integer"]=> + int(12) +} \ No newline at end of file -- 2.40.0