Commit 3ed80a154d5 for php.net
commit 3ed80a154d589e3ea1b02f43fa1899ea9c46d70f
Author: David CARLIER <devnexen@gmail.com>
Date: Sat May 16 14:01:47 2026 +0100
ext/intl: Migrate formatter, listformatter, and rangeformatter from I… (#21378)
- formatter: Replace intl_convert_utf8_to_utf16/uloc_getISO3Language with
intl_stringFromChar/icu::Locale, use UnicodeString for pattern handling
- listformatter: Replace ulistfmt_open/ulistfmt_openForType/ulistfmt_close/
ulistfmt_format with ListFormatter C++ equivalents, use UnicodeString array
and intl_stringFromChar/intl_charFromString for string conversions
- rangeformatter: Replace uloc_getISO3Language with icu::Locale::getISO3Language
- Replace UNumberFormat*/unum_* with icu::NumberFormat C++ equivalents.
- Attribute get/set still use reinterpret_cast<UNumberFormat*> for now.
diff --git a/ext/intl/formatter/formatter_attr.cpp b/ext/intl/formatter/formatter_attr.cpp
index 01108351a5d..d21873ecdab 100644
--- a/ext/intl/formatter/formatter_attr.cpp
+++ b/ext/intl/formatter/formatter_attr.cpp
@@ -16,13 +16,16 @@
#include <config.h>
#endif
+#include <unicode/unum.h>
+#include "../intl_convertcpp.h"
+#include "formatter_class.h"
+
extern "C" {
#include "php_intl.h"
#include "intl_convert.h"
}
-#include "formatter_class.h"
-#include <unicode/ustring.h>
+#define FORMATTER_UNUM(nfo) reinterpret_cast<UNumberFormat*>(FORMATTER_OBJECT(nfo))
/* {{{ Get formatter attribute value. */
U_CFUNC PHP_FUNCTION( numfmt_get_attribute )
@@ -62,7 +65,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_attribute )
case UNUM_MIN_SIGNIFICANT_DIGITS:
case UNUM_MAX_SIGNIFICANT_DIGITS:
case UNUM_LENIENT_PARSE:
- value = unum_getAttribute(FORMATTER_OBJECT(nfo), attribute);
+ value = unum_getAttribute(FORMATTER_UNUM(nfo), attribute);
if(value == -1) {
INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
} else {
@@ -71,7 +74,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_attribute )
break;
case UNUM_ROUNDING_INCREMENT:
{
- double value_double = unum_getDoubleAttribute(FORMATTER_OBJECT(nfo), attribute);
+ double value_double = unum_getDoubleAttribute(FORMATTER_UNUM(nfo), attribute);
if(value_double == -1) {
INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
} else {
@@ -110,12 +113,12 @@ U_CFUNC PHP_FUNCTION( numfmt_get_text_attribute )
UNumberFormatTextAttribute attribute = static_cast<UNumberFormatTextAttribute>(lattribute);
- length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, value_buf_size, &INTL_DATA_ERROR_CODE(nfo) );
+ length = unum_getTextAttribute( FORMATTER_UNUM(nfo), attribute, value, value_buf_size, &INTL_DATA_ERROR_CODE(nfo) );
if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= value_buf_size) {
++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
value = eumalloc(length);
- length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, length, &INTL_DATA_ERROR_CODE(nfo) );
+ length = unum_getTextAttribute( FORMATTER_UNUM(nfo), attribute, value, length, &INTL_DATA_ERROR_CODE(nfo) );
if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
efree(value);
value = value_buf;
@@ -166,10 +169,10 @@ U_CFUNC PHP_FUNCTION( numfmt_set_attribute )
case UNUM_MIN_SIGNIFICANT_DIGITS:
case UNUM_MAX_SIGNIFICANT_DIGITS:
case UNUM_LENIENT_PARSE:
- unum_setAttribute(FORMATTER_OBJECT(nfo), attribute, zval_get_long(value));
+ unum_setAttribute(FORMATTER_UNUM(nfo), attribute, zval_get_long(value));
break;
case UNUM_ROUNDING_INCREMENT:
- unum_setDoubleAttribute(FORMATTER_OBJECT(nfo), attribute, zval_get_double(value));
+ unum_setDoubleAttribute(FORMATTER_UNUM(nfo), attribute, zval_get_double(value));
break;
default:
INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
@@ -185,8 +188,6 @@ U_CFUNC PHP_FUNCTION( numfmt_set_attribute )
/* {{{ Get formatter attribute value. */
U_CFUNC PHP_FUNCTION( numfmt_set_text_attribute )
{
- int32_t slength = 0;
- UChar *svalue = NULL;
zend_long attribute;
char *value;
size_t len;
@@ -203,14 +204,12 @@ U_CFUNC PHP_FUNCTION( numfmt_set_text_attribute )
FORMATTER_METHOD_FETCH_OBJECT;
/* Convert given attribute value to UTF-16. */
- intl_convert_utf8_to_utf16(&svalue, &slength, value, len, &INTL_DATA_ERROR_CODE(nfo));
+ UnicodeString svalue;
+ intl_stringFromChar(svalue, value, len, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Error converting attribute value to UTF-16" );
/* Actually set new attribute value. */
- unum_setTextAttribute(FORMATTER_OBJECT(nfo), static_cast<UNumberFormatTextAttribute>(attribute), svalue, slength, &INTL_DATA_ERROR_CODE(nfo));
- if (svalue) {
- efree(svalue);
- }
+ unum_setTextAttribute(FORMATTER_UNUM(nfo), static_cast<UNumberFormatTextAttribute>(attribute), svalue.getBuffer(), svalue.length(), &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Error setting text attribute" );
RETURN_TRUE;
@@ -243,12 +242,12 @@ U_CFUNC PHP_FUNCTION( numfmt_get_symbol )
/* Fetch the object. */
FORMATTER_METHOD_FETCH_OBJECT;
- length = unum_getSymbol(FORMATTER_OBJECT(nfo), symbol, value_buf, length, &INTL_DATA_ERROR_CODE(nfo));
+ length = unum_getSymbol(FORMATTER_UNUM(nfo), symbol, value_buf, length, &INTL_DATA_ERROR_CODE(nfo));
if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) {
++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
value = eumalloc(length);
- length = unum_getSymbol(FORMATTER_OBJECT(nfo), symbol, value, length, &INTL_DATA_ERROR_CODE(nfo));
+ length = unum_getSymbol(FORMATTER_UNUM(nfo), symbol, value, length, &INTL_DATA_ERROR_CODE(nfo));
if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
efree(value);
value = value_buf;
@@ -266,8 +265,6 @@ U_CFUNC PHP_FUNCTION( numfmt_set_symbol )
zend_long lsymbol;
char* value = NULL;
size_t value_len = 0;
- UChar* svalue = 0;
- int32_t slength = 0;
FORMATTER_METHOD_INIT_VARS;
/* Parse parameters. */
@@ -288,14 +285,12 @@ U_CFUNC PHP_FUNCTION( numfmt_set_symbol )
FORMATTER_METHOD_FETCH_OBJECT;
/* Convert given symbol to UTF-16. */
- intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
+ UnicodeString svalue;
+ intl_stringFromChar(svalue, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Error converting symbol value to UTF-16" );
/* Actually set the symbol. */
- unum_setSymbol(FORMATTER_OBJECT(nfo), symbol, svalue, slength, &INTL_DATA_ERROR_CODE(nfo));
- if (svalue) {
- efree(svalue);
- }
+ unum_setSymbol(FORMATTER_UNUM(nfo), symbol, svalue.getBuffer(), svalue.length(), &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Error setting symbol value" );
RETURN_TRUE;
@@ -320,12 +315,12 @@ U_CFUNC PHP_FUNCTION( numfmt_get_pattern )
/* Fetch the object. */
FORMATTER_METHOD_FETCH_OBJECT;
- length = unum_toPattern(FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo));
+ length = unum_toPattern(FORMATTER_UNUM(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo));
if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) {
++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
value = eumalloc(length);
- length = unum_toPattern( FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo) );
+ length = unum_toPattern( FORMATTER_UNUM(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo) );
if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
efree(value);
value = value_buf;
@@ -342,8 +337,6 @@ U_CFUNC PHP_FUNCTION( numfmt_set_pattern )
{
char* value = NULL;
size_t value_len = 0;
- int32_t slength = 0;
- UChar* svalue = NULL;
UParseError spattern_error = {0};
FORMATTER_METHOD_INIT_VARS;
@@ -357,13 +350,11 @@ U_CFUNC PHP_FUNCTION( numfmt_set_pattern )
FORMATTER_METHOD_FETCH_OBJECT;
/* Convert given pattern to UTF-16. */
- intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
+ UnicodeString svalue;
+ intl_stringFromChar(svalue, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Error converting pattern to UTF-16" );
- unum_applyPattern(FORMATTER_OBJECT(nfo), 0, svalue, slength, &spattern_error, &INTL_DATA_ERROR_CODE(nfo));
- if (svalue) {
- efree(svalue);
- }
+ unum_applyPattern(FORMATTER_UNUM(nfo), 0, svalue.getBuffer(), svalue.length(), &spattern_error, &INTL_DATA_ERROR_CODE(nfo));
if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
char *msg;
spprintf(&msg, 0, "Error setting pattern value at line %d, offset %d", spattern_error.line, spattern_error.offset);
@@ -393,7 +384,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_locale )
/* Fetch the object. */
FORMATTER_METHOD_FETCH_OBJECT;
- loc = unum_getLocaleByType(FORMATTER_OBJECT(nfo), static_cast<ULocDataLocaleType>(type), &INTL_DATA_ERROR_CODE(nfo));
+ loc = unum_getLocaleByType(FORMATTER_UNUM(nfo), static_cast<ULocDataLocaleType>(type), &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Error getting locale" );
RETURN_STRING(loc);
}
diff --git a/ext/intl/formatter/formatter_class.cpp b/ext/intl/formatter/formatter_class.cpp
index ce367096d85..7391a1cd9f6 100644
--- a/ext/intl/formatter/formatter_class.cpp
+++ b/ext/intl/formatter/formatter_class.cpp
@@ -12,8 +12,6 @@
+----------------------------------------------------------------------+
*/
-#include <unicode/unum.h>
-
#include "formatter_class.h"
extern "C" {
#include "php_intl.h"
@@ -72,10 +70,9 @@ U_CFUNC zend_object *NumberFormatter_object_clone(zend_object *object)
zend_objects_clone_members(&new_nfo->zo, &nfo->zo);
/* clone formatter object. It may fail, the destruction code must handle this case */
- if (FORMATTER_OBJECT(nfo) != NULL) {
- UErrorCode error = U_ZERO_ERROR;
- FORMATTER_OBJECT(new_nfo) = unum_clone(FORMATTER_OBJECT(nfo), &error);
- if (U_FAILURE(error)) {
+ if (FORMATTER_OBJECT(nfo) != nullptr) {
+ FORMATTER_OBJECT(new_nfo) = FORMATTER_OBJECT(nfo)->clone();
+ if (FORMATTER_OBJECT(new_nfo) == nullptr) {
zend_throw_error(NULL, "Failed to clone NumberFormatter");
}
} else {
diff --git a/ext/intl/formatter/formatter_class.h b/ext/intl/formatter/formatter_class.h
index 465c9edaceb..47207b843bf 100644
--- a/ext/intl/formatter/formatter_class.h
+++ b/ext/intl/formatter/formatter_class.h
@@ -15,7 +15,7 @@
#ifndef FORMATTER_CLASS_H
#define FORMATTER_CLASS_H
-#include <php.h>
+#include "formatter_data.h"
#ifdef __cplusplus
extern "C" {
@@ -23,7 +23,6 @@ extern "C" {
#include "intl_common.h"
#include "intl_error.h"
#include "intl_data.h"
-#include "formatter_data.h"
#ifdef __cplusplus
}
#endif
diff --git a/ext/intl/formatter/formatter_data.cpp b/ext/intl/formatter/formatter_data.cpp
index 593d746f100..506972e7208 100644
--- a/ext/intl/formatter/formatter_data.cpp
+++ b/ext/intl/formatter/formatter_data.cpp
@@ -26,7 +26,7 @@ void formatter_data_init( formatter_data* nf_data )
if( !nf_data )
return;
- nf_data->unum = NULL;
+ nf_data->unum = nullptr;
intl_error_reset( &nf_data->error );
}
/* }}} */
@@ -39,10 +39,8 @@ void formatter_data_free( formatter_data* nf_data )
if( !nf_data )
return;
- if( nf_data->unum )
- unum_close( nf_data->unum );
-
- nf_data->unum = NULL;
+ delete nf_data->unum;
+ nf_data->unum = nullptr;
intl_error_reset( &nf_data->error );
}
/* }}} */
diff --git a/ext/intl/formatter/formatter_data.h b/ext/intl/formatter/formatter_data.h
index 183682bee6a..1208970471b 100644
--- a/ext/intl/formatter/formatter_data.h
+++ b/ext/intl/formatter/formatter_data.h
@@ -15,9 +15,21 @@
#ifndef FORMATTER_DATA_H
#define FORMATTER_DATA_H
+#ifdef __cplusplus
+extern "C" {
+#endif
#include <php.h>
+#ifdef __cplusplus
+}
+#endif
+
+#include <unicode/numfmt.h>
-#include <unicode/unum.h>
+#ifdef __cplusplus
+using icu::NumberFormat;
+#else
+typedef void NumberFormat;
+#endif
#ifdef __cplusplus
extern "C" {
@@ -32,7 +44,7 @@ typedef struct {
intl_error error;
// formatter handling
- UNumberFormat* unum;
+ NumberFormat* unum;
} formatter_data;
#ifdef __cplusplus
diff --git a/ext/intl/formatter/formatter_format.cpp b/ext/intl/formatter/formatter_format.cpp
index 6b2651e4ead..48edabc8ccd 100644
--- a/ext/intl/formatter/formatter_format.cpp
+++ b/ext/intl/formatter/formatter_format.cpp
@@ -16,24 +16,21 @@
#include <config.h>
#endif
+#include <unicode/curramt.h>
+#include <unicode/fmtable.h>
+#include "../intl_convertcpp.h"
+#include "formatter_class.h"
+#include "formatter_format.h"
+
extern "C" {
#include "php_intl.h"
-#include "intl_convert.h"
}
-#include <unicode/ustring.h>
-
-#include "formatter_class.h"
-#include "formatter_format.h"
-
/* {{{ Format a number. */
U_CFUNC PHP_FUNCTION( numfmt_format )
{
zval *number;
zend_long type = FORMAT_TYPE_DEFAULT;
- UChar format_buf[32];
- UChar* formatted = format_buf;
- int32_t formatted_len = USIZE(format_buf);
FORMATTER_METHOD_INIT_VARS;
/* Parse parameters. */
@@ -59,50 +56,27 @@ U_CFUNC PHP_FUNCTION( numfmt_format )
}
}
+ icu::UnicodeString result;
+ icu::FieldPosition pos;
+
switch(type) {
case FORMAT_TYPE_INT32:
convert_to_long(number);
- formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
- formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
- if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
- intl_error_reset(INTL_DATA_ERROR_P(nfo));
- formatted = eumalloc(formatted_len);
- formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
- formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
- if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
- efree(formatted);
- }
- }
+ FORMATTER_OBJECT(nfo)->format((int32_t)Z_LVAL_P(number), result, pos, INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
break;
case FORMAT_TYPE_INT64:
{
int64_t value = (Z_TYPE_P(number) == IS_DOUBLE)?(int64_t)Z_DVAL_P(number):Z_LVAL_P(number);
- formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
- if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
- intl_error_reset(INTL_DATA_ERROR_P(nfo));
- formatted = eumalloc(formatted_len);
- formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
- if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
- efree(formatted);
- }
- }
+ FORMATTER_OBJECT(nfo)->format(value, result, pos, INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
}
break;
case FORMAT_TYPE_DOUBLE:
convert_to_double(number);
- formatted_len = unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
- if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
- intl_error_reset(INTL_DATA_ERROR_P(nfo));
- formatted = eumalloc(formatted_len);
- unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
- if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
- efree(formatted);
- }
- }
+ FORMATTER_OBJECT(nfo)->format(Z_DVAL_P(number), result, pos, INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
break;
case FORMAT_TYPE_CURRENCY:
@@ -120,7 +94,9 @@ U_CFUNC PHP_FUNCTION( numfmt_format )
RETURN_THROWS();
}
- INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );
+ zend_string *u8str = intl_charFromString(result, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS(nfo, "Error converting result to UTF-8");
+ RETVAL_STR(u8str);
}
/* }}} */
@@ -128,13 +104,8 @@ U_CFUNC PHP_FUNCTION( numfmt_format )
U_CFUNC PHP_FUNCTION( numfmt_format_currency )
{
double number;
- UChar format_buf[32];
- UChar* formatted = format_buf;
- int32_t formatted_len = USIZE(format_buf);
char* currency = NULL;
size_t currency_len = 0;
- UChar* scurrency = NULL;
- int32_t scurrency_len = 0;
FORMATTER_METHOD_INIT_VARS;
/* Parse parameters. */
@@ -148,36 +119,31 @@ U_CFUNC PHP_FUNCTION( numfmt_format_currency )
FORMATTER_METHOD_FETCH_OBJECT;
/* Convert currency to UTF-16. */
- intl_convert_utf8_to_utf16(&scurrency, &scurrency_len, currency, currency_len, &INTL_DATA_ERROR_CODE(nfo));
+ icu::UnicodeString ucurrency;
+ intl_stringFromChar(ucurrency, currency, currency_len, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-16 failed" );
- /* Format the number using a fixed-length buffer. */
- formatted_len = unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
-
- /* If the buffer turned out to be too small
- * then allocate another buffer dynamically
- * and use it to format the number.
- */
- if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
- intl_error_reset(INTL_DATA_ERROR_P(nfo));
- formatted = eumalloc(formatted_len);
- unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ /* Format using CurrencyAmount. */
+ icu::CurrencyAmount *currAmt = new icu::CurrencyAmount(number, ucurrency.getTerminatedBuffer(), INTL_DATA_ERROR_CODE(nfo));
+ if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ delete currAmt;
+ intl_errors_set_custom_msg(INTL_DATA_ERROR_P(nfo), "Number formatting failed");
+ RETURN_FALSE;
}
-
- if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) ) {
- intl_error_set_code( NULL, INTL_DATA_ERROR_CODE((nfo)) );
- intl_errors_set_custom_msg( INTL_DATA_ERROR_P(nfo), "Number formatting failed");
- RETVAL_FALSE;
- if (formatted != format_buf) {
- efree(formatted);
- }
- } else {
- INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );
+ icu::Formattable fmt;
+ fmt.adoptObject(currAmt);
+ icu::UnicodeString result;
+ icu::FieldPosition pos;
+ FORMATTER_OBJECT(nfo)->format(fmt, result, pos, INTL_DATA_ERROR_CODE(nfo));
+
+ if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ intl_errors_set_custom_msg(INTL_DATA_ERROR_P(nfo), "Number formatting failed");
+ RETURN_FALSE;
}
- if(scurrency) {
- efree(scurrency);
- }
+ zend_string *u8str = intl_charFromString(result, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS(nfo, "Error converting result to UTF-8");
+ RETVAL_STR(u8str);
}
/* }}} */
diff --git a/ext/intl/formatter/formatter_main.cpp b/ext/intl/formatter/formatter_main.cpp
index 31c4cdcc485..79750714a5f 100644
--- a/ext/intl/formatter/formatter_main.cpp
+++ b/ext/intl/formatter/formatter_main.cpp
@@ -16,14 +16,17 @@
#include <config.h>
#endif
-#include <unicode/ustring.h>
-#include <unicode/uloc.h>
+#include <unicode/locid.h>
+#include <unicode/decimfmt.h>
+#include <unicode/dcfmtsym.h>
+#include <unicode/rbnf.h>
+#include <unicode/compactdecimalformat.h>
+#include "../intl_convertcpp.h"
+#include "formatter_class.h"
extern "C" {
#include "php_intl.h"
-#include "intl_convert.h"
}
-#include "formatter_class.h"
/* {{{ */
static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS)
@@ -32,8 +35,7 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS)
char* pattern = NULL;
size_t locale_len = 0, pattern_len = 0;
zend_long style;
- UChar* spattern = NULL;
- int32_t spattern_len = 0;
+ UnicodeString upattern;
FORMATTER_METHOD_INIT_VARS;
ZEND_PARSE_PARAMETERS_START(2, 3)
@@ -53,7 +55,7 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS)
/* Convert pattern (if specified) to UTF-16. */
if(pattern && pattern_len) {
- intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(nfo));
+ intl_stringFromChar(upattern, pattern, pattern_len, &INTL_DATA_ERROR_CODE(nfo));
INTL_CTOR_CHECK_STATUS(nfo, "error converting pattern to UTF-16");
}
@@ -61,7 +63,7 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS)
locale = (char *)intl_locale_get_default();
}
- if (strlen(uloc_getISO3Language(locale)) == 0) {
+ if (icu::Locale(locale).getISO3Language()[0] == '\0') {
zend_argument_value_error(1, "\"%s\" is invalid", locale);
return FAILURE;
}
@@ -70,12 +72,55 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS)
const char* final_locale = canonicalized_locale ? canonicalized_locale : locale;
/* Create an ICU number formatter. */
- FORMATTER_OBJECT(nfo) = unum_open(static_cast<UNumberFormatStyle>(style), spattern, spattern_len, final_locale, nullptr, &INTL_DATA_ERROR_CODE(nfo));
-
- if (spattern) {
- efree(spattern);
+ icu::Locale loc(final_locale);
+ switch (style) {
+ case UNUM_PATTERN_DECIMAL: {
+ icu::DecimalFormatSymbols *syms = new icu::DecimalFormatSymbols(loc, INTL_DATA_ERROR_CODE(nfo));
+ if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ delete syms;
+ break;
+ }
+ FORMATTER_OBJECT(nfo) = new icu::DecimalFormat(upattern, syms, INTL_DATA_ERROR_CODE(nfo));
+ if (FORMATTER_OBJECT(nfo) == nullptr) {
+ delete syms;
+ }
+ break;
+ }
+ case UNUM_PATTERN_RULEBASED: {
+ UParseError parseError;
+ FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(upattern, loc, parseError, INTL_DATA_ERROR_CODE(nfo));
+ break;
+ }
+ case UNUM_SPELLOUT:
+ FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(icu::URBNF_SPELLOUT, loc, INTL_DATA_ERROR_CODE(nfo));
+ break;
+ case UNUM_ORDINAL:
+ FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(icu::URBNF_ORDINAL, loc, INTL_DATA_ERROR_CODE(nfo));
+ break;
+ case UNUM_DURATION:
+ FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(icu::URBNF_DURATION, loc, INTL_DATA_ERROR_CODE(nfo));
+ break;
+ case UNUM_NUMBERING_SYSTEM: {
+ UErrorCode localErr = U_ZERO_ERROR;
+ int32_t keywordLength = loc.getKeywordValue("numbers", nullptr, 0, localErr);
+ if (keywordLength > 0) {
+ FORMATTER_OBJECT(nfo) = NumberFormat::createInstance(loc, UNUM_DEFAULT, INTL_DATA_ERROR_CODE(nfo));
+ } else {
+ FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(icu::URBNF_NUMBERING_SYSTEM, loc, INTL_DATA_ERROR_CODE(nfo));
+ }
+ break;
+ }
+ case UNUM_DECIMAL_COMPACT_SHORT:
+ FORMATTER_OBJECT(nfo) = icu::CompactDecimalFormat::createInstance(loc, UNUM_SHORT, INTL_DATA_ERROR_CODE(nfo));
+ break;
+ case UNUM_DECIMAL_COMPACT_LONG:
+ FORMATTER_OBJECT(nfo) = icu::CompactDecimalFormat::createInstance(loc, UNUM_LONG, INTL_DATA_ERROR_CODE(nfo));
+ break;
+ default:
+ FORMATTER_OBJECT(nfo) = NumberFormat::createInstance(loc, static_cast<UNumberFormatStyle>(style), INTL_DATA_ERROR_CODE(nfo));
+ break;
}
-
+
if (canonicalized_locale) {
efree(canonicalized_locale);
}
diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp
index b2dc2e8dcbd..7bbc461516b 100644
--- a/ext/intl/formatter/formatter_parse.cpp
+++ b/ext/intl/formatter/formatter_parse.cpp
@@ -16,16 +16,18 @@
#include <config.h>
#endif
+#include <unicode/fmtable.h>
+#include <unicode/curramt.h>
+#include "../intl_convertcpp.h"
+#include "formatter_class.h"
+#include "formatter_format.h"
+
extern "C" {
#include "php_intl.h"
-#include "intl_convert.h"
}
-#include <unicode/ustring.h>
#include <locale.h>
-
-#include "formatter_class.h"
-#include "formatter_format.h"
+#include <memory>
#define ICU_LOCALE_BUG 1
@@ -33,14 +35,9 @@ extern "C" {
U_CFUNC PHP_FUNCTION( numfmt_parse )
{
zend_long type = FORMAT_TYPE_DOUBLE;
- UChar* sstr = NULL;
- int32_t sstr_len = 0;
char* str = NULL;
size_t str_len;
- int32_t val32, position = 0;
- int64_t val64;
- double val_double;
- int32_t* position_p = NULL;
+ int32_t position = 0;
zval *zposition = NULL;
char *oldlocale;
FORMATTER_METHOD_INIT_VARS;
@@ -54,14 +51,14 @@ U_CFUNC PHP_FUNCTION( numfmt_parse )
if (zposition) {
position = (int32_t) zval_get_long(zposition);
- position_p = &position;
}
/* Fetch the object. */
FORMATTER_METHOD_FETCH_OBJECT;
/* Convert given string to UTF-16. */
- intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
+ icu::UnicodeString ustr;
+ intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
#if ICU_LOCALE_BUG && defined(LC_NUMERIC)
@@ -72,21 +69,38 @@ U_CFUNC PHP_FUNCTION( numfmt_parse )
switch(type) {
case FORMAT_TYPE_INT32:
- val32 = unum_parse(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
- RETVAL_LONG(val32);
- break;
case FORMAT_TYPE_INT64:
- val64 = unum_parseInt64(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
- if(val64 > ZEND_LONG_MAX || val64 < ZEND_LONG_MIN) {
- RETVAL_DOUBLE(val64);
+ case FORMAT_TYPE_DOUBLE:
+ {
+ icu::Formattable result;
+ icu::ParsePosition pp(position);
+ FORMATTER_OBJECT(nfo)->parse(ustr, result, pp);
+
+ if (pp.getErrorIndex() >= 0) {
+ INTL_DATA_ERROR_CODE(nfo) = U_PARSE_ERROR;
} else {
- RETVAL_LONG((zend_long)val64);
+ position = pp.getIndex();
+ switch(type) {
+ case FORMAT_TYPE_INT32:
+ RETVAL_LONG(result.getLong(INTL_DATA_ERROR_CODE(nfo)));
+ break;
+ case FORMAT_TYPE_INT64:
+ {
+ int64_t val64 = result.getInt64(INTL_DATA_ERROR_CODE(nfo));
+ if(val64 > ZEND_LONG_MAX || val64 < ZEND_LONG_MIN) {
+ RETVAL_DOUBLE(val64);
+ } else {
+ RETVAL_LONG((zend_long)val64);
+ }
+ break;
+ }
+ case FORMAT_TYPE_DOUBLE:
+ RETVAL_DOUBLE(result.getDouble(INTL_DATA_ERROR_CODE(nfo)));
+ break;
+ }
}
break;
- case FORMAT_TYPE_DOUBLE:
- val_double = unum_parseDouble(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
- RETVAL_DOUBLE(val_double);
- break;
+ }
case FORMAT_TYPE_CURRENCY:
if (hasThis()) {
const char *space;
@@ -113,10 +127,6 @@ cleanup:
efree(oldlocale);
#endif
- if (sstr) {
- efree(sstr);
- }
-
INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
}
/* }}} */
@@ -124,14 +134,8 @@ cleanup:
/* {{{ Parse a number as currency. */
U_CFUNC PHP_FUNCTION( numfmt_parse_currency )
{
- double number;
- UChar currency[5] = {0};
- UChar* sstr = NULL;
- int32_t sstr_len = 0;
- zend_string *u8str;
char *str;
size_t str_len;
- int32_t* position_p = NULL;
int32_t position = 0;
zval *zcurrency, *zposition = NULL;
FORMATTER_METHOD_INIT_VARS;
@@ -147,27 +151,34 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency )
FORMATTER_METHOD_FETCH_OBJECT;
/* Convert given string to UTF-16. */
- intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
+ icu::UnicodeString ustr;
+ intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
if(zposition) {
position = (int32_t) zval_get_long(zposition);
- position_p = &position;
}
- number = unum_parseDoubleCurrency(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, currency, &INTL_DATA_ERROR_CODE(nfo));
- if(zposition) {
- ZEND_TRY_ASSIGN_REF_LONG(zposition, position);
+ icu::ParsePosition pp(position);
+ std::unique_ptr<icu::CurrencyAmount> currAmt(FORMATTER_OBJECT(nfo)->parseCurrency(ustr, pp));
+
+ if (currAmt == nullptr || pp.getErrorIndex() >= 0) {
+ INTL_DATA_ERROR_CODE(nfo) = U_PARSE_ERROR;
+ INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
}
- if (sstr) {
- efree(sstr);
+
+ if(zposition) {
+ ZEND_TRY_ASSIGN_REF_LONG(zposition, pp.getIndex());
}
- INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
+
+ double number = currAmt->getNumber().getDouble(INTL_DATA_ERROR_CODE(nfo));
/* Convert parsed currency to UTF-8 and pass it back to caller. */
- u8str = intl_convert_utf16_to_utf8(currency, u_strlen(currency), &INTL_DATA_ERROR_CODE(nfo));
+ icu::UnicodeString ucurrency(currAmt->getISOCurrency());
+
+ zend_string *u8str = intl_charFromString(ucurrency, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-8 failed" );
- ZEND_TRY_ASSIGN_REF_NEW_STR(zcurrency, u8str);
+ ZEND_TRY_ASSIGN_REF_STR(zcurrency, u8str);
RETVAL_DOUBLE( number );
}
diff --git a/ext/intl/listformatter/listformatter_class.cpp b/ext/intl/listformatter/listformatter_class.cpp
index f39e86f6673..817b3d74369 100644
--- a/ext/intl/listformatter/listformatter_class.cpp
+++ b/ext/intl/listformatter/listformatter_class.cpp
@@ -14,22 +14,27 @@
extern "C" {
#include "php.h"
+}
+
+#include <unicode/listformatter.h>
+#include <unicode/locid.h>
+#include "../intl_convertcpp.h"
+
+extern "C" {
#include "php_intl.h"
-#include "intl_convert.h"
}
-#include <unicode/ulistformatter.h>
#include "listformatter_class.h"
#include "listformatter_arginfo.h"
+#include <memory>
+
static zend_object_handlers listformatter_handlers;
static void listformatter_free_obj(zend_object *object)
{
ListFormatter_object *obj = php_intl_listformatter_fetch_object(object);
- if( obj->lf_data.ulistfmt )
- ulistfmt_close( obj->lf_data.ulistfmt );
-
+ delete obj->lf_data.ulistfmt;
obj->lf_data.ulistfmt = nullptr;
intl_error_reset( &obj->lf_data.error );
@@ -78,7 +83,7 @@ PHP_METHOD(IntlListFormatter, __construct)
RETURN_THROWS();
}
- if (strlen(uloc_getISO3Language(locale)) == 0) {
+ if (icu::Locale(locale).getISO3Language()[0] == '\0') {
zend_argument_value_error(1, "\"%s\" is invalid", locale);
RETURN_THROWS();
}
@@ -89,13 +94,13 @@ PHP_METHOD(IntlListFormatter, __construct)
zend_argument_value_error(2, "must be one of IntlListFormatter::TYPE_AND, IntlListFormatter::TYPE_OR, or IntlListFormatter::TYPE_UNITS");
RETURN_THROWS();
}
-
+
if (width != ULISTFMT_WIDTH_WIDE && width != ULISTFMT_WIDTH_SHORT && width != ULISTFMT_WIDTH_NARROW) {
zend_argument_value_error(3, "must be one of IntlListFormatter::WIDTH_WIDE, IntlListFormatter::WIDTH_SHORT, or IntlListFormatter::WIDTH_NARROW");
RETURN_THROWS();
}
- LISTFORMATTER_OBJECT(obj) = ulistfmt_openForType(locale, static_cast<UListFormatterType>(type), static_cast<UListFormatterWidth>(width), &status);
+ LISTFORMATTER_OBJECT(obj) = ListFormatter::createInstance(icu::Locale(locale), static_cast<UListFormatterType>(type), static_cast<UListFormatterWidth>(width), status);
#else
if (type != INTL_LISTFORMATTER_FALLBACK_TYPE_AND) {
zend_argument_value_error(2, "contains an unsupported type. ICU 66 and below only support IntlListFormatter::TYPE_AND");
@@ -107,7 +112,7 @@ PHP_METHOD(IntlListFormatter, __construct)
RETURN_THROWS();
}
- LISTFORMATTER_OBJECT(obj) = ulistfmt_open(locale, &status);
+ LISTFORMATTER_OBJECT(obj) = ListFormatter::createInstance(icu::Locale(locale), status);
#endif
if (U_FAILURE(status)) {
@@ -131,84 +136,47 @@ PHP_METHOD(IntlListFormatter, format)
RETURN_EMPTY_STRING();
}
- const UChar **items = (const UChar **)safe_emalloc(count, sizeof(const UChar *), 0);
- int32_t *itemLengths = (int32_t *)safe_emalloc(count, sizeof(int32_t), 0);
+ std::unique_ptr<UnicodeString[]> items(new UnicodeString[count]);
uint32_t i = 0;
zval *val;
ZEND_HASH_FOREACH_VAL(ht, val) {
zend_string *str_val, *tmp_str;
-
- str_val = zval_get_tmp_string(val, &tmp_str);
-
- // Convert PHP string to UTF-16
- UChar *ustr = nullptr;
- int32_t ustr_len = 0;
- UErrorCode status = U_ZERO_ERROR;
-
- intl_convert_utf8_to_utf16(&ustr, &ustr_len, ZSTR_VAL(str_val), ZSTR_LEN(str_val), &status);
+ UErrorCode conv_status = U_ZERO_ERROR;
+
+ str_val = zval_try_get_tmp_string(val, &tmp_str);
+ if (UNEXPECTED(!str_val)) {
+ RETURN_THROWS();
+ }
+ intl_stringFromChar(items[i], ZSTR_VAL(str_val), ZSTR_LEN(str_val), &conv_status);
zend_tmp_string_release(tmp_str);
- if (U_FAILURE(status)) {
- // We can't use goto cleanup because items and itemLengths are incompletely allocated
- for (uint32_t j = 0; j < i; j++) {
- efree((void *)items[j]);
- }
- efree(items);
- efree(itemLengths);
- intl_error_set(nullptr, status, "Failed to convert string to UTF-16");
+ if (U_FAILURE(conv_status)) {
+ intl_error_set(nullptr, conv_status, "Failed to convert string to UTF-16");
RETURN_FALSE;
}
-
- items[i] = ustr;
- itemLengths[i] = ustr_len;
+
i++;
} ZEND_HASH_FOREACH_END();
UErrorCode status = U_ZERO_ERROR;
- int32_t resultLength;
- UChar *result = nullptr;
- zend_string *ret = nullptr;
-
- resultLength = ulistfmt_format(LISTFORMATTER_OBJECT(obj), items, itemLengths, count, nullptr, 0, &status);
-
- if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
- intl_error_set(nullptr, status, "Failed to format list");
- RETVAL_FALSE;
- goto cleanup;
- }
+ UnicodeString result;
- // Allocate buffer and try again
- status = U_ZERO_ERROR;
- result = (UChar *)safe_emalloc(resultLength + 1, sizeof(UChar), 0);
- ulistfmt_format(LISTFORMATTER_OBJECT(obj), items, itemLengths, count, result, resultLength, &status);
+ LISTFORMATTER_OBJECT(obj)->format(items.get(), count, result, status);
if (U_FAILURE(status)) {
- if (result) {
- efree(result);
- }
intl_error_set(nullptr, status, "Failed to format list");
- RETVAL_FALSE;
- goto cleanup;
+ RETURN_FALSE;
}
- // Convert result back to UTF-8
- ret = intl_convert_utf16_to_utf8(result, resultLength, &status);
- efree(result);
-
+ zend_string *ret = intl_charFromString(result, &status);
+
if (!ret) {
intl_error_set(nullptr, status, "Failed to convert result to UTF-8");
- RETVAL_FALSE;
- } else {
- RETVAL_NEW_STR(ret);
+ RETURN_FALSE;
}
-cleanup:
- for (i = 0; i < count; i++) {
- efree((void *)items[i]);
- }
- efree(items);
- efree(itemLengths);
+ RETVAL_STR(ret);
}
PHP_METHOD(IntlListFormatter, getErrorCode)
@@ -236,7 +204,7 @@ void listformatter_register_class(void)
{
zend_class_entry *class_entry = register_class_IntlListFormatter();
class_entry->create_object = listformatter_create_object;
-
+
memcpy(&listformatter_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
listformatter_handlers.offset = offsetof(ListFormatter_object, zo);
listformatter_handlers.free_obj = listformatter_free_obj;
diff --git a/ext/intl/listformatter/listformatter_class.h b/ext/intl/listformatter/listformatter_class.h
index dc40b7a8be0..6f8676193df 100644
--- a/ext/intl/listformatter/listformatter_class.h
+++ b/ext/intl/listformatter/listformatter_class.h
@@ -21,14 +21,20 @@
#include "intl_error.h"
#include "intl_data.h"
-#include <unicode/ulistformatter.h>
+#include <unicode/listformatter.h>
+
+#ifdef __cplusplus
+using icu::ListFormatter;
+#else
+typedef void ListFormatter;
+#endif
typedef struct {
// error handling
intl_error error;
// formatter handling
- UListFormatter* ulistfmt;
+ ListFormatter* ulistfmt;
} listformatter_data;
typedef struct {
diff --git a/ext/intl/rangeformatter/rangeformatter_class.cpp b/ext/intl/rangeformatter/rangeformatter_class.cpp
index 5a19df08316..6921791e588 100644
--- a/ext/intl/rangeformatter/rangeformatter_class.cpp
+++ b/ext/intl/rangeformatter/rangeformatter_class.cpp
@@ -22,6 +22,7 @@ extern "C" {
#include <unicode/numberrangeformatter.h>
#include <unicode/numberformatter.h>
#include <unicode/unistr.h>
+#include <unicode/locid.h>
#include "../intl_convertcpp.h"
extern "C" {
@@ -30,7 +31,6 @@ extern "C" {
#include "../intl_data.h"
#include "rangeformatter_arginfo.h"
#include "rangeformatter_class.h"
- #include "intl_convert.h"
}
using icu::number::NumberRangeFormatter;
@@ -91,7 +91,7 @@ U_CFUNC PHP_METHOD(IntlNumberRangeFormatter, createFromSkeleton)
RETURN_THROWS();
}
- if (strlen(uloc_getISO3Language(locale)) == 0) {
+ if (icu::Locale(locale).getISO3Language()[0] == '\0') {
zend_argument_value_error(2, "\"%s\" is invalid", locale);
RETURN_THROWS();
}
diff --git a/ext/intl/tests/formatter_fail.phpt b/ext/intl/tests/formatter_fail.phpt
index 53f6b4ac7c7..678b87b8c1b 100644
--- a/ext/intl/tests/formatter_fail.phpt
+++ b/ext/intl/tests/formatter_fail.phpt
@@ -137,9 +137,9 @@ function crt($t, $l, $s) {
'U_ZERO_ERROR'
IntlException: NumberFormatter::__construct(): number formatter creation failed
-'NumberFormatter::__construct(): number formatter creation failed: U_UNSUPPORTED_ERROR'
-'NumberFormatter::create(): number formatter creation failed: U_UNSUPPORTED_ERROR'
-'numfmt_create(): number formatter creation failed: U_UNSUPPORTED_ERROR'
+'NumberFormatter::__construct(): number formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'NumberFormatter::create(): number formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'numfmt_create(): number formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
IntlException: NumberFormatter::__construct(): number formatter creation failed
'NumberFormatter::__construct(): number formatter creation failed: U_MEMORY_ALLOCATION_ERROR'