php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #39815 to_zval_double() in ext/soap/php_encoding.c is not locale-independent
Submitted: 2006-12-13 13:15 UTC Modified: 2006-12-19 12:01 UTC
Votes:2
Avg. Score:4.0 ± 0.0
Reproduced:2 of 2 (100.0%)
Same Version:1 (50.0%)
Same OS:1 (50.0%)
From: fredrik at wangel dot net Assigned: dmitry (profile)
Status: Closed Package: SOAP related
PHP Version: 6CVS-2006-12-13 (snap) OS: Irrelevant
Private report: No CVE-ID: None
 [2006-12-13 13:15 UTC] fredrik at wangel dot net
Description:
------------
[Note: This is an inherent problem within a PHP extension's C code, not with a PHP script]

Problem: The SOAPClient/SOAPServer converts xsd:float values in a locale-dependent fashion, causing serious issues when the caller's locale is not English.

According to "XML Schema Part 2: Datatypes", a decimal (either xsd:float or xsd:double) is always written using a period (.) as a decimal indicator [See http://www.w3.org/TR/xmlschema-2/#decimal]

However, SOAPServer writes decimals using the current locale's (LC_NUMERIC) formatting rules, which is a violation of the standard. Likewise, on the client side, the function to_zval_double() in ext/soap/php_encoding.c uses atof() which parses a string according to the current locale. Since the server and client can have totally different settings for locale, the conversion fails.

Hence, if the SOAP server's locale is set to "sv_SE" (Swedish) or some other language that does not use period as decimal indicator, and the SOAP client is in English, the resulting float is truncated into an integer value.

Suggested fix is to either either temporarily change LC_NUMERIC [via setlocale()] into "POSIX" (or "C"), or use zend_strtod() for conversion from XML. When converting to XML [using convert_to_string()] the simplest solution should be to change LC_NUMERIC temporarily, since zend_sprintf() also uses current locale settings.

PLEASE NOTE: If you do not have the locales from the example (sv_SE and en_US) on your test server, this will likely fallback into C locale, and thus the result always looks good. Verify that your test server formats 123.456 into "123,456". Try "se" or "Swedish" if "sv_SE" fails.

Reproduce code:
---------------
-- SERVER --
class Server {function getValue(){return (float)123.456;}}
setlocale(LC_ALL,"sv_SE");
$server = new SoapServer(null,array('uri'=>'http://localhost/', 'soap_version'=>SOAP_1_2, 'encoding'=>'ISO-8859-1'));
$server->setClass("Server");
$server->handle();

--- CLIENT --
$client = new SoapClient(null,array('location'=>'http://localhost/soap/Server.php', 'uri'=>'http://localhost/', 'soap_version'=>SOAP_1_2, 'encoding'=>'ISO-8859-1'));
header("Content-Type: text/plain; charset=ISO-8859-1");
setlocale(LC_ALL,"sv_SE");
print((float)123.456 . "\n");
setlocale(LC_ALL,"en_US");
$value = $client->getValue();
var_dump($value);


Expected result:
----------------
123,456
float(123.456)

Actual result:
--------------
123,456
float(123)

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2006-12-19 12:01 UTC] dmitry@php.net
Fixed in CVS HEAD and PHP_5_2.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 08:01:29 2024 UTC