]> granicus.if.org Git - php/commitdiff
Fixed bug #39815 (SOAP double encoding is not locale-independent)
authorDmitry Stogov <dmitry@php.net>
Tue, 19 Dec 2006 11:54:38 +0000 (11:54 +0000)
committerDmitry Stogov <dmitry@php.net>
Tue, 19 Dec 2006 11:54:38 +0000 (11:54 +0000)
NEWS
ext/soap/php_encoding.c
ext/soap/tests/bugs/bug39815.phpt [new file with mode: 0755]
ext/soap/tests/interop/Round3/GroupD/r3_groupD_compound2_001w.phpt
ext/standard/formatted_print.c
main/snprintf.c
main/snprintf.h
main/spprintf.c

diff --git a/NEWS b/NEWS
index 16bf5a31d1c655235433f852332ea625cb0ce606..8c80bb882f5a55607a1f7a464c5178c40fbcba80 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ PHP                                                                        NEWS
   when trying to open "php://wrong"). (Tony)
 - Fixed bug #39832 (SOAP Server: parameter not matching the WSDL specified type
   are set to 0). (Dmitry)
+- Fixed bug #39815 (SOAP double encoding is not locale-independent). (Dmitry)
 
 14 Dec 2006, PHP 5.2.1RC1
 - Added a meta tag to phpinfo() output to prevent search engines from indexing 
index b401d6d430fc10049156290bb551db80956855f7..0f0b281c25de32bcb42e0165a68806a2e0d1a232 100644 (file)
@@ -961,19 +961,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 (executable)
index 0000000..71fb407
--- /dev/null
@@ -0,0 +1,42 @@
+--TEST--
+Bug #39815 (to_zval_double() in ext/soap/php_encoding.c is not locale-independent)
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+function test(){
+  return 123.456;
+}
+class LocalSoapClient extends SoapClient {
+
+  function __construct($wsdl, $options) {
+    parent::__construct($wsdl, $options);
+    $this->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)
+<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://testuri.org" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:testResponse><return xsi:type="xsd:float">123.456</return></ns1:testResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
+float(123.456)
+<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://testuri.org" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:testResponse><return xsi:type="xsd:float">123.456</return></ns1:testResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
index 9aa448ce9e045002c4ae8563141ed2d8b1cacb16..4fca0b84d15281f91458fb2a18ee8019a42fb36b 100644 (file)
@@ -31,7 +31,7 @@ echo "ok\n";
 ?>
 --EXPECT--
 <?xml version="1.0" encoding="UTF-8"?>
-<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://soapinterop.org/person" xmlns:ns2="http://soapinterop.org/employee"><SOAP-ENV:Body><ns2:x_Employee><ns2:person><ns1:Name>Shane</ns1:Name><ns1:Male>true</ns1:Male></ns2:person><ns2:salary>1000000</ns2:salary><ns2:ID>12345</ns2:ID></ns2:x_Employee></SOAP-ENV:Body></SOAP-ENV:Envelope>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://soapinterop.org/person" xmlns:ns2="http://soapinterop.org/employee"><SOAP-ENV:Body><ns2:x_Employee><ns2:person><ns1:Name>Shane</ns1:Name><ns1:Male>true</ns1:Male></ns2:person><ns2:salary>1.0E+6</ns2:salary><ns2:ID>12345</ns2:ID></ns2:x_Employee></SOAP-ENV:Body></SOAP-ENV:Envelope>
 <?xml version="1.0" encoding="UTF-8"?>
-<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://soapinterop.org/person" xmlns:ns2="http://soapinterop.org/employee"><SOAP-ENV:Body><ns2:result_Employee><ns2:person><ns1:Name>Shane</ns1:Name><ns1:Male>true</ns1:Male></ns2:person><ns2:salary>1000000</ns2:salary><ns2:ID>12345</ns2:ID></ns2:result_Employee></SOAP-ENV:Body></SOAP-ENV:Envelope>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://soapinterop.org/person" xmlns:ns2="http://soapinterop.org/employee"><SOAP-ENV:Body><ns2:result_Employee><ns2:person><ns1:Name>Shane</ns1:Name><ns1:Male>true</ns1:Male></ns2:person><ns2:salary>1.0E+6</ns2:salary><ns2:ID>12345</ns2:ID></ns2:result_Employee></SOAP-ENV:Body></SOAP-ENV:Envelope>
 ok
index 0e28edd06413bed86ce3bff83fea6944d2d1781b..bdc9ddb3254d8855a74df8bbd0861f06a0cf20ce 100644 (file)
@@ -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;
        }
 
index 28ff0f3231978d04b5a3eaa15d3b08348dae9cfe..6f07022383df9082d9fe0a5cf90df5fb929199ac 100644 (file)
@@ -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 %<unknown> */
 
+       struct lconv *lconv = NULL;
+
        /*
         * Flag variables
         */
@@ -930,6 +921,7 @@ static int format_converter(register buffy * odp, const char *fmt,
 
                                
                                case 'f':
+                               case 'F':
                                case 'e':
                                case 'E':
                                        switch(modifier) {
@@ -950,8 +942,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 = '-';
@@ -998,7 +994,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)
@@ -1010,8 +1009,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;
 
 
index 2512ca79b3a46b548f4ad0b645367f56a8b6db01..0b158235cfafa00f69748b68c63f17167777ca8b 100644 (file)
@@ -65,10 +65,21 @@ 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 +97,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 +125,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 */
 
 /*
index 6e378a7842cae603340bf1741f739b9fd091a573..a0d499fdf2f29b5d61404a2746032de50df130b3 100644 (file)
 #include <inttypes.h>
 #endif
 
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
 #include "snprintf.h"
 
 #define FALSE           0
@@ -195,6 +199,8 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
        char num_buf[NUM_BUF_SIZE];
        char char_buf[2];                       /* for printing %% and %<unknown> */
 
+       struct lconv *lconv = NULL;
+
        /*
         * Flag variables
         */
@@ -527,6 +533,7 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
 
 
                                case 'f':
+                               case 'F':
                                case 'e':
                                case 'E':
                                        switch(modifier) {
@@ -547,8 +554,12 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
                                                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 = '-';
@@ -595,7 +606,10 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
                                        /*
                                         * * 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)
@@ -607,8 +621,6 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
 
                                        if (alternate_form && (q = strchr(s, '.')) == NULL)
                                                s[s_len++] = '.';
-                                       if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
-                                               *q = 'E';
                                        break;