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.
This commit is contained in:
David CARLIER authored and GitHub committed 2026-05-16 14:01:47 +01:00
1 parent b89e0ef305
commit 3ed80a154d
12 files changed
+245 -252

No files matched your search

+25 -34
View File
@@ -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);
}
+3 -6
View File
@@ -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 {
+1 -2
View File
@@ -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
+3 -5
View File
@@ -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 );
}
/* }}} */
+14 -2
View File
@@ -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/unum.h>
#include <unicode/numfmt.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
+36 -70
View File
@@ -16,24 +16,21 @@
#include <config.h>
#endif
extern "C" {
#include "php_intl.h"
#include "intl_convert.h"
}
#include <unicode/ustring.h>
#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"
}
/* {{{ 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));
/* 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;
}
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 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));
if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
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 ) );
}
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);
}
/* }}} */
+58 -13
View File
@@ -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);
}
+58 -47
View File
@@ -16,31 +16,28 @@
#include <config.h>
#endif
extern "C" {
#include "php_intl.h"
#include "intl_convert.h"
}
#include <unicode/ustring.h>
#include <locale.h>
#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 <locale.h>
#include <memory>
#define ICU_LOCALE_BUG 1
/* {{{ Parse a number. */
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));
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(zposition) {
ZEND_TRY_ASSIGN_REF_LONG(zposition, position);
ZEND_TRY_ASSIGN_REF_LONG(zposition, pp.getIndex());
}
if (sstr) {
efree(sstr);
}
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 );
}
+34 -66
View File
@@ -14,22 +14,27 @@
extern "C" {
#include "php.h"
#include "php_intl.h"
#include "intl_convert.h"
}
#include <unicode/ulistformatter.h>
#include <unicode/listformatter.h>
#include <unicode/locid.h>
#include "../intl_convertcpp.h"
extern "C" {
#include "php_intl.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;
UnicodeString result;
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;
}
// 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;
+8 -2
View File
@@ -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 {
@@ -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();
}
+3 -3
View File
@@ -137,9 +137,9 @@ TypeError: numfmt_create(): Argument #1 ($locale) must be of type string, array
'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'