ext/intl: Refactor ResourceBundle get and dimension access (#13503)

This commit is contained in:
Gina Peter Banyard 2024-02-26 17:03:17 +00:00 committed by GitHub
parent 23844538d1
commit dbf0b6aa42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 178 additions and 53 deletions

7
NEWS
View file

@ -62,6 +62,13 @@ PHP NEWS
. Added IntlDateFormatter::getIanaID/intltz_get_iana_id method/function. . Added IntlDateFormatter::getIanaID/intltz_get_iana_id method/function.
(David Carlier) (David Carlier)
. Set to C++17 standard for icu 74 and onwards. (David Carlier) . Set to C++17 standard for icu 74 and onwards. (David Carlier)
. resourcebundle_get(), ResourceBundle::get(), and accessing offsets on a
ResourceBundle object now throw:
- TypeError for invalid offset types
- ValueError for an empty string
- ValueError if the integer index does not fit in a signed 32 bit integer
. ResourceBundle::get() now has a tentative return type of:
ResourceBundle|array|string|int|null
- LDAP: - LDAP:
. Added LDAP_OPT_X_TLS_PROTOCOL_MAX/LDAP_OPT_X_TLS_PROTOCOL_TLS1_3 . Added LDAP_OPT_X_TLS_PROTOCOL_MAX/LDAP_OPT_X_TLS_PROTOCOL_TLS1_3

View file

@ -45,6 +45,13 @@ PHP 8.4 UPGRADE NOTES
object. This is no longer possible, and cloning a DOMXPath object now throws object. This is no longer possible, and cloning a DOMXPath object now throws
an error. an error.
- Intl:
. resourcebundle_get(), ResourceBundle::get(), and accessing offsets on a
ResourceBundle object now throw:
- TypeError for invalid offset types
- ValueError for an empty string
- ValueError if the integer index does not fit in a signed 32 bit integer
- MBString: - MBString:
. mb_encode_numericentity() and mb_decode_numericentity() now check that . mb_encode_numericentity() and mb_decode_numericentity() now check that
the $map is only composed of integers, if not a ValueError is thrown. the $map is only composed of integers, if not a ValueError is thrown.
@ -321,6 +328,8 @@ PHP 8.4 UPGRADE NOTES
RFC: https://wiki.php.net/rfc/new_rounding_modes_to_round_function RFC: https://wiki.php.net/rfc/new_rounding_modes_to_round_function
. NumberFormatter::ROUND_HALFODD added to complement existing . NumberFormatter::ROUND_HALFODD added to complement existing
NumberFormatter::ROUND_HALFEVEN functionality. NumberFormatter::ROUND_HALFEVEN functionality.
. ResourceBundle::get() now has a tentative return type of:
ResourceBundle|array|string|int|null
- MBString: - MBString:
. The behavior of mb_strcut is more consistent now on invalid UTF-8 and UTF-16 . The behavior of mb_strcut is more consistent now on invalid UTF-8 and UTF-16

View file

@ -543,8 +543,7 @@ function normalizer_get_raw_decomposition(string $string, int $form = Normalizer
function resourcebundle_create(?string $locale, ?string $bundle, bool $fallback = true): ?ResourceBundle {} function resourcebundle_create(?string $locale, ?string $bundle, bool $fallback = true): ?ResourceBundle {}
/** @param string|int $index */ function resourcebundle_get(ResourceBundle $bundle, string|int $index, bool $fallback = true): ResourceBundle|array|string|int|null {}
function resourcebundle_get(ResourceBundle $bundle, $index, bool $fallback = true): mixed {}
function resourcebundle_count(ResourceBundle $bundle): int {} function resourcebundle_count(ResourceBundle $bundle): int {}

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead. /* This is a generated file, edit the .stub.php file instead.
* Stub hash: 50cc9edef7f40e6885be38be25a71f66d7405a50 */ * Stub hash: c1adcd8a928af82766ee8c0cd6b20c2f2e9afcc1 */
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_intlcal_create_instance, 0, 0, IntlCalendar, 1) ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_intlcal_create_instance, 0, 0, IntlCalendar, 1)
ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, timezone, "null") ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, timezone, "null")
@ -630,9 +630,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_resourcebundle_create, 0, 2, Reso
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fallback, _IS_BOOL, 0, "true") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fallback, _IS_BOOL, 0, "true")
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_resourcebundle_get, 0, 2, IS_MIXED, 0) ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_resourcebundle_get, 0, 2, ResourceBundle, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_NULL)
ZEND_ARG_OBJ_INFO(0, bundle, ResourceBundle, 0) ZEND_ARG_OBJ_INFO(0, bundle, ResourceBundle, 0)
ZEND_ARG_INFO(0, index) ZEND_ARG_TYPE_MASK(0, index, MAY_BE_STRING|MAY_BE_LONG, NULL)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fallback, _IS_BOOL, 0, "true") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fallback, _IS_BOOL, 0, "true")
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()

View file

@ -71,11 +71,7 @@ void resourcebundle_extract_value( zval *return_value, ResourceBundle_object *so
source->child = NULL; source->child = NULL;
intl_errors_reset(INTL_DATA_ERROR_P(source)); intl_errors_reset(INTL_DATA_ERROR_P(source));
break; break;
EMPTY_SWITCH_DEFAULT_CASE();
default:
intl_errors_set(INTL_DATA_ERROR_P(source), U_ILLEGAL_ARGUMENT_ERROR, "Unknown resource type", 0);
RETURN_FALSE;
break;
} }
} }
/* }}} */ /* }}} */

View file

@ -13,12 +13,8 @@ class ResourceBundle implements IteratorAggregate, Countable
*/ */
public static function create(?string $locale, ?string $bundle, bool $fallback = true): ?ResourceBundle {} public static function create(?string $locale, ?string $bundle, bool $fallback = true): ?ResourceBundle {}
/** /** @tentative-return-type */
* @param string|int $index public function get(string|int $index, bool $fallback = true): ResourceBundle|array|string|int|null {}
* @tentative-return-type
* @alias resourcebundle_get
*/
public function get($index, bool $fallback = true): mixed {}
/** /**
* @tentative-return-type * @tentative-return-type

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead. /* This is a generated file, edit the .stub.php file instead.
* Stub hash: 7816536650d8513ef6998233096b0bf6a29d7af4 */ * Stub hash: e302e5ca1abcb9b52e3c14abbd38b9e8f1461390 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ResourceBundle___construct, 0, 0, 2) ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ResourceBundle___construct, 0, 0, 2)
ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 1)
@ -13,8 +13,8 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_ResourceBundle_cr
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fallback, _IS_BOOL, 0, "true") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fallback, _IS_BOOL, 0, "true")
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ResourceBundle_get, 0, 1, IS_MIXED, 0) ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_ResourceBundle_get, 0, 1, ResourceBundle, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_NULL)
ZEND_ARG_INFO(0, index) ZEND_ARG_TYPE_MASK(0, index, MAY_BE_STRING|MAY_BE_LONG, NULL)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fallback, _IS_BOOL, 0, "true") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fallback, _IS_BOOL, 0, "true")
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
@ -35,7 +35,7 @@ ZEND_END_ARG_INFO()
ZEND_METHOD(ResourceBundle, __construct); ZEND_METHOD(ResourceBundle, __construct);
ZEND_FUNCTION(resourcebundle_create); ZEND_FUNCTION(resourcebundle_create);
ZEND_FUNCTION(resourcebundle_get); ZEND_METHOD(ResourceBundle, get);
ZEND_FUNCTION(resourcebundle_count); ZEND_FUNCTION(resourcebundle_count);
ZEND_FUNCTION(resourcebundle_locales); ZEND_FUNCTION(resourcebundle_locales);
ZEND_FUNCTION(resourcebundle_get_error_code); ZEND_FUNCTION(resourcebundle_get_error_code);
@ -45,7 +45,7 @@ ZEND_METHOD(ResourceBundle, getIterator);
static const zend_function_entry class_ResourceBundle_methods[] = { static const zend_function_entry class_ResourceBundle_methods[] = {
ZEND_ME(ResourceBundle, __construct, arginfo_class_ResourceBundle___construct, ZEND_ACC_PUBLIC) ZEND_ME(ResourceBundle, __construct, arginfo_class_ResourceBundle___construct, ZEND_ACC_PUBLIC)
ZEND_RAW_FENTRY("create", zif_resourcebundle_create, arginfo_class_ResourceBundle_create, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL) ZEND_RAW_FENTRY("create", zif_resourcebundle_create, arginfo_class_ResourceBundle_create, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
ZEND_RAW_FENTRY("get", zif_resourcebundle_get, arginfo_class_ResourceBundle_get, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(ResourceBundle, get, arginfo_class_ResourceBundle_get, ZEND_ACC_PUBLIC)
ZEND_RAW_FENTRY("count", zif_resourcebundle_count, arginfo_class_ResourceBundle_count, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("count", zif_resourcebundle_count, arginfo_class_ResourceBundle_count, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_RAW_FENTRY("getLocales", zif_resourcebundle_locales, arginfo_class_ResourceBundle_getLocales, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL) ZEND_RAW_FENTRY("getLocales", zif_resourcebundle_locales, arginfo_class_ResourceBundle_getLocales, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
ZEND_RAW_FENTRY("getErrorCode", zif_resourcebundle_get_error_code, arginfo_class_ResourceBundle_getErrorCode, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getErrorCode", zif_resourcebundle_get_error_code, arginfo_class_ResourceBundle_getErrorCode, ZEND_ACC_PUBLIC, NULL, NULL)

View file

@ -168,84 +168,134 @@ PHP_FUNCTION( resourcebundle_create )
/* }}} */ /* }}} */
/* {{{ resourcebundle_array_fetch */ /* {{{ resourcebundle_array_fetch */
static void resourcebundle_array_fetch(zend_object *object, zval *offset, zval *return_value, int fallback) static zval *resource_bundle_array_fetch(
zend_object *object, zend_string *offset_str, zend_long offset_int,
zval *return_value, bool fallback, uint32_t offset_arg_num)
{ {
int32_t meindex = 0; int32_t index = 0;
char * mekey = NULL; char *key = NULL;
bool is_numeric = 0; bool is_numeric = offset_str == NULL;
char *pbuf; char *pbuf;
ResourceBundle_object *rb; ResourceBundle_object *rb;
rb = php_intl_resourcebundle_fetch_object(object); rb = php_intl_resourcebundle_fetch_object(object);
intl_error_reset(NULL); intl_error_reset(NULL);
intl_error_reset(INTL_DATA_ERROR_P(rb)); intl_error_reset(INTL_DATA_ERROR_P(rb));
if(Z_TYPE_P(offset) == IS_LONG) { if (offset_str) {
is_numeric = 1; if (UNEXPECTED(ZSTR_LEN(offset_str) == 0)) {
meindex = (int32_t)Z_LVAL_P(offset); if (offset_arg_num) {
rb->child = ures_getByIndex( rb->me, meindex, rb->child, &INTL_DATA_ERROR_CODE(rb) ); zend_argument_value_error(offset_arg_num, "cannot be empty");
} else if(Z_TYPE_P(offset) == IS_STRING) { } else {
mekey = Z_STRVAL_P(offset); zend_value_error("Offset cannot be empty");
rb->child = ures_getByKey(rb->me, mekey, rb->child, &INTL_DATA_ERROR_CODE(rb) ); }
return NULL;
}
key = ZSTR_VAL(offset_str);
rb->child = ures_getByKey(rb->me, key, rb->child, &INTL_DATA_ERROR_CODE(rb) );
} else { } else {
intl_errors_set(INTL_DATA_ERROR_P(rb), U_ILLEGAL_ARGUMENT_ERROR, if (UNEXPECTED(offset_int < (zend_long)INT32_MIN || offset_int > (zend_long)INT32_MAX)) {
"resourcebundle_get: index should be integer or string", 0); if (offset_arg_num) {
RETURN_NULL(); zend_argument_value_error(offset_arg_num, "index must be between %d and %d", INT32_MIN, INT32_MAX);
} else {
zend_value_error("Index must be between %d and %d", INT32_MIN, INT32_MAX);
}
return NULL;
}
index = (int32_t)offset_int;
rb->child = ures_getByIndex(rb->me, index, rb->child, &INTL_DATA_ERROR_CODE(rb));
} }
intl_error_set_code( NULL, INTL_DATA_ERROR_CODE(rb) ); intl_error_set_code( NULL, INTL_DATA_ERROR_CODE(rb) );
if (U_FAILURE(INTL_DATA_ERROR_CODE(rb))) { if (U_FAILURE(INTL_DATA_ERROR_CODE(rb))) {
if (is_numeric) { if (is_numeric) {
spprintf( &pbuf, 0, "Cannot load resource element %d", meindex ); spprintf( &pbuf, 0, "Cannot load resource element %d", index );
} else { } else {
spprintf( &pbuf, 0, "Cannot load resource element '%s'", mekey ); spprintf( &pbuf, 0, "Cannot load resource element '%s'", key );
} }
intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 ); intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 );
efree(pbuf); efree(pbuf);
RETURN_NULL(); RETVAL_NULL();
return return_value;
} }
if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) { if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) {
UErrorCode icuerror; UErrorCode icuerror;
const char * locale = ures_getLocaleByType( rb->me, ULOC_ACTUAL_LOCALE, &icuerror ); const char * locale = ures_getLocaleByType( rb->me, ULOC_ACTUAL_LOCALE, &icuerror );
if (is_numeric) { if (is_numeric) {
spprintf( &pbuf, 0, "Cannot load element %d without fallback from to %s", meindex, locale ); spprintf(&pbuf, 0, "Cannot load element %d without fallback from to %s", index, locale);
} else { } else {
spprintf( &pbuf, 0, "Cannot load element '%s' without fallback from to %s", mekey, locale ); spprintf(&pbuf, 0, "Cannot load element '%s' without fallback from to %s", key, locale);
} }
intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 ); intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1);
efree(pbuf); efree(pbuf);
RETURN_NULL(); RETVAL_NULL();
return return_value;
} }
resourcebundle_extract_value( return_value, rb ); resourcebundle_extract_value( return_value, rb );
return return_value;
} }
/* }}} */ /* }}} */
/* {{{ resourcebundle_array_get */ /* {{{ resourcebundle_array_get */
zval *resourcebundle_array_get(zend_object *object, zval *offset, int type, zval *rv) zval *resourcebundle_array_get(zend_object *object, zval *offset, int type, zval *rv)
{ {
if(offset == NULL) { if (offset == NULL) {
php_error( E_ERROR, "Cannot apply [] to ResourceBundle object" ); zend_throw_error(NULL, "Cannot apply [] to ResourceBundle object");
return NULL;
}
ZVAL_DEREF(offset);
if (Z_TYPE_P(offset) == IS_LONG) {
return resource_bundle_array_fetch(object, /* offset_str */ NULL, Z_LVAL_P(offset), rv, /* fallback */ true, /* arg_num */ 0);
} else if (Z_TYPE_P(offset) == IS_STRING) {
return resource_bundle_array_fetch(object, Z_STR_P(offset), /* offset_int */ 0, rv, /* fallback */ true, /* arg_num */ 0);
} else {
zend_illegal_container_offset(object->ce->name, offset, type);
return NULL;
} }
ZVAL_NULL(rv);
resourcebundle_array_fetch(object, offset, rv, 1);
return rv;
} }
/* }}} */ /* }}} */
/* {{{ Get resource identified by numerical index or key name. */ /* {{{ Get resource identified by numerical index or key name. */
PHP_FUNCTION( resourcebundle_get ) PHP_FUNCTION( resourcebundle_get )
{ {
bool fallback = 1; bool fallback = true;
zval * offset; zend_object *resource_bundle = NULL;
zval * object; zend_string *offset_str = NULL;
zend_long offset_long = 0;
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oz|b", &object, ResourceBundle_ce_ptr, &offset, &fallback ) == FAILURE) { ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_OBJ_OF_CLASS(resource_bundle, ResourceBundle_ce_ptr)
Z_PARAM_STR_OR_LONG(offset_str, offset_long)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(fallback)
ZEND_PARSE_PARAMETERS_END();
zval *retval = resource_bundle_array_fetch(resource_bundle, offset_str, offset_long, return_value, fallback, /* arg_num */ 2);
if (!retval) {
RETURN_THROWS(); RETURN_THROWS();
} }
}
/* }}} */
resourcebundle_array_fetch(Z_OBJ_P(object), offset, return_value, fallback); PHP_METHOD(ResourceBundle , get)
{
bool fallback = true;
zend_string *offset_str = NULL;
zend_long offset_long = 0;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR_OR_LONG(offset_str, offset_long)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(fallback)
ZEND_PARSE_PARAMETERS_END();
zval *retval = resource_bundle_array_fetch(Z_OBJ_P(ZEND_THIS), offset_str, offset_long, return_value, fallback, /* arg_num */ 1);
if (!retval) {
RETURN_THROWS();
}
} }
/* }}} */ /* }}} */

View file

@ -23,6 +23,11 @@ intl
$r2 = $r['testarray']; $r2 = $r['testarray'];
printf( "testarray: %s\n", $r2[2] ); printf( "testarray: %s\n", $r2[2] );
echo "Using a reference as an offset:\n";
$offset = 'teststring';
$ref = &$offset;
var_dump($r[$ref]);
$t = $r['nonexisting']; $t = $r['nonexisting'];
echo debug( $t ); echo debug( $t );
?> ?>
@ -46,5 +51,7 @@ Array
testbin: a1b2c3d4e5f67890 testbin: a1b2c3d4e5f67890
testtable: 3 testtable: 3
testarray: string 3 testarray: string 3
Using a reference as an offset:
string(12) "Hello World!"
NULL NULL
2: Cannot load resource element 'nonexisting': U_MISSING_RESOURCE_ERROR 2: Cannot load resource element 'nonexisting': U_MISSING_RESOURCE_ERROR

View file

@ -0,0 +1,61 @@
--TEST--
Test ResourceBundle errors with []
--EXTENSIONS--
intl
--FILE--
<?php
include "resourcebundle.inc";
// fall back
$r = new ResourceBundle( 'en_US', BUNDLE );
try {
$ref = &$r[];
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
try {
var_dump(isset($r['non-existent']));
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
try {
var_dump($r['non-existent'] ?? "default");
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
try {
var_dump($r[12.5]);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
try {
var_dump($r[new stdClass()]);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
try {
var_dump($r['']);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
try {
/* This can only happen on 64bit */
if (PHP_INT_SIZE > 4) {
var_dump($r[0xFFFFFFFFF]);
} else {
echo 'ValueError: Index must be between -2147483648 and 2147483647', PHP_EOL;
}
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
Error: Cannot apply [] to ResourceBundle object
Error: Cannot use object of type ResourceBundle as array
string(7) "default"
TypeError: Cannot access offset of type float on ResourceBundle
TypeError: Cannot access offset of type stdClass on ResourceBundle
ValueError: Offset cannot be empty
ValueError: Index must be between -2147483648 and 2147483647