Skip to content

Commit c43fc2a

Browse files
committed
Fixed bug #39815 (SOAP double encoding is not locale-independent)
1 parent 39a7719 commit c43fc2a

File tree

8 files changed

+110
-56
lines changed

8 files changed

+110
-56
lines changed

NEWS

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ PHP NEWS
1414
when trying to open "php://wrong"). (Tony)
1515
- Fixed bug #39832 (SOAP Server: parameter not matching the WSDL specified type
1616
are set to 0). (Dmitry)
17+
- Fixed bug #39815 (SOAP double encoding is not locale-independent). (Dmitry)
1718

1819
14 Dec 2006, PHP 5.2.1RC1
1920
- Added a meta tag to phpinfo() output to prevent search engines from indexing

ext/soap/php_encoding.c

+8-4
Original file line numberDiff line numberDiff line change
@@ -961,19 +961,23 @@ static xmlNodePtr to_xml_double(encodeTypePtr type, zval *data, int style, xmlNo
961961
{
962962
xmlNodePtr ret;
963963
zval tmp;
964+
char *str;
965+
TSRMLS_FETCH();
964966

965967
ret = xmlNewNode(NULL, BAD_CAST("BOGUS"));
966968
xmlAddChild(parent, ret);
967969
FIND_ZVAL_NULL(data, ret, style);
968970

969971
tmp = *data;
970-
zval_copy_ctor(&tmp);
971972
if (Z_TYPE(tmp) != IS_DOUBLE) {
973+
zval_copy_ctor(&tmp);
972974
convert_to_double(&tmp);
973975
}
974-
convert_to_string(&tmp);
975-
xmlNodeSetContentLen(ret, BAD_CAST(Z_STRVAL(tmp)), Z_STRLEN(tmp));
976-
zval_dtor(&tmp);
976+
977+
str = (char *) emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
978+
php_gcvt(Z_DVAL(tmp), EG(precision), '.', 'E', str);
979+
xmlNodeSetContentLen(ret, BAD_CAST(str), strlen(str));
980+
efree(str);
977981

978982
if (style == SOAP_ENCODED) {
979983
set_ns_and_type(ret, type);

ext/soap/tests/bugs/bug39815.phpt

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
Bug #39815 (to_zval_double() in ext/soap/php_encoding.c is not locale-independent)
3+
--SKIPIF--
4+
<?php require_once('skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
function test(){
8+
return 123.456;
9+
}
10+
class LocalSoapClient extends SoapClient {
11+
12+
function __construct($wsdl, $options) {
13+
parent::__construct($wsdl, $options);
14+
$this->server = new SoapServer($wsdl, $options);
15+
$this->server->addFunction('test');
16+
}
17+
18+
function __doRequest($request, $location, $action, $version) {
19+
ob_start();
20+
$this->server->handle($request);
21+
$response = ob_get_contents();
22+
ob_end_clean();
23+
return $response;
24+
}
25+
26+
}
27+
$x = new LocalSoapClient(NULL,array('location'=>'test://',
28+
'uri'=>'http://testuri.org',
29+
"trace"=>1));
30+
setlocale(LC_ALL,"sv_SE");
31+
var_dump($x->test());
32+
echo $x->__getLastResponse();
33+
setlocale(LC_ALL,"en_US");
34+
var_dump($x->test());
35+
echo $x->__getLastResponse();
36+
--EXPECT--
37+
float(123,456)
38+
<?xml version="1.0" encoding="UTF-8"?>
39+
<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>
40+
float(123.456)
41+
<?xml version="1.0" encoding="UTF-8"?>
42+
<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>

ext/soap/tests/interop/Round3/GroupD/r3_groupD_compound2_001w.phpt

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ echo "ok\n";
3131
?>
3232
--EXPECT--
3333
<?xml version="1.0" encoding="UTF-8"?>
34-
<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>
34+
<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>
3535
<?xml version="1.0" encoding="UTF-8"?>
36-
<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>
36+
<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>
3737
ok

ext/standard/formatted_print.c

+7-7
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,9 @@ php_sprintf_appenddouble(char **buffer, int *pos,
196196
TSRMLS_DC)
197197
{
198198
char num_buf[NUM_BUF_SIZE];
199-
char *s = NULL, *q;
199+
char *s = NULL;
200200
int s_len = 0, is_negative = 0;
201+
struct lconv *lconv;
201202

202203
PRINTF_DEBUG(("sprintf: appenddouble(%x, %x, %x, %f, %d, '%c', %d, %c)\n",
203204
*buffer, pos, size, number, width, padding, alignment, fmt));
@@ -229,7 +230,9 @@ php_sprintf_appenddouble(char **buffer, int *pos,
229230
case 'E':
230231
case 'f':
231232
case 'F':
232-
s = ap_php_conv_fp(fmt, number, 0, precision,
233+
lconv = localeconv();
234+
s = php_conv_fp((fmt == 'f')?'F':fmt, number, 0, precision,
235+
(fmt == 'f')?(*lconv->decimal_point):'.',
233236
&is_negative, &num_buf[1], &s_len);
234237
if (is_negative) {
235238
num_buf[0] = '-';
@@ -249,7 +252,8 @@ php_sprintf_appenddouble(char **buffer, int *pos,
249252
/*
250253
* * We use &num_buf[ 1 ], so that we have room for the sign
251254
*/
252-
s = bsd_gcvt(number, precision, &num_buf[1]);
255+
lconv = localeconv();
256+
s = php_gcvt(number, precision, *lconv->decimal_point, (fmt == 'G')?'E':'e', &num_buf[1]);
253257
is_negative = 0;
254258
if (*s == '-') {
255259
is_negative = 1;
@@ -260,10 +264,6 @@ php_sprintf_appenddouble(char **buffer, int *pos,
260264
}
261265

262266
s_len = strlen(s);
263-
264-
if (fmt == 'G' && (q = strchr(s, 'e')) != NULL) {
265-
*q = 'E';
266-
}
267267
break;
268268
}
269269

main/snprintf.c

+23-26
Original file line numberDiff line numberDiff line change
@@ -115,23 +115,20 @@ static char * __cvt(double value, int ndigit, int *decpt, int *sign, int fmode,
115115
return(s);
116116
}
117117

118-
char *bsd_ecvt(double value, int ndigit, int *decpt, int *sign)
118+
static inline char *php_ecvt(double value, int ndigit, int *decpt, int *sign)
119119
{
120120
return(__cvt(value, ndigit, decpt, sign, 0, 1));
121121
}
122122

123-
char *bsd_fcvt(double value, int ndigit, int *decpt, int *sign)
123+
static inline char *php_fcvt(double value, int ndigit, int *decpt, int *sign)
124124
{
125125
return(__cvt(value, ndigit, decpt, sign, 1, 1));
126126
}
127127

128-
char *bsd_gcvt(double value, int ndigit, char *buf)
128+
PHPAPI char *php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf)
129129
{
130130
char *digits, *dst, *src;
131131
int i, decpt, sign;
132-
struct lconv *lconv;
133-
134-
lconv = localeconv();
135132

136133
digits = zend_dtoa(value, 2, ndigit, &decpt, &sign, NULL);
137134
if (decpt == 9999) {
@@ -161,15 +158,15 @@ char *bsd_gcvt(double value, int ndigit, char *buf)
161158
sign = 0;
162159
src = digits;
163160
*dst++ = *src++;
164-
*dst++ = *lconv->decimal_point;
161+
*dst++ = dec_point;
165162
if (*src == '\0') {
166163
*dst++ = '0';
167164
} else {
168165
do {
169166
*dst++ = *src++;
170167
} while (*src != '\0');
171168
}
172-
*dst++ = 'e';
169+
*dst++ = exponent;
173170
if (sign)
174171
*dst++ = '-';
175172
else
@@ -190,7 +187,7 @@ char *bsd_gcvt(double value, int ndigit, char *buf)
190187
} else if (decpt < 0) {
191188
/* standard format 0. */
192189
*dst++ = '0'; /* zero before decimal point */
193-
*dst++ = *lconv->decimal_point;
190+
*dst++ = dec_point;
194191
do {
195192
*dst++ = '0';
196193
} while (++decpt < 0);
@@ -210,7 +207,7 @@ char *bsd_gcvt(double value, int ndigit, char *buf)
210207
if (*src != '\0') {
211208
if (src == digits)
212209
*dst++ = '0'; /* zero before decimal point */
213-
*dst++ = *lconv->decimal_point;
210+
*dst++ = dec_point;
214211
for (i = decpt; digits[i] != '\0'; i++) {
215212
*dst++ = digits[i];
216213
}
@@ -357,29 +354,21 @@ char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned,
357354
* The sign is returned in the is_negative argument (and is not placed
358355
* in buf).
359356
*/
360-
char * ap_php_conv_fp(register char format, register double num,
361-
boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len)
357+
PHPAPI char * php_conv_fp(register char format, register double num,
358+
boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, int *len)
362359
{
363360
register char *s = buf;
364361
register char *p, *p_orig;
365362
int decimal_point;
366-
char dec_point = '.';
367-
368-
if (format == 'f') {
369-
struct lconv *lconv;
370-
lconv = localeconv();
371-
dec_point = *lconv->decimal_point;
372-
format = 'F';
373-
}
374363

375364
if (precision >= NDIG - 1) {
376365
precision = NDIG - 2;
377366
}
378367

379368
if (format == 'F')
380-
p_orig = p = bsd_fcvt(num, precision, &decimal_point, is_negative);
369+
p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
381370
else /* either e or E format */
382-
p_orig = p = bsd_ecvt(num, precision + 1, &decimal_point, is_negative);
371+
p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative);
383372

384373
/*
385374
* Check for Infinity and NaN
@@ -595,6 +584,8 @@ static int format_converter(register buffy * odp, const char *fmt,
595584
char num_buf[NUM_BUF_SIZE];
596585
char char_buf[2]; /* for printing %% and %<unknown> */
597586

587+
struct lconv *lconv = NULL;
588+
598589
/*
599590
* Flag variables
600591
*/
@@ -930,6 +921,7 @@ static int format_converter(register buffy * odp, const char *fmt,
930921

931922

932923
case 'f':
924+
case 'F':
933925
case 'e':
934926
case 'E':
935927
switch(modifier) {
@@ -950,8 +942,12 @@ static int format_converter(register buffy * odp, const char *fmt,
950942
s = "inf";
951943
s_len = 3;
952944
} else {
953-
s = ap_php_conv_fp(*fmt, fp_num, alternate_form,
945+
if (!lconv) {
946+
lconv = localeconv();
947+
}
948+
s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
954949
(adjust_precision == NO) ? FLOAT_DIGITS : precision,
950+
(*fmt == 'f')?(*lconv->decimal_point):'.',
955951
&is_negative, &num_buf[1], &s_len);
956952
if (is_negative)
957953
prefix_char = '-';
@@ -998,7 +994,10 @@ static int format_converter(register buffy * odp, const char *fmt,
998994
/*
999995
* * We use &num_buf[ 1 ], so that we have room for the sign
1000996
*/
1001-
s = bsd_gcvt(fp_num, precision, &num_buf[1]);
997+
if (!lconv) {
998+
lconv = localeconv();
999+
}
1000+
s = php_gcvt(fp_num, precision, *lconv->decimal_point, (*fmt == 'G')?'E':'e', &num_buf[1]);
10021001
if (*s == '-')
10031002
prefix_char = *s++;
10041003
else if (print_sign)
@@ -1010,8 +1009,6 @@ static int format_converter(register buffy * odp, const char *fmt,
10101009

10111010
if (alternate_form && (q = strchr(s, '.')) == NULL)
10121011
s[s_len++] = '.';
1013-
if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
1014-
*q = 'E';
10151012
break;
10161013

10171014

main/snprintf.h

+11-13
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,21 @@ spprintf is the dynamical version of snprintf. It allocates the buffer in size
6565
#ifndef SNPRINTF_H
6666
#define SNPRINTF_H
6767

68+
typedef int bool_int;
69+
70+
typedef enum {
71+
NO = 0, YES = 1
72+
} boolean_e;
73+
74+
6875
BEGIN_EXTERN_C()
6976
PHPAPI int ap_php_snprintf(char *, size_t, const char *, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4);
7077
PHPAPI int ap_php_vsnprintf(char *, size_t, const char *, va_list ap) PHP_ATTRIBUTE_FORMAT(printf, 3, 0);
7178
PHPAPI int php_sprintf (char* s, const char* format, ...) PHP_ATTRIBUTE_FORMAT(printf, 2, 3);
79+
PHPAPI char * php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf);
80+
PHPAPI char * php_conv_fp(register char format, register double num,
81+
boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, int *len);
82+
7283
END_EXTERN_C()
7384

7485
#ifdef snprintf
@@ -86,10 +97,6 @@ END_EXTERN_C()
8697
#endif
8798
#define sprintf php_sprintf
8899

89-
typedef enum {
90-
NO = 0, YES = 1
91-
} boolean_e;
92-
93100
typedef enum {
94101
LM_STD = 0,
95102
#if SIZEOF_INTMAX_T
@@ -118,21 +125,12 @@ typedef enum {
118125
typedef WIDE_INT wide_int;
119126
typedef unsigned WIDE_INT u_wide_int;
120127

121-
typedef int bool_int;
122-
123128
extern char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned,
124129
register bool_int * is_negative, char *buf_end, register int *len);
125130

126-
extern char * ap_php_conv_fp(register char format, register double num,
127-
boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len);
128-
129131
extern char * ap_php_conv_p2(register u_wide_int num, register int nbits,
130132
char format, char *buf_end, register int *len);
131133

132-
extern char * bsd_ecvt(double value, int ndigit, int *decpt, int *sign);
133-
extern char * bsd_fcvt(double value, int ndigit, int *decpt, int *sign);
134-
extern char * bsd_gcvt(double value, int ndigit, char *buf);
135-
136134
#endif /* SNPRINTF_H */
137135

138136
/*

main/spprintf.c

+16-4
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@
9090
#include <inttypes.h>
9191
#endif
9292

93+
#ifdef HAVE_LOCALE_H
94+
#include <locale.h>
95+
#endif
96+
9397
#include "snprintf.h"
9498

9599
#define FALSE 0
@@ -195,6 +199,8 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
195199
char num_buf[NUM_BUF_SIZE];
196200
char char_buf[2]; /* for printing %% and %<unknown> */
197201

202+
struct lconv *lconv = NULL;
203+
198204
/*
199205
* Flag variables
200206
*/
@@ -527,6 +533,7 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
527533

528534

529535
case 'f':
536+
case 'F':
530537
case 'e':
531538
case 'E':
532539
switch(modifier) {
@@ -547,8 +554,12 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
547554
s = "inf";
548555
s_len = 3;
549556
} else {
550-
s = ap_php_conv_fp(*fmt, fp_num, alternate_form,
557+
if (!lconv) {
558+
lconv = localeconv();
559+
}
560+
s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
551561
(adjust_precision == NO) ? FLOAT_DIGITS : precision,
562+
(*fmt == 'f')?(*lconv->decimal_point):'.',
552563
&is_negative, &num_buf[1], &s_len);
553564
if (is_negative)
554565
prefix_char = '-';
@@ -595,7 +606,10 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
595606
/*
596607
* * We use &num_buf[ 1 ], so that we have room for the sign
597608
*/
598-
s = bsd_gcvt(fp_num, precision, &num_buf[1]);
609+
if (!lconv) {
610+
lconv = localeconv();
611+
}
612+
s = php_gcvt(fp_num, precision, *lconv->decimal_point, (*fmt == 'G')?'E':'e', &num_buf[1]);
599613
if (*s == '-')
600614
prefix_char = *s++;
601615
else if (print_sign)
@@ -607,8 +621,6 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
607621

608622
if (alternate_form && (q = strchr(s, '.')) == NULL)
609623
s[s_len++] = '.';
610-
if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
611-
*q = 'E';
612624
break;
613625

614626

0 commit comments

Comments
 (0)