Skip to content

ext/intl: GH-17006 part 1, NumberFormat and NumberFormatter co-existing. #17073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
6 changes: 3 additions & 3 deletions ext/intl/formatter/formatter.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,13 @@ class NumberFormatter
#[\Deprecated(since: '8.3')]
public const int TYPE_CURRENCY = UNKNOWN;

public function __construct(string $locale, int $style, ?string $pattern = null) {}
public function __construct(string $locale, string|int $style, ?string $pattern = null) {}

/**
* @tentative-return-type
* @alias numfmt_create
*/
public static function create(string $locale, int $style, ?string $pattern = null): ?NumberFormatter {}
public static function create(string $locale, string|int $style, ?string $pattern = null): ?NumberFormatter {}

/**
* @tentative-return-type
Expand All @@ -208,7 +208,7 @@ public function parse(string $string, int $type = NumberFormatter::TYPE_DOUBLE,
* @tentative-return-type
* @alias numfmt_format_currency
*/
public function formatCurrency(float $amount, string $currency): string|false {}
public function formatCurrency(float $amount, ?string $currency = null): string|false {}

/**
* @param string $currency
Expand Down
10 changes: 5 additions & 5 deletions ext/intl/formatter/formatter_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions ext/intl/formatter/formatter_class.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ zend_object *NumberFormatter_object_clone(zend_object *object)
} else {
zend_throw_error(NULL, "Cannot clone uninitialized NumberFormatter");
}
if (FORMATTER_OBJECT2(nfo) != NULL) {
FORMATTER_OBJECT2(new_nfo) = FORMATTER_OBJECT2(nfo);
}
return new_obj;
}
/* }}} */
Expand Down
8 changes: 8 additions & 0 deletions ext/intl/formatter/formatter_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ extern zend_class_entry *NumberFormatter_ce_ptr;

#define FORMATTER_METHOD_INIT_VARS INTL_METHOD_INIT_VARS(NumberFormatter, nfo)
#define FORMATTER_OBJECT(nfo) (nfo)->nf_data.unum
#define FORMATTER_OBJECT2(nfo) (nfo)->nf_data.unum2
#define FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK INTL_METHOD_FETCH_OBJECT(INTL_NUMBERFORMATTER, nfo)
#define FORMATTER_METHOD_FETCH_OBJECT \
FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK; \
Expand All @@ -47,6 +48,13 @@ extern zend_class_entry *NumberFormatter_ce_ptr;
zend_throw_error(NULL, "Found unconstructed NumberFormatter"); \
RETURN_THROWS(); \
}
#define FORMATTER_METHOD_FETCH_OBJECT2 \
FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK; \
if (FORMATTER_OBJECT(nfo) == NULL && FORMATTER_OBJECT2(nfo) == NULL) \
{ \
zend_throw_error(NULL, "Found unconstructed NumberFormatter nor new NumberFormatter"); \
RETURN_THROWS(); \
}


#endif // #ifndef FORMATTER_CLASS_H
3 changes: 3 additions & 0 deletions ext/intl/formatter/formatter_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ void formatter_data_free( formatter_data* nf_data )
if( nf_data->unum )
unum_close( nf_data->unum );

if ( nf_data->unum2 )
unumf_close( nf_data->unum2 );

nf_data->unum = NULL;
intl_error_reset( &nf_data->error );
}
Expand Down
4 changes: 4 additions & 0 deletions ext/intl/formatter/formatter_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <php.h>

#include <unicode/unum.h>
#include <unicode/unumberformatter.h>

#include "intl_error.h"

Expand All @@ -27,6 +28,9 @@ typedef struct {

// formatter handling
UNumberFormat* unum;

// new formatter handling
UNumberFormatter* unum2;
} formatter_data;

formatter_data* formatter_data_create( void );
Expand Down
127 changes: 97 additions & 30 deletions ext/intl/formatter/formatter_format.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "php_intl.h"

#include <unicode/unumberformatter.h>
#include <unicode/ustring.h>

#include "formatter_class.h"
Expand All @@ -28,6 +29,7 @@
PHP_FUNCTION( numfmt_format )
{
zval *number;
UFormattedNumber *result = NULL;
zend_long type = FORMAT_TYPE_DEFAULT;
UChar format_buf[32];
UChar* formatted = format_buf;
Expand All @@ -42,7 +44,12 @@ PHP_FUNCTION( numfmt_format )
}

/* Fetch the object. */
FORMATTER_METHOD_FETCH_OBJECT;
FORMATTER_METHOD_FETCH_OBJECT2;

if (FORMATTER_OBJECT2(nfo)) {
result = unumf_openResult(&INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS(nfo, "Error opening formatter result");
}

if(type == FORMAT_TYPE_DEFAULT) {
switch(Z_TYPE_P(number)) {
Expand All @@ -60,48 +67,77 @@ PHP_FUNCTION( numfmt_format )
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 (FORMATTER_OBJECT(nfo)) {
formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
} else {
unumf_formatInt(FORMATTER_OBJECT2(nfo), (int32_t)Z_LVAL_P(number), result, &INTL_DATA_ERROR_CODE(nfo));
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
}
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
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 (FORMATTER_OBJECT(nfo)) {
formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
} else {
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
}
if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
efree(formatted);
}
}
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 (FORMATTER_OBJECT(nfo)) {
formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
} else {
unumf_formatInt(FORMATTER_OBJECT2(nfo), value, result, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
formatted_len = unumf_resultToString(result, formatted, formatted_len, &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 (FORMATTER_OBJECT(nfo)) {
formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
} else {
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
}
if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
efree(formatted);
}
}
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting to string 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 (FORMATTER_OBJECT(nfo)) {
formatted_len = unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
} else {
unumf_formatDouble(FORMATTER_OBJECT2(nfo), Z_DVAL_P(number), result, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
formatted_len = unumf_resultToString(result, formatted, formatted_len, &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 (FORMATTER_OBJECT(nfo)) {
unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
} else {
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
}
if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
efree(formatted);
}
}
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting to string failed" );
break;
case FORMAT_TYPE_CURRENCY:
if (getThis()) {
Expand All @@ -119,6 +155,8 @@ PHP_FUNCTION( numfmt_format )
}

INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );

unumf_closeResult(result);
}
/* }}} */

Expand All @@ -133,33 +171,60 @@ PHP_FUNCTION( numfmt_format_currency )
size_t currency_len = 0;
UChar* scurrency = NULL;
int32_t scurrency_len = 0;
UFormattedNumber *result = NULL;
FORMATTER_METHOD_INIT_VARS;

/* Parse parameters. */
if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Ods",
if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Od|s!",
&object, NumberFormatter_ce_ptr, &number, &currency, &currency_len ) == FAILURE )
{
RETURN_THROWS();
}

/* Fetch the object. */
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));
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));
FORMATTER_METHOD_FETCH_OBJECT2;

if (FORMATTER_OBJECT(nfo)) {
if (!currency) {
zend_argument_value_error(3, "currency cannot be null when instantiating NumberFormatter"
"with a style constant");
RETURN_THROWS();
}
/* Convert currency to UTF-16. */
intl_convert_utf8_to_utf16(&scurrency, &scurrency_len, 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));
}
} else {
/* The new NumberFormatter takes the currency format (aka skeleton)
* from NumberFormatter::__construct 2nd argument.
*/
zend_error(E_DEPRECATED, "Calling NumberFormat::formatCurrency when the object has been instantiated with the currency/* token is deprecated.");
result = unumf_openResult(&INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS(nfo, "Error opening formatter result");
unumf_formatDouble(FORMATTER_OBJECT2(nfo), number, result, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
formatted_len = unumf_resultToString(result, formatted, formatted_len, &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);
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
}
}

if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) ) {
Expand All @@ -176,6 +241,8 @@ PHP_FUNCTION( numfmt_format_currency )
if(scurrency) {
efree(scurrency);
}

unumf_closeResult(result);
}

/* }}} */
24 changes: 19 additions & 5 deletions ext/intl/formatter/formatter_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include <config.h>
#endif

#include <unicode/ucurr.h>
#include <unicode/unumberformatter.h>
#include <unicode/ustring.h>
#include <unicode/uloc.h>

Expand All @@ -30,13 +32,14 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_error_handling *error_
char* pattern = NULL;
size_t locale_len = 0, pattern_len = 0;
zend_long style;
zend_string *sstyle;
UChar* spattern = NULL;
int32_t spattern_len = 0;
FORMATTER_METHOD_INIT_VARS;

ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STRING(locale, locale_len)
Z_PARAM_LONG(style)
Z_PARAM_STR_OR_LONG(sstyle, style)
Z_PARAM_OPTIONAL
Z_PARAM_STRING_OR_NULL(pattern, pattern_len)
ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
Expand Down Expand Up @@ -70,13 +73,24 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_error_handling *error_
}

/* Create an ICU number formatter. */
FORMATTER_OBJECT(nfo) = unum_open(style, spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
if (!sstyle) {
FORMATTER_OBJECT(nfo) = unum_open(style, spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));

if(spattern) {
efree(spattern);
if(spattern) {
efree(spattern);
}

INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: number formatter creation failed");
} else {
UChar *bstyle = 0;
int32_t blen = 0;
intl_convert_utf8_to_utf16(&bstyle, &blen, ZSTR_VAL(sstyle), ZSTR_LEN(sstyle), &INTL_DATA_ERROR_CODE(nfo));
INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: error converting style to UTF-16");
FORMATTER_OBJECT2(nfo) = unumf_openForSkeletonAndLocale(bstyle, blen, locale, &INTL_DATA_ERROR_CODE(nfo));
efree(bstyle);
INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: new number formatter failed");
}

INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: number formatter creation failed");
return SUCCESS;
}
/* }}} */
Expand Down
Loading
Loading