Skip to content

Commit 8361a89

Browse files
committed
ext/intl: GH-17006 part 1, NumberFormat and NumberFormatter co-existing.
Those APIs are not particularly 1:1, here using NumberFormatter for NumberFormatter::format/numft_format at the moment, trying to assess what's possible for next.
1 parent 2b80b2e commit 8361a89

File tree

9 files changed

+109
-22
lines changed

9 files changed

+109
-22
lines changed

ext/intl/formatter/formatter.stub.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,13 @@ class NumberFormatter
183183
#[\Deprecated(since: '8.3')]
184184
public const int TYPE_CURRENCY = UNKNOWN;
185185

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

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

194194
/**
195195
* @tentative-return-type

ext/intl/formatter/formatter_arginfo.h

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/intl/formatter/formatter_class.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ zend_object *NumberFormatter_object_clone(zend_object *object)
7777
} else {
7878
zend_throw_error(NULL, "Cannot clone uninitialized NumberFormatter");
7979
}
80+
if (FORMATTER_OBJECT2(nfo) != NULL) {
81+
FORMATTER_OBJECT2(new_nfo) = FORMATTER_OBJECT2(nfo);
82+
}
8083
return new_obj;
8184
}
8285
/* }}} */

ext/intl/formatter/formatter_class.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ extern zend_class_entry *NumberFormatter_ce_ptr;
3939

4040
#define FORMATTER_METHOD_INIT_VARS INTL_METHOD_INIT_VARS(NumberFormatter, nfo)
4141
#define FORMATTER_OBJECT(nfo) (nfo)->nf_data.unum
42+
#define FORMATTER_OBJECT2(nfo) (nfo)->nf_data.unum2
4243
#define FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK INTL_METHOD_FETCH_OBJECT(INTL_NUMBERFORMATTER, nfo)
4344
#define FORMATTER_METHOD_FETCH_OBJECT \
4445
FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK; \
@@ -47,6 +48,13 @@ extern zend_class_entry *NumberFormatter_ce_ptr;
4748
zend_throw_error(NULL, "Found unconstructed NumberFormatter"); \
4849
RETURN_THROWS(); \
4950
}
51+
#define FORMATTER_METHOD_FETCH_OBJECT2 \
52+
FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK; \
53+
if (FORMATTER_OBJECT(nfo) == NULL && FORMATTER_OBJECT2(nfo) == NULL) \
54+
{ \
55+
zend_throw_error(NULL, "Found unconstructed NumberFormatter nor new NumberFormatter"); \
56+
RETURN_THROWS(); \
57+
}
5058

5159

5260
#endif // #ifndef FORMATTER_CLASS_H

ext/intl/formatter/formatter_data.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ void formatter_data_free( formatter_data* nf_data )
4242
if( nf_data->unum )
4343
unum_close( nf_data->unum );
4444

45+
if ( nf_data->unum2 )
46+
unumf_close( nf_data->unum2 );
47+
4548
nf_data->unum = NULL;
4649
intl_error_reset( &nf_data->error );
4750
}

ext/intl/formatter/formatter_data.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <php.h>
1919

2020
#include <unicode/unum.h>
21+
#include <unicode/unumberformatter.h>
2122

2223
#include "intl_error.h"
2324

@@ -27,6 +28,9 @@ typedef struct {
2728

2829
// formatter handling
2930
UNumberFormat* unum;
31+
32+
// new formatter handling
33+
UNumberFormatter* unum2;
3034
} formatter_data;
3135

3236
formatter_data* formatter_data_create( void );

ext/intl/formatter/formatter_format.c

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "php_intl.h"
2020

21+
#include <unicode/unumberformatter.h>
2122
#include <unicode/ustring.h>
2223

2324
#include "formatter_class.h"
@@ -28,6 +29,7 @@
2829
PHP_FUNCTION( numfmt_format )
2930
{
3031
zval *number;
32+
UFormattedNumber *result = NULL;
3133
zend_long type = FORMAT_TYPE_DEFAULT;
3234
UChar format_buf[32];
3335
UChar* formatted = format_buf;
@@ -42,7 +44,12 @@ PHP_FUNCTION( numfmt_format )
4244
}
4345

4446
/* Fetch the object. */
45-
FORMATTER_METHOD_FETCH_OBJECT;
47+
FORMATTER_METHOD_FETCH_OBJECT2;
48+
49+
if (FORMATTER_OBJECT2(nfo)) {
50+
result = unumf_openResult(&INTL_DATA_ERROR_CODE(nfo));
51+
INTL_METHOD_CHECK_STATUS(nfo, "Error opening formatter result");
52+
}
4653

4754
if(type == FORMAT_TYPE_DEFAULT) {
4855
switch(Z_TYPE_P(number)) {
@@ -60,48 +67,77 @@ PHP_FUNCTION( numfmt_format )
6067
switch(type) {
6168
case FORMAT_TYPE_INT32:
6269
convert_to_long(number);
63-
formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
64-
formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
70+
if (FORMATTER_OBJECT(nfo)) {
71+
formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
72+
formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
73+
} else {
74+
unumf_formatInt(FORMATTER_OBJECT2(nfo), (int32_t)Z_LVAL_P(number), result, &INTL_DATA_ERROR_CODE(nfo));
75+
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
76+
}
77+
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
6578
if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
6679
intl_error_reset(INTL_DATA_ERROR_P(nfo));
6780
formatted = eumalloc(formatted_len);
68-
formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
69-
formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
81+
if (FORMATTER_OBJECT(nfo)) {
82+
formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
83+
formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
84+
} else {
85+
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
86+
}
7087
if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
7188
efree(formatted);
7289
}
7390
}
74-
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
7591
break;
7692

7793
case FORMAT_TYPE_INT64:
7894
{
7995
int64_t value = (Z_TYPE_P(number) == IS_DOUBLE)?(int64_t)Z_DVAL_P(number):Z_LVAL_P(number);
80-
formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
96+
if (FORMATTER_OBJECT(nfo)) {
97+
formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
98+
} else {
99+
unumf_formatInt(FORMATTER_OBJECT2(nfo), value, result, &INTL_DATA_ERROR_CODE(nfo));
100+
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
101+
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
102+
}
81103
if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
82104
intl_error_reset(INTL_DATA_ERROR_P(nfo));
83105
formatted = eumalloc(formatted_len);
84-
formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
106+
if (FORMATTER_OBJECT(nfo)) {
107+
formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
108+
} else {
109+
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
110+
}
85111
if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
86112
efree(formatted);
87113
}
88114
}
89-
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
115+
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting to string failed" );
90116
}
91117
break;
92118

93119
case FORMAT_TYPE_DOUBLE:
94120
convert_to_double(number);
95-
formatted_len = unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
121+
if (FORMATTER_OBJECT(nfo)) {
122+
formatted_len = unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
123+
} else {
124+
unumf_formatDouble(FORMATTER_OBJECT2(nfo), Z_DVAL_P(number), result, &INTL_DATA_ERROR_CODE(nfo));
125+
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
126+
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
127+
}
96128
if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
97129
intl_error_reset(INTL_DATA_ERROR_P(nfo));
98130
formatted = eumalloc(formatted_len);
99-
unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
131+
if (FORMATTER_OBJECT(nfo)) {
132+
unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
133+
} else {
134+
formatted_len = unumf_resultToString(result, formatted, formatted_len, &INTL_DATA_ERROR_CODE(nfo));
135+
}
100136
if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
101137
efree(formatted);
102138
}
103139
}
104-
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
140+
INTL_METHOD_CHECK_STATUS( nfo, "Number formatting to string failed" );
105141
break;
106142
case FORMAT_TYPE_CURRENCY:
107143
if (getThis()) {
@@ -119,6 +155,8 @@ PHP_FUNCTION( numfmt_format )
119155
}
120156

121157
INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );
158+
159+
unumf_closeResult(result);
122160
}
123161
/* }}} */
124162

ext/intl/formatter/formatter_main.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <config.h>
1717
#endif
1818

19+
#include <unicode/ucurr.h>
20+
#include <unicode/unumberformatter.h>
1921
#include <unicode/ustring.h>
2022
#include <unicode/uloc.h>
2123

@@ -30,13 +32,14 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_error_handling *error_
3032
char* pattern = NULL;
3133
size_t locale_len = 0, pattern_len = 0;
3234
zend_long style;
35+
zend_string *sstyle;
3336
UChar* spattern = NULL;
3437
int32_t spattern_len = 0;
3538
FORMATTER_METHOD_INIT_VARS;
3639

3740
ZEND_PARSE_PARAMETERS_START(2, 3)
3841
Z_PARAM_STRING(locale, locale_len)
39-
Z_PARAM_LONG(style)
42+
Z_PARAM_STR_OR_LONG(sstyle, style)
4043
Z_PARAM_OPTIONAL
4144
Z_PARAM_STRING_OR_NULL(pattern, pattern_len)
4245
ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
@@ -70,13 +73,24 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_error_handling *error_
7073
}
7174

7275
/* Create an ICU number formatter. */
73-
FORMATTER_OBJECT(nfo) = unum_open(style, spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
76+
if (!sstyle) {
77+
FORMATTER_OBJECT(nfo) = unum_open(style, spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
7478

75-
if(spattern) {
76-
efree(spattern);
79+
if(spattern) {
80+
efree(spattern);
81+
}
82+
83+
INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: number formatter creation failed");
84+
} else {
85+
UChar *bstyle = 0;
86+
int32_t blen = 0;
87+
intl_convert_utf8_to_utf16(&bstyle, &blen, ZSTR_VAL(sstyle), ZSTR_LEN(sstyle), &INTL_DATA_ERROR_CODE(nfo));
88+
INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: error converting style to UTF-16");
89+
FORMATTER_OBJECT2(nfo) = unumf_openForSkeletonAndLocale(bstyle, blen, locale, &INTL_DATA_ERROR_CODE(nfo));
90+
efree(bstyle);
91+
INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: new number formatter failed");
7792
}
7893

79-
INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: number formatter creation failed");
8094
return SUCCESS;
8195
}
8296
/* }}} */

ext/intl/tests/gh17006.phpt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
NumberFormatter::format using new API
3+
--EXTENSIONS--
4+
intl
5+
--FILE--
6+
<?php
7+
$number = 1_000_000;
8+
$oldf = new \NumberFormatter('en_US', \NumberFormatter::SPELLOUT);
9+
$oldv = $oldf->format($number);
10+
var_dump($oldv);
11+
$newf = new \NumberFormatter('en_US', "compact-long");
12+
$newv = $newf->format($number);
13+
var_dump($newv);
14+
?>
15+
--EXPECT--
16+
string(11) "one million"
17+
string(9) "1 million"

0 commit comments

Comments
 (0)