From: Dmitry Stogov Date: Tue, 19 Dec 2006 11:55:16 +0000 (+0000) Subject: Fixed bug #39815 (SOAP double encoding is not locale-independent) X-Git-Tag: RELEASE_1_0_0RC1~648 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=869003830b264dc52703804d230a9f1e466238e6;p=php Fixed bug #39815 (SOAP double encoding is not locale-independent) --- diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index f689d333a2..338239ff00 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -928,19 +928,23 @@ static xmlNodePtr to_xml_double(encodeTypePtr type, zval *data, int style, xmlNo { xmlNodePtr ret; zval tmp; + char *str; + TSRMLS_FETCH(); ret = xmlNewNode(NULL, BAD_CAST("BOGUS")); xmlAddChild(parent, ret); FIND_ZVAL_NULL(data, ret, style); tmp = *data; - zval_copy_ctor(&tmp); if (Z_TYPE(tmp) != IS_DOUBLE) { + zval_copy_ctor(&tmp); convert_to_double(&tmp); } - convert_to_string(&tmp); - xmlNodeSetContentLen(ret, BAD_CAST(Z_STRVAL(tmp)), Z_STRLEN(tmp)); - zval_dtor(&tmp); + + str = (char *) emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1); + php_gcvt(Z_DVAL(tmp), EG(precision), '.', 'E', str); + xmlNodeSetContentLen(ret, BAD_CAST(str), strlen(str)); + efree(str); if (style == SOAP_ENCODED) { set_ns_and_type(ret, type); diff --git a/ext/soap/tests/bugs/bug39815.phpt b/ext/soap/tests/bugs/bug39815.phpt new file mode 100755 index 0000000000..c9695d7a41 --- /dev/null +++ b/ext/soap/tests/bugs/bug39815.phpt @@ -0,0 +1,42 @@ +--TEST-- +Bug #39815 (to_zval_double() in ext/soap/php_encoding.c is not locale-independent) +--SKIPIF-- + +--FILE-- +server = new SoapServer($wsdl, $options); + $this->server->addFunction('test'); + } + + function __doRequest($request, $location, $action, $version) { + ob_start(); + $this->server->handle($request); + $response = ob_get_contents(); + ob_end_clean(); + return $response; + } + +} +$x = new LocalSoapClient(NULL,array('location'=>'test://', + 'uri'=>'http://testuri.org', + "trace"=>1)); +@setlocale(LC_ALL,"sv_SE"); +var_dump($x->test()); +echo $x->__getLastResponse(); +@setlocale(LC_ALL,"en_US"); +var_dump($x->test()); +echo $x->__getLastResponse(); +--EXPECT-- +float(123,456) + +123.456 +float(123.456) + +123.456 diff --git a/ext/soap/tests/interop/Round3/GroupD/r3_groupD_compound2_001w.phpt b/ext/soap/tests/interop/Round3/GroupD/r3_groupD_compound2_001w.phpt index 9aa448ce9e..4fca0b84d1 100644 --- a/ext/soap/tests/interop/Round3/GroupD/r3_groupD_compound2_001w.phpt +++ b/ext/soap/tests/interop/Round3/GroupD/r3_groupD_compound2_001w.phpt @@ -31,7 +31,7 @@ echo "ok\n"; ?> --EXPECT-- -Shanetrue100000012345 +Shanetrue1.0E+612345 -Shanetrue100000012345 +Shanetrue1.0E+612345 ok diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index d9031841fe..eea91efdcf 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -196,8 +196,9 @@ php_sprintf_appenddouble(char **buffer, int *pos, TSRMLS_DC) { char num_buf[NUM_BUF_SIZE]; - char *s = NULL, *q; + char *s = NULL; int s_len = 0, is_negative = 0; + struct lconv *lconv; PRINTF_DEBUG(("sprintf: appenddouble(%x, %x, %x, %f, %d, '%c', %d, %c)\n", *buffer, pos, size, number, width, padding, alignment, fmt)); @@ -229,7 +230,9 @@ php_sprintf_appenddouble(char **buffer, int *pos, case 'E': case 'f': case 'F': - s = ap_php_conv_fp(fmt, number, 0, precision, + lconv = localeconv(); + s = php_conv_fp((fmt == 'f')?'F':fmt, number, 0, precision, + (fmt == 'f')?(*lconv->decimal_point):'.', &is_negative, &num_buf[1], &s_len); if (is_negative) { num_buf[0] = '-'; @@ -249,7 +252,8 @@ php_sprintf_appenddouble(char **buffer, int *pos, /* * * We use &num_buf[ 1 ], so that we have room for the sign */ - s = bsd_gcvt(number, precision, &num_buf[1]); + lconv = localeconv(); + s = php_gcvt(number, precision, *lconv->decimal_point, (fmt == 'G')?'E':'e', &num_buf[1]); is_negative = 0; if (*s == '-') { is_negative = 1; @@ -260,10 +264,6 @@ php_sprintf_appenddouble(char **buffer, int *pos, } s_len = strlen(s); - - if (fmt == 'G' && (q = strchr(s, 'e')) != NULL) { - *q = 'E'; - } break; } diff --git a/main/snprintf.c b/main/snprintf.c index 30f99c575b..f6df906d52 100644 --- a/main/snprintf.c +++ b/main/snprintf.c @@ -115,23 +115,20 @@ static char * __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, return(s); } -char *bsd_ecvt(double value, int ndigit, int *decpt, int *sign) +static inline char *php_ecvt(double value, int ndigit, int *decpt, int *sign) { return(__cvt(value, ndigit, decpt, sign, 0, 1)); } -char *bsd_fcvt(double value, int ndigit, int *decpt, int *sign) +static inline char *php_fcvt(double value, int ndigit, int *decpt, int *sign) { return(__cvt(value, ndigit, decpt, sign, 1, 1)); } -char *bsd_gcvt(double value, int ndigit, char *buf) +PHPAPI char *php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf) { char *digits, *dst, *src; int i, decpt, sign; - struct lconv *lconv; - - lconv = localeconv(); digits = zend_dtoa(value, 2, ndigit, &decpt, &sign, NULL); if (decpt == 9999) { @@ -161,7 +158,7 @@ char *bsd_gcvt(double value, int ndigit, char *buf) sign = 0; src = digits; *dst++ = *src++; - *dst++ = *lconv->decimal_point; + *dst++ = dec_point; if (*src == '\0') { *dst++ = '0'; } else { @@ -169,7 +166,7 @@ char *bsd_gcvt(double value, int ndigit, char *buf) *dst++ = *src++; } while (*src != '\0'); } - *dst++ = 'e'; + *dst++ = exponent; if (sign) *dst++ = '-'; else @@ -190,7 +187,7 @@ char *bsd_gcvt(double value, int ndigit, char *buf) } else if (decpt < 0) { /* standard format 0. */ *dst++ = '0'; /* zero before decimal point */ - *dst++ = *lconv->decimal_point; + *dst++ = dec_point; do { *dst++ = '0'; } while (++decpt < 0); @@ -210,7 +207,7 @@ char *bsd_gcvt(double value, int ndigit, char *buf) if (*src != '\0') { if (src == digits) *dst++ = '0'; /* zero before decimal point */ - *dst++ = *lconv->decimal_point; + *dst++ = dec_point; for (i = decpt; digits[i] != '\0'; i++) { *dst++ = digits[i]; } @@ -357,29 +354,21 @@ char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned, * The sign is returned in the is_negative argument (and is not placed * in buf). */ -char * ap_php_conv_fp(register char format, register double num, - boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len) +PHPAPI char * php_conv_fp(register char format, register double num, + boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, int *len) { register char *s = buf; register char *p, *p_orig; int decimal_point; - char dec_point = '.'; - - if (format == 'f') { - struct lconv *lconv; - lconv = localeconv(); - dec_point = *lconv->decimal_point; - format = 'F'; - } if (precision >= NDIG - 1) { precision = NDIG - 2; } if (format == 'F') - p_orig = p = bsd_fcvt(num, precision, &decimal_point, is_negative); + p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative); else /* either e or E format */ - p_orig = p = bsd_ecvt(num, precision + 1, &decimal_point, is_negative); + p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative); /* * Check for Infinity and NaN @@ -595,6 +584,8 @@ static int format_converter(register buffy * odp, const char *fmt, char num_buf[NUM_BUF_SIZE]; char char_buf[2]; /* for printing %% and % */ + struct lconv *lconv = NULL; + /* * Flag variables */ @@ -929,6 +920,7 @@ static int format_converter(register buffy * odp, const char *fmt, case 'f': + case 'F': case 'e': case 'E': switch(modifier) { @@ -949,8 +941,12 @@ static int format_converter(register buffy * odp, const char *fmt, s = "inf"; s_len = 3; } else { - s = ap_php_conv_fp(*fmt, fp_num, alternate_form, + if (!lconv) { + lconv = localeconv(); + } + s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form, (adjust_precision == NO) ? FLOAT_DIGITS : precision, + (*fmt == 'f')?(*lconv->decimal_point):'.', &is_negative, &num_buf[1], &s_len); if (is_negative) prefix_char = '-'; @@ -997,7 +993,10 @@ static int format_converter(register buffy * odp, const char *fmt, /* * * We use &num_buf[ 1 ], so that we have room for the sign */ - s = bsd_gcvt(fp_num, precision, &num_buf[1]); + if (!lconv) { + lconv = localeconv(); + } + s = php_gcvt(fp_num, precision, *lconv->decimal_point, (*fmt == 'G')?'E':'e', &num_buf[1]); if (*s == '-') prefix_char = *s++; else if (print_sign) @@ -1009,8 +1008,6 @@ static int format_converter(register buffy * odp, const char *fmt, if (alternate_form && (q = strchr(s, '.')) == NULL) s[s_len++] = '.'; - if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) - *q = 'E'; break; diff --git a/main/snprintf.h b/main/snprintf.h index 2512ca79b3..e20d5a81f0 100644 --- a/main/snprintf.h +++ b/main/snprintf.h @@ -65,10 +65,19 @@ Example: #ifndef SNPRINTF_H #define SNPRINTF_H +typedef int bool_int; + +typedef enum { + NO = 0, YES = 1 +} boolean_e; + BEGIN_EXTERN_C() PHPAPI int ap_php_snprintf(char *, size_t, const char *, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); PHPAPI int ap_php_vsnprintf(char *, size_t, const char *, va_list ap) PHP_ATTRIBUTE_FORMAT(printf, 3, 0); PHPAPI int php_sprintf (char* s, const char* format, ...) PHP_ATTRIBUTE_FORMAT(printf, 2, 3); +PHPAPI char * php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf); +PHPAPI char * php_conv_fp(register char format, register double num, + boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, int *len); END_EXTERN_C() #ifdef snprintf @@ -86,10 +95,6 @@ END_EXTERN_C() #endif #define sprintf php_sprintf -typedef enum { - NO = 0, YES = 1 -} boolean_e; - typedef enum { LM_STD = 0, #if SIZEOF_INTMAX_T @@ -118,21 +123,12 @@ typedef enum { typedef WIDE_INT wide_int; typedef unsigned WIDE_INT u_wide_int; -typedef int bool_int; - extern char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned, register bool_int * is_negative, char *buf_end, register int *len); -extern char * ap_php_conv_fp(register char format, register double num, - boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len); - extern char * ap_php_conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len); -extern char * bsd_ecvt(double value, int ndigit, int *decpt, int *sign); -extern char * bsd_fcvt(double value, int ndigit, int *decpt, int *sign); -extern char * bsd_gcvt(double value, int ndigit, char *buf); - #endif /* SNPRINTF_H */ /* diff --git a/main/spprintf.c b/main/spprintf.c index c46bd70c61..6e10e52841 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -91,6 +91,10 @@ #include #endif +#ifdef HAVE_LOCALE_H +#include +#endif + #include "snprintf.h" #define FALSE 0 @@ -226,6 +230,8 @@ static void xbuf_format_converter(int unicode, smart_str *xbuf, const char *fmt, char char_buf[2]; /* for printing %% and % */ zend_bool free_s; /* free string if allocated here */ + struct lconv *lconv = NULL; + /* * Flag variables */ @@ -617,6 +623,7 @@ fmt_string: break; case 'f': + case 'F': case 'e': case 'E': switch(modifier) { @@ -637,8 +644,12 @@ fmt_string: s = "inf"; s_len = 3; } else { - s = ap_php_conv_fp(*fmt, fp_num, alternate_form, - (adjust_precision == NO) ? FLOAT_DIGITS : precision, + if (!lconv) { + lconv = localeconv(); + } + s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form, + (adjust_precision == NO) ? FLOAT_DIGITS : precision, + (*fmt == 'f')?(*lconv->decimal_point):'.', &is_negative, &num_buf[1], &s_len); if (is_negative) prefix_char = '-'; @@ -685,7 +696,10 @@ fmt_string: /* * * We use &num_buf[ 1 ], so that we have room for the sign */ - s = bsd_gcvt(fp_num, precision, &num_buf[1]); + if (!lconv) { + lconv = localeconv(); + } + s = php_gcvt(fp_num, precision, *lconv->decimal_point, (*fmt == 'G')?'E':'e', &num_buf[1]); if (*s == '-') prefix_char = *s++; else if (print_sign) @@ -697,8 +711,6 @@ fmt_string: if (alternate_form && (q = strchr(s, '.')) == NULL) s[s_len++] = '.'; - if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) - *q = 'E'; break;