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
5 changes: 3 additions & 2 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ PHP 8.7 UPGRADE NOTES
. Added IntlNumberFormatter class to format a number with a given locale
and ICU number skeleton, which also covers measurement units, e.g.
"measure-unit/speed-kilometer-per-hour unit-width-full-name".
IntlNumberFormatter::format() accepts an int, a float or a numeric
string, the latter being formatted as a decimal of arbitrary precision.
IntlNumberFormatter::format() accepts an int, a float or a decimal
string without exponent, the latter being formatted with arbitrary
precision.
It is supported from icu 63.

========================================
Expand Down
15 changes: 15 additions & 0 deletions ext/intl/numberformatter/numberformatter_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ U_CFUNC PHP_METHOD(IntlNumberFormatter, createFromSkeleton)
RETURN_THROWS();
}

const char *scale = zend_memnstr(skeleton, "scale/", sizeof("scale/") - 1, skeleton + skeleton_len);

if (scale) {
scale += sizeof("scale/") - 1;
scale += strspn(scale, "0123456789.+-");
if (UNEXPECTED(*scale == 'e' || *scale == 'E')) {
intl_error_set(nullptr, U_NUMBER_SKELETON_SYNTAX_ERROR, "Number formatter creation failed");
RETURN_NULL();
}
}

UErrorCode status = U_ZERO_ERROR;
UnicodeString uskeleton;

Expand Down Expand Up @@ -128,6 +139,10 @@ U_CFUNC PHP_METHOD(IntlNumberFormatter, format)
zend_argument_value_error(1, "is too long");
RETURN_THROWS();
}
if (UNEXPECTED(strpbrk(Z_STRVAL_P(value), "eE"))) {
intl_errors_set(INTL_DATA_ERROR_P(nfo), U_DECIMAL_NUMBER_SYNTAX_ERROR, "Number formatting failed");
RETURN_FALSE;
}
result = NUMBERFORMATTER_OBJECT(nfo)->formatDecimal(icu::StringPiece(Z_STRVAL_P(value), static_cast<int32_t>(Z_STRLEN_P(value))), status).toString(status);
} else if (Z_TYPE_P(value) == IS_DOUBLE) {
result = NUMBERFORMATTER_OBJECT(nfo)->formatDouble(Z_DVAL_P(value), status).toString(status);
Expand Down
6 changes: 3 additions & 3 deletions ext/intl/tests/numberformatter/format_string.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ $formatter = IntlNumberFormatter::createFromSkeleton('precision-unlimited', 'en_
var_dump($formatter->format('12345678901234567890.123456789'));
var_dump($formatter->format('9223372036854775808'));
var_dump($formatter->format('-0.1'));
var_dump($formatter->format('1e3'));

var_dump(IntlNumberFormatter::createFromSkeleton('', 'en_US')->format('12345678901234567890.123456789'));
var_dump(IntlNumberFormatter::createFromSkeleton('measure-unit/length-meter .00', 'de')->format('1234.5'));

foreach (['abc', '', ' 12', '1,5'] as $value) {
foreach (['abc', '', ' 12', '1,5', '1e3'] as $value) {
var_dump($formatter->format($value));
var_dump($formatter->getErrorMessage());
}
Expand All @@ -40,7 +39,6 @@ try {
string(36) "12,345,678,901,234,567,890.123456789"
string(25) "9,223,372,036,854,775,808"
string(4) "-0.1"
string(5) "1,000"
string(33) "12,345,678,901,234,567,890.123457"
string(10) "1.234,50 m"
bool(false)
Expand All @@ -51,6 +49,8 @@ bool(false)
string(86) "IntlNumberFormatter::format(): Number formatting failed: U_DECIMAL_NUMBER_SYNTAX_ERROR"
bool(false)
string(86) "IntlNumberFormatter::format(): Number formatting failed: U_DECIMAL_NUMBER_SYNTAX_ERROR"
bool(false)
string(86) "IntlNumberFormatter::format(): Number formatting failed: U_DECIMAL_NUMBER_SYNTAX_ERROR"
string(2) "12"
int(0)
ValueError: IntlNumberFormatter::format(): Argument #1 ($value) must not contain any null bytes
42 changes: 42 additions & 0 deletions ext/intl/tests/numberformatter/format_string_exponent.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
IntlNumberFormatter::format() rejects numeric strings in exponent notation
--EXTENSIONS--
intl
--SKIPIF--
<?php
if (!class_exists('IntlNumberFormatter')) {
die('skip IntlNumberFormatter not available');
}
?>
--INI--
memory_limit=16M
--FILE--
<?php

$formatter = IntlNumberFormatter::createFromSkeleton('precision-unlimited', 'en_US');

var_dump(strlen($formatter->format('1' . str_repeat('0', 999))));

foreach (['1e3', '1E-3', '1e999999999'] as $value) {
var_dump($formatter->format($value));
var_dump($formatter->getErrorMessage());
}

ini_set('intl.use_exceptions', '1');

try {
$formatter->format('1e999999999');
} catch (IntlException $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
int(1333)
bool(false)
string(86) "IntlNumberFormatter::format(): Number formatting failed: U_DECIMAL_NUMBER_SYNTAX_ERROR"
bool(false)
string(86) "IntlNumberFormatter::format(): Number formatting failed: U_DECIMAL_NUMBER_SYNTAX_ERROR"
bool(false)
string(86) "IntlNumberFormatter::format(): Number formatting failed: U_DECIMAL_NUMBER_SYNTAX_ERROR"
IntlException: IntlNumberFormatter::format(): Number formatting failed
42 changes: 42 additions & 0 deletions ext/intl/tests/numberformatter/skeleton_scale_exponent.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
IntlNumberFormatter::createFromSkeleton() rejects a scale option in exponent notation
--EXTENSIONS--
intl
--SKIPIF--
<?php
if (!class_exists('IntlNumberFormatter')) {
die('skip IntlNumberFormatter not available');
}
?>
--INI--
memory_limit=16M
--FILE--
<?php

var_dump(IntlNumberFormatter::createFromSkeleton('precision-unlimited scale/1000', 'en_US')->format(1));
var_dump(IntlNumberFormatter::createFromSkeleton('scale/0.5 percent', 'en_US')->format(1));

foreach (['precision-unlimited scale/1e3000000', 'precision-unlimited scale/1E-3000000', 'scale/1.5e3'] as $skeleton) {
var_dump(IntlNumberFormatter::createFromSkeleton($skeleton, 'en_US'));
var_dump(intl_get_error_message());
}

ini_set('intl.use_exceptions', '1');

try {
IntlNumberFormatter::createFromSkeleton('precision-unlimited scale/1e3000000', 'en_US');
} catch (IntlException $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
string(5) "1,000"
string(4) "0.5%"
NULL
string(107) "IntlNumberFormatter::createFromSkeleton(): Number formatter creation failed: U_NUMBER_SKELETON_SYNTAX_ERROR"
NULL
string(107) "IntlNumberFormatter::createFromSkeleton(): Number formatter creation failed: U_NUMBER_SKELETON_SYNTAX_ERROR"
NULL
string(107) "IntlNumberFormatter::createFromSkeleton(): Number formatter creation failed: U_NUMBER_SKELETON_SYNTAX_ERROR"
IntlException: IntlNumberFormatter::createFromSkeleton(): Number formatter creation failed
Loading