Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ PHP 8.0 UPGRADE NOTES
array, resource or non-overloaded object. The only exception to this is the
array + array merge operation, which remains supported.
RFC: https://wiki.php.net/rfc/arithmetic_operator_type_checks
. Float to string casting will now always behave locale-independently.
RFC: https://wiki.php.net/rfc/locale_independent_float_to_string

- COM:
. Removed the ability to import case-insensitive constants from type
Expand Down
20 changes: 3 additions & 17 deletions Zend/zend_operators.c
Original file line number Diff line number Diff line change
Expand Up @@ -605,20 +605,6 @@ ZEND_API void ZEND_FASTCALL convert_to_boolean(zval *op) /* {{{ */
}
/* }}} */

ZEND_API void ZEND_FASTCALL _convert_to_cstring(zval *op) /* {{{ */
{
if (Z_TYPE_P(op) == IS_DOUBLE) {
zend_string *str;
double dval = Z_DVAL_P(op);

str = zend_strpprintf_unchecked(0, "%.*H", (int) EG(precision), dval);
ZVAL_NEW_STR(op, str);
} else {
_convert_to_string(op);
}
}
/* }}} */

ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op) /* {{{ */
{
try_again:
Expand Down Expand Up @@ -648,8 +634,8 @@ ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op) /* {{{ */
zend_string *str;
double dval = Z_DVAL_P(op);

str = zend_strpprintf(0, "%.*G", (int) EG(precision), dval);
/* %G already handles removing trailing zeros from the fractional part, yay */
str = zend_strpprintf_unchecked(0, "%.*H", (int) EG(precision), dval);

ZVAL_NEW_STR(op, str);
break;
}
Expand Down Expand Up @@ -953,7 +939,7 @@ static zend_always_inline zend_string* __zval_get_string_func(zval *op, zend_boo
return zend_long_to_str(Z_LVAL_P(op));
}
case IS_DOUBLE: {
return zend_strpprintf(0, "%.*G", (int) EG(precision), Z_DVAL_P(op));
return zend_strpprintf_unchecked(0, "%.*H", (int) EG(precision), Z_DVAL_P(op));
}
case IS_ARRAY:
zend_error(E_WARNING, "Array to string conversion");
Expand Down
2 changes: 0 additions & 2 deletions Zend/zend_operators.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,6 @@ ZEND_API int ZEND_FASTCALL increment_function(zval *op1);
ZEND_API int ZEND_FASTCALL decrement_function(zval *op2);

ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op);
ZEND_API void ZEND_FASTCALL _convert_to_cstring(zval *op);
ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op);
ZEND_API void ZEND_FASTCALL convert_to_long(zval *op);
ZEND_API void ZEND_FASTCALL convert_to_double(zval *op);
Expand Down Expand Up @@ -340,7 +339,6 @@ static zend_always_inline zend_bool try_convert_to_string(zval *op) {
#define _zval_get_double_func(op) zval_get_double_func(op)
#define _zval_get_string_func(op) zval_get_string_func(op)

#define convert_to_cstring(op) if (Z_TYPE_P(op) != IS_STRING) { _convert_to_cstring((op)); }
#define convert_to_string(op) if (Z_TYPE_P(op) != IS_STRING) { _convert_to_string((op)); }


Expand Down
2 changes: 1 addition & 1 deletion ext/intl/tests/bug67052-win32.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ ut_run();

?>
--EXPECT--
1234567,891
1234567.891
de-de
2 changes: 1 addition & 1 deletion ext/intl/tests/bug67052.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ ut_run();

?>
--EXPECT--
1234567,891
1234567.891
de_DE.UTF-8
6 changes: 3 additions & 3 deletions ext/json/tests/bug41403.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ echo "Done\n";
--EXPECT--
array(1) {
[0]=>
float(2,1)
float(2.1)
}
array(1) {
[0]=>
float(0,15)
float(0.15)
}
array(1) {
[0]=>
float(123,13452345)
float(123.13452345)
}
array(2) {
[0]=>
Expand Down
11 changes: 2 additions & 9 deletions ext/pdo/pdo_stmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,15 +230,8 @@ static int really_register_bound_param(struct pdo_bound_param_data *param, pdo_s
}

if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR && param->max_value_len <= 0 && !Z_ISNULL_P(parameter)) {
if (Z_TYPE_P(parameter) == IS_DOUBLE) {
char *p;
int len = zend_spprintf_unchecked(&p, 0, "%.*H", (int) EG(precision), Z_DVAL_P(parameter));
ZVAL_STRINGL(parameter, p, len);
efree(p);
} else {
if (!try_convert_to_string(parameter)) {
return 0;
}
if (!try_convert_to_string(parameter)) {
return 0;
}
} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT && (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE)) {
convert_to_long(parameter);
Expand Down
2 changes: 1 addition & 1 deletion ext/pgsql/pgsql.c
Original file line number Diff line number Diff line change
Expand Up @@ -1987,7 +1987,7 @@ PHP_FUNCTION(pg_query_params)
zval tmp_val;

ZVAL_COPY(&tmp_val, tmp);
convert_to_cstring(&tmp_val);
convert_to_string(&tmp_val);
if (Z_TYPE(tmp_val) != IS_STRING) {
php_error_docref(NULL, E_WARNING,"Error converting parameter");
zval_ptr_dtor(&tmp_val);
Expand Down
2 changes: 1 addition & 1 deletion ext/soap/tests/bugs/bug39815.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ setlocale(LC_ALL,"en_US","en_US.ISO8859-1");
var_dump($x->test());
echo $x->__getLastResponse();
--EXPECT--
float(123,456)
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)
Expand Down
107 changes: 107 additions & 0 deletions ext/standard/tests/strings/locale_independent_float_to_string.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
--TEST--
Test that floats are converted to string locale independently
--SKIPIF--
<?php

if (!setlocale
(LC_ALL,
"german", "de", "de_DE", "de_DE.ISO8859-1", "de_DE.ISO_8859-1", "de_DE.UTF-8",
"french", "fr", "fr_FR", "fr_FR.ISO8859-1", "fr_FR.ISO_8859-1", "fr_FR.UTF-8",
)) {
die("skip - locale needed for this test is not supported on this platform");
}

if (!extension_loaded("json")) {
print "skip - test requires the json extension";
}

?>
--FILE--
<?php

function print_float(float $f)
{
echo "- casting:\n";
echo $f . "\n";
echo strval($f) . "\n";
$g = $f;
settype($g, "string");
echo $g . "\n";

echo "- *printf functions:\n";
printf("%.2f\n", $f);
printf("%.2F\n", $f);
echo sprintf("%.2f", $f) . "\n";
echo sprintf("%.2F", $f) . "\n";

echo "- export/import:\n";
echo var_export($f, true) . "\n";
echo serialize($f) . "\n";
echo json_encode($f) . "\n";

echo "- debugging:\n";
echo print_r($f, true) . "\n";
var_dump($f);
debug_zval_dump($f);

echo "- other:\n";
echo implode([$f]) . "\n";
}

setlocale(LC_ALL, "C");
echo "C locale:\n";

print_float(3.14);

setlocale(
LC_ALL,
"german", "de", "de_DE", "de_DE.ISO8859-1", "de_DE.ISO_8859-1", "de_DE.UTF-8",
"french", "fr", "fr_FR", "fr_FR.ISO8859-1", "fr_FR.ISO_8859-1", "fr_FR.UTF-8",
);
echo "\nde_DE locale:\n";

print_float(3.14);

?>
--EXPECT--
C locale:
- casting:
3.14
3.14
3.14
- *printf functions:
3.14
3.14
3.14
3.14
- export/import:
3.14
d:3.14;
3.14
- debugging:
3.14
float(3.14)
float(3.14)
- other:
3.14

de_DE locale:
- casting:
3.14
3.14
3.14
- *printf functions:
3,14
3.14
3,14
3.14
- export/import:
3.14
d:3.14;
3.14
- debugging:
3.14
float(3.14)
float(3.14)
- other:
3.14
4 changes: 2 additions & 2 deletions ext/standard/var.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */
php_printf("%sint(" ZEND_LONG_FMT ")\n", COMMON, Z_LVAL_P(struc));
break;
case IS_DOUBLE:
php_printf("%sfloat(%.*G)\n", COMMON, (int) PG(serialize_precision), Z_DVAL_P(struc));
php_printf_unchecked("%sfloat(%.*H)\n", COMMON, (int) PG(serialize_precision), Z_DVAL_P(struc));
break;
case IS_STRING:
php_printf("%sstring(%zd) \"", COMMON, Z_STRLEN_P(struc));
Expand Down Expand Up @@ -295,7 +295,7 @@ PHPAPI void php_debug_zval_dump(zval *struc, int level) /* {{{ */
php_printf("%sint(" ZEND_LONG_FMT ")\n", COMMON, Z_LVAL_P(struc));
break;
case IS_DOUBLE:
php_printf("%sfloat(%.*G)\n", COMMON, (int) PG(serialize_precision), Z_DVAL_P(struc));
php_printf_unchecked("%sfloat(%.*H)\n", COMMON, (int) PG(serialize_precision), Z_DVAL_P(struc));
break;
case IS_STRING:
php_printf("%sstring(%zd) \"", COMMON, Z_STRLEN_P(struc));
Expand Down
19 changes: 19 additions & 0 deletions main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,25 @@ PHPAPI size_t php_printf(const char *format, ...)
}
/* }}} */

/* {{{ php_printf_unchecked
*/
PHPAPI size_t php_printf_unchecked(const char *format, ...)
{
va_list args;
size_t ret;
char *buffer;
size_t size;

va_start(args, format);
size = vspprintf(&buffer, 0, format, args);
ret = PHPWRITE(buffer, size);
efree(buffer);
va_end(args);

return ret;
}
/* }}} */

static zend_string *escape_html(const char *buffer, size_t buffer_len) {
zend_string *result = php_escape_html_entities_ex(
(const unsigned char *) buffer, buffer_len, 0, ENT_COMPAT,
Expand Down
4 changes: 2 additions & 2 deletions main/php.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,8 @@ ssize_t pread(int, void *, size_t, off64_t);
BEGIN_EXTERN_C()
void phperror(char *error);
PHPAPI size_t php_write(void *buf, size_t size);
PHPAPI size_t php_printf(const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 1,
2);
PHPAPI size_t php_printf(const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 1, 2);
PHPAPI size_t php_printf_unchecked(const char *format, ...);
PHPAPI int php_get_module_initialized(void);
#ifdef HAVE_SYSLOG_H
#include "php_syslog.h"
Expand Down
30 changes: 30 additions & 0 deletions tests/basic/consistent_float_string_casts.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--TEST--
Test that float to string and string to float casts are consistent
--SKIPIF--
<?php
if (!setlocale(
LC_ALL,
"german", "de", "de_DE", "de_DE.ISO8859-1", "de_DE.ISO_8859-1", "de_DE.UTF-8",
"french", "fr", "fr_FR", "fr_FR.ISO8859-1", "fr_FR.ISO_8859-1", "fr_FR.UTF-8",
)) {
die("skip locale needed for this test is not supported on this platform");
}
?>
--FILE--
<?php

setlocale(
LC_ALL,
"german", "de", "de_DE", "de_DE.ISO8859-1", "de_DE.ISO_8859-1", "de_DE.UTF-8",
"french", "fr", "fr_FR", "fr_FR.ISO8859-1", "fr_FR.ISO_8859-1", "fr_FR.UTF-8",
);

$float = 1/3;
$string = (string) $float;
$float = (float) $string;

printf("%.2f", $float);

?>
--EXPECT--
0,33
2 changes: 1 addition & 1 deletion tests/lang/034.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ setlocale(LC_NUMERIC, "de_DE.UTF-8", "de_DE", "de", "german", "ge", "de_DE.ISO-8
echo (float)"3.14", "\n";
?>
--EXPECT--
3,14
3.14