Skip to content

Commit dbf0b6a

Browse files
authored
ext/intl: Refactor ResourceBundle get and dimension access (#13503)
1 parent 2384453 commit dbf0b6a

10 files changed

+179
-54
lines changed

NEWS

+7
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ PHP NEWS
6262
. Added IntlDateFormatter::getIanaID/intltz_get_iana_id method/function.
6363
(David Carlier)
6464
. Set to C++17 standard for icu 74 and onwards. (David Carlier)
65+
. resourcebundle_get(), ResourceBundle::get(), and accessing offsets on a
66+
ResourceBundle object now throw:
67+
- TypeError for invalid offset types
68+
- ValueError for an empty string
69+
- ValueError if the integer index does not fit in a signed 32 bit integer
70+
. ResourceBundle::get() now has a tentative return type of:
71+
ResourceBundle|array|string|int|null
6572

6673
- LDAP:
6774
. Added LDAP_OPT_X_TLS_PROTOCOL_MAX/LDAP_OPT_X_TLS_PROTOCOL_TLS1_3

UPGRADING

+9
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ PHP 8.4 UPGRADE NOTES
4545
object. This is no longer possible, and cloning a DOMXPath object now throws
4646
an error.
4747

48+
- Intl:
49+
. resourcebundle_get(), ResourceBundle::get(), and accessing offsets on a
50+
ResourceBundle object now throw:
51+
- TypeError for invalid offset types
52+
- ValueError for an empty string
53+
- ValueError if the integer index does not fit in a signed 32 bit integer
54+
4855
- MBString:
4956
. mb_encode_numericentity() and mb_decode_numericentity() now check that
5057
the $map is only composed of integers, if not a ValueError is thrown.
@@ -321,6 +328,8 @@ PHP 8.4 UPGRADE NOTES
321328
RFC: https://wiki.php.net/rfc/new_rounding_modes_to_round_function
322329
. NumberFormatter::ROUND_HALFODD added to complement existing
323330
NumberFormatter::ROUND_HALFEVEN functionality.
331+
. ResourceBundle::get() now has a tentative return type of:
332+
ResourceBundle|array|string|int|null
324333

325334
- MBString:
326335
. The behavior of mb_strcut is more consistent now on invalid UTF-8 and UTF-16

ext/intl/php_intl.stub.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,7 @@ function normalizer_get_raw_decomposition(string $string, int $form = Normalizer
543543

544544
function resourcebundle_create(?string $locale, ?string $bundle, bool $fallback = true): ?ResourceBundle {}
545545

546-
/** @param string|int $index */
547-
function resourcebundle_get(ResourceBundle $bundle, $index, bool $fallback = true): mixed {}
546+
function resourcebundle_get(ResourceBundle $bundle, string|int $index, bool $fallback = true): ResourceBundle|array|string|int|null {}
548547

549548
function resourcebundle_count(ResourceBundle $bundle): int {}
550549

ext/intl/php_intl_arginfo.h

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/intl/resourcebundle/resourcebundle.c

+1-5
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,7 @@ void resourcebundle_extract_value( zval *return_value, ResourceBundle_object *so
7171
source->child = NULL;
7272
intl_errors_reset(INTL_DATA_ERROR_P(source));
7373
break;
74-
75-
default:
76-
intl_errors_set(INTL_DATA_ERROR_P(source), U_ILLEGAL_ARGUMENT_ERROR, "Unknown resource type", 0);
77-
RETURN_FALSE;
78-
break;
74+
EMPTY_SWITCH_DEFAULT_CASE();
7975
}
8076
}
8177
/* }}} */

ext/intl/resourcebundle/resourcebundle.stub.php

+2-6
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,8 @@ public function __construct(?string $locale, ?string $bundle, bool $fallback = t
1313
*/
1414
public static function create(?string $locale, ?string $bundle, bool $fallback = true): ?ResourceBundle {}
1515

16-
/**
17-
* @param string|int $index
18-
* @tentative-return-type
19-
* @alias resourcebundle_get
20-
*/
21-
public function get($index, bool $fallback = true): mixed {}
16+
/** @tentative-return-type */
17+
public function get(string|int $index, bool $fallback = true): ResourceBundle|array|string|int|null {}
2218

2319
/**
2420
* @tentative-return-type

ext/intl/resourcebundle/resourcebundle_arginfo.h

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/intl/resourcebundle/resourcebundle_class.c

+83-33
Original file line numberDiff line numberDiff line change
@@ -168,84 +168,134 @@ PHP_FUNCTION( resourcebundle_create )
168168
/* }}} */
169169

170170
/* {{{ resourcebundle_array_fetch */
171-
static void resourcebundle_array_fetch(zend_object *object, zval *offset, zval *return_value, int fallback)
171+
static zval *resource_bundle_array_fetch(
172+
zend_object *object, zend_string *offset_str, zend_long offset_int,
173+
zval *return_value, bool fallback, uint32_t offset_arg_num)
172174
{
173-
int32_t meindex = 0;
174-
char * mekey = NULL;
175-
bool is_numeric = 0;
176-
char *pbuf;
175+
int32_t index = 0;
176+
char *key = NULL;
177+
bool is_numeric = offset_str == NULL;
178+
char *pbuf;
177179
ResourceBundle_object *rb;
178180

179181
rb = php_intl_resourcebundle_fetch_object(object);
180182
intl_error_reset(NULL);
181183
intl_error_reset(INTL_DATA_ERROR_P(rb));
182184

183-
if(Z_TYPE_P(offset) == IS_LONG) {
184-
is_numeric = 1;
185-
meindex = (int32_t)Z_LVAL_P(offset);
186-
rb->child = ures_getByIndex( rb->me, meindex, rb->child, &INTL_DATA_ERROR_CODE(rb) );
187-
} else if(Z_TYPE_P(offset) == IS_STRING) {
188-
mekey = Z_STRVAL_P(offset);
189-
rb->child = ures_getByKey(rb->me, mekey, rb->child, &INTL_DATA_ERROR_CODE(rb) );
185+
if (offset_str) {
186+
if (UNEXPECTED(ZSTR_LEN(offset_str) == 0)) {
187+
if (offset_arg_num) {
188+
zend_argument_value_error(offset_arg_num, "cannot be empty");
189+
} else {
190+
zend_value_error("Offset cannot be empty");
191+
}
192+
return NULL;
193+
}
194+
key = ZSTR_VAL(offset_str);
195+
rb->child = ures_getByKey(rb->me, key, rb->child, &INTL_DATA_ERROR_CODE(rb) );
190196
} else {
191-
intl_errors_set(INTL_DATA_ERROR_P(rb), U_ILLEGAL_ARGUMENT_ERROR,
192-
"resourcebundle_get: index should be integer or string", 0);
193-
RETURN_NULL();
197+
if (UNEXPECTED(offset_int < (zend_long)INT32_MIN || offset_int > (zend_long)INT32_MAX)) {
198+
if (offset_arg_num) {
199+
zend_argument_value_error(offset_arg_num, "index must be between %d and %d", INT32_MIN, INT32_MAX);
200+
} else {
201+
zend_value_error("Index must be between %d and %d", INT32_MIN, INT32_MAX);
202+
}
203+
return NULL;
204+
}
205+
index = (int32_t)offset_int;
206+
rb->child = ures_getByIndex(rb->me, index, rb->child, &INTL_DATA_ERROR_CODE(rb));
194207
}
195208

196209
intl_error_set_code( NULL, INTL_DATA_ERROR_CODE(rb) );
197210
if (U_FAILURE(INTL_DATA_ERROR_CODE(rb))) {
198211
if (is_numeric) {
199-
spprintf( &pbuf, 0, "Cannot load resource element %d", meindex );
212+
spprintf( &pbuf, 0, "Cannot load resource element %d", index );
200213
} else {
201-
spprintf( &pbuf, 0, "Cannot load resource element '%s'", mekey );
214+
spprintf( &pbuf, 0, "Cannot load resource element '%s'", key );
202215
}
203216
intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 );
204217
efree(pbuf);
205-
RETURN_NULL();
218+
RETVAL_NULL();
219+
return return_value;
206220
}
207221

208222
if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) {
209223
UErrorCode icuerror;
210224
const char * locale = ures_getLocaleByType( rb->me, ULOC_ACTUAL_LOCALE, &icuerror );
211225
if (is_numeric) {
212-
spprintf( &pbuf, 0, "Cannot load element %d without fallback from to %s", meindex, locale );
226+
spprintf(&pbuf, 0, "Cannot load element %d without fallback from to %s", index, locale);
213227
} else {
214-
spprintf( &pbuf, 0, "Cannot load element '%s' without fallback from to %s", mekey, locale );
228+
spprintf(&pbuf, 0, "Cannot load element '%s' without fallback from to %s", key, locale);
215229
}
216-
intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 );
230+
intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1);
217231
efree(pbuf);
218-
RETURN_NULL();
232+
RETVAL_NULL();
233+
return return_value;
219234
}
220235

221236
resourcebundle_extract_value( return_value, rb );
237+
return return_value;
222238
}
223239
/* }}} */
224240

225241
/* {{{ resourcebundle_array_get */
226242
zval *resourcebundle_array_get(zend_object *object, zval *offset, int type, zval *rv)
227243
{
228-
if(offset == NULL) {
229-
php_error( E_ERROR, "Cannot apply [] to ResourceBundle object" );
244+
if (offset == NULL) {
245+
zend_throw_error(NULL, "Cannot apply [] to ResourceBundle object");
246+
return NULL;
247+
}
248+
249+
ZVAL_DEREF(offset);
250+
if (Z_TYPE_P(offset) == IS_LONG) {
251+
return resource_bundle_array_fetch(object, /* offset_str */ NULL, Z_LVAL_P(offset), rv, /* fallback */ true, /* arg_num */ 0);
252+
} else if (Z_TYPE_P(offset) == IS_STRING) {
253+
return resource_bundle_array_fetch(object, Z_STR_P(offset), /* offset_int */ 0, rv, /* fallback */ true, /* arg_num */ 0);
254+
} else {
255+
zend_illegal_container_offset(object->ce->name, offset, type);
256+
return NULL;
230257
}
231-
ZVAL_NULL(rv);
232-
resourcebundle_array_fetch(object, offset, rv, 1);
233-
return rv;
234258
}
235259
/* }}} */
236260

237261
/* {{{ Get resource identified by numerical index or key name. */
238262
PHP_FUNCTION( resourcebundle_get )
239263
{
240-
bool fallback = 1;
241-
zval * offset;
242-
zval * object;
243-
244-
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oz|b", &object, ResourceBundle_ce_ptr, &offset, &fallback ) == FAILURE) {
264+
bool fallback = true;
265+
zend_object *resource_bundle = NULL;
266+
zend_string *offset_str = NULL;
267+
zend_long offset_long = 0;
268+
269+
ZEND_PARSE_PARAMETERS_START(2, 3)
270+
Z_PARAM_OBJ_OF_CLASS(resource_bundle, ResourceBundle_ce_ptr)
271+
Z_PARAM_STR_OR_LONG(offset_str, offset_long)
272+
Z_PARAM_OPTIONAL
273+
Z_PARAM_BOOL(fallback)
274+
ZEND_PARSE_PARAMETERS_END();
275+
276+
zval *retval = resource_bundle_array_fetch(resource_bundle, offset_str, offset_long, return_value, fallback, /* arg_num */ 2);
277+
if (!retval) {
245278
RETURN_THROWS();
246279
}
280+
}
281+
/* }}} */
247282

248-
resourcebundle_array_fetch(Z_OBJ_P(object), offset, return_value, fallback);
283+
PHP_METHOD(ResourceBundle , get)
284+
{
285+
bool fallback = true;
286+
zend_string *offset_str = NULL;
287+
zend_long offset_long = 0;
288+
289+
ZEND_PARSE_PARAMETERS_START(1, 2)
290+
Z_PARAM_STR_OR_LONG(offset_str, offset_long)
291+
Z_PARAM_OPTIONAL
292+
Z_PARAM_BOOL(fallback)
293+
ZEND_PARSE_PARAMETERS_END();
294+
295+
zval *retval = resource_bundle_array_fetch(Z_OBJ_P(ZEND_THIS), offset_str, offset_long, return_value, fallback, /* arg_num */ 1);
296+
if (!retval) {
297+
RETURN_THROWS();
298+
}
249299
}
250300
/* }}} */
251301

ext/intl/tests/resourcebundle_arrayaccess.phpt

+7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ intl
2323
$r2 = $r['testarray'];
2424
printf( "testarray: %s\n", $r2[2] );
2525

26+
echo "Using a reference as an offset:\n";
27+
$offset = 'teststring';
28+
$ref = &$offset;
29+
var_dump($r[$ref]);
30+
2631
$t = $r['nonexisting'];
2732
echo debug( $t );
2833
?>
@@ -46,5 +51,7 @@ Array
4651
testbin: a1b2c3d4e5f67890
4752
testtable: 3
4853
testarray: string 3
54+
Using a reference as an offset:
55+
string(12) "Hello World!"
4956
NULL
5057
2: Cannot load resource element 'nonexisting': U_MISSING_RESOURCE_ERROR
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
Test ResourceBundle errors with []
3+
--EXTENSIONS--
4+
intl
5+
--FILE--
6+
<?php
7+
include "resourcebundle.inc";
8+
9+
// fall back
10+
$r = new ResourceBundle( 'en_US', BUNDLE );
11+
12+
try {
13+
$ref = &$r[];
14+
} catch (\Throwable $e) {
15+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
16+
}
17+
try {
18+
var_dump(isset($r['non-existent']));
19+
} catch (\Throwable $e) {
20+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
21+
}
22+
try {
23+
var_dump($r['non-existent'] ?? "default");
24+
} catch (\Throwable $e) {
25+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
26+
}
27+
try {
28+
var_dump($r[12.5]);
29+
} catch (\Throwable $e) {
30+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
31+
}
32+
try {
33+
var_dump($r[new stdClass()]);
34+
} catch (\Throwable $e) {
35+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
36+
}
37+
try {
38+
var_dump($r['']);
39+
} catch (\Throwable $e) {
40+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
41+
}
42+
try {
43+
/* This can only happen on 64bit */
44+
if (PHP_INT_SIZE > 4) {
45+
var_dump($r[0xFFFFFFFFF]);
46+
} else {
47+
echo 'ValueError: Index must be between -2147483648 and 2147483647', PHP_EOL;
48+
}
49+
} catch (\Throwable $e) {
50+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
51+
}
52+
53+
?>
54+
--EXPECT--
55+
Error: Cannot apply [] to ResourceBundle object
56+
Error: Cannot use object of type ResourceBundle as array
57+
string(7) "default"
58+
TypeError: Cannot access offset of type float on ResourceBundle
59+
TypeError: Cannot access offset of type stdClass on ResourceBundle
60+
ValueError: Offset cannot be empty
61+
ValueError: Index must be between -2147483648 and 2147483647

0 commit comments

Comments
 (0)