From 045d005f80934d4a122145d1afef767f7b5039e5 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 25 Aug 2026 22:51:37 +0100 Subject: [PATCH 1/2] mysqli: move behaviour existing only for mysqli_fetch_object() into it (#23451) The shared function is effectively ignored for mysqli_fetch_object() so just move the relevant behaviour and simplify the common implementation --- ext/mysqli/mysqli.c | 64 +++++++------------------------------- ext/mysqli/mysqli_api.c | 2 +- ext/mysqli/mysqli_nonapi.c | 51 ++++++++++++++++++++++++++++-- ext/mysqli/mysqli_priv.h | 2 +- 4 files changed, 61 insertions(+), 58 deletions(-) diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 2f1fa1c21c55..e6f876433571 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -747,72 +747,30 @@ void php_mysqli_fetch_into_hash_aux(zval *return_value, MYSQL_RES * result, zend /* TODO Split this up */ /* {{{ php_mysqli_fetch_into_hash */ -void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags, int into_object) +void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags) { MYSQL_RES *result; zval *mysql_result; zend_long fetchtype; - HashTable *ctor_params = NULL; - zend_class_entry *ce = NULL; - if (into_object) { - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|Ch", &mysql_result, mysqli_result_class_entry, &ce, &ctor_params) == FAILURE) { + if (override_flags) { + ZEND_ASSERT(override_flags >= MYSQLI_ASSOC && override_flags <= MYSQLI_BOTH); + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) { RETURN_THROWS(); } - if (ce == NULL) { - ce = zend_standard_class_def; - } - if (UNEXPECTED(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) { - zend_throw_error(NULL, "Class %s cannot be instantiated", ZSTR_VAL(ce->name)); + fetchtype = override_flags; + } else { + fetchtype = MYSQLI_BOTH; + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &fetchtype) == FAILURE) { RETURN_THROWS(); } - fetchtype = MYSQLI_ASSOC; - } else { - if (override_flags) { - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) { - RETURN_THROWS(); - } - fetchtype = override_flags; - } else { - fetchtype = MYSQLI_BOTH; - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &fetchtype) == FAILURE) { - RETURN_THROWS(); - } + if (fetchtype < MYSQLI_ASSOC || fetchtype > MYSQLI_BOTH) { + zend_argument_value_error(ERROR_ARG_POS(2), "must be one of MYSQLI_NUM, MYSQLI_ASSOC, or MYSQLI_BOTH"); + RETURN_THROWS(); } } MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, mysql_result, MYSQLI_STATUS_VALID); - if (fetchtype < MYSQLI_ASSOC || fetchtype > MYSQLI_BOTH) { - zend_argument_value_error(ERROR_ARG_POS(2), "must be one of MYSQLI_NUM, MYSQLI_ASSOC, or MYSQLI_BOTH"); - RETURN_THROWS(); - } - php_mysqli_fetch_into_hash_aux(return_value, result, fetchtype); - - if (into_object && Z_TYPE_P(return_value) == IS_ARRAY) { - zval dataset; - - ZVAL_COPY_VALUE(&dataset, return_value); - - object_init_ex(return_value, ce); - HashTable *prop_table = zend_symtable_to_proptable(Z_ARR(dataset)); - zval_ptr_dtor(&dataset); - if (!ce->default_properties_count && !ce->__set) { - Z_OBJ_P(return_value)->properties = prop_table; - } else { - zend_merge_properties(return_value, prop_table); - zend_array_release(prop_table); - } - - if (ce->constructor) { - zend_call_known_function(ce->constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), - /* retval */ NULL, /* argc */ 0, /* params */ NULL, ctor_params); - } else if (ctor_params && zend_hash_num_elements(ctor_params) > 0) { - zend_argument_value_error(ERROR_ARG_POS(3), - "must be empty when the specified class (%s) does not have a constructor", - ZSTR_VAL(ce->name) - ); - } - } } /* }}} */ diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 526fd10b2623..e55083d940cf 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -780,7 +780,7 @@ PHP_FUNCTION(mysqli_fetch_lengths) /* {{{ Get a result row as an enumerated array */ PHP_FUNCTION(mysqli_fetch_row) { - php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_NUM, 0); + php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_NUM); } /* }}} */ diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index 1e46aeedd93b..1b973f405497 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -363,14 +363,14 @@ PHP_FUNCTION(mysqli_connect_error) /* {{{ Fetch a result row as an associative array, a numeric array, or both */ PHP_FUNCTION(mysqli_fetch_array) { - php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); + php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Fetch a result row as an associative array */ PHP_FUNCTION(mysqli_fetch_assoc) { - php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 0); + php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC); } /* }}} */ @@ -525,7 +525,52 @@ PHP_FUNCTION(mysqli_stmt_error_list) /* {{{ Fetch a result row as an object */ PHP_FUNCTION(mysqli_fetch_object) { - php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 1); + zval *mysql_result; + zend_class_entry *ce = NULL; + HashTable *ctor_params = NULL; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|Ch", &mysql_result, mysqli_result_class_entry, &ce, &ctor_params) == FAILURE) { + RETURN_THROWS(); + } + if (ce == NULL) { + ce = zend_standard_class_def; + } + if (UNEXPECTED(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) { + zend_throw_error(NULL, "Class %s cannot be instantiated", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + if (!ce->constructor && ctor_params && zend_hash_num_elements(ctor_params) > 0) { + zend_argument_value_error(ERROR_ARG_POS(3), + "must be empty when the specified class (%s) does not have a constructor", + ZSTR_VAL(ce->name) + ); + RETURN_THROWS(); + } + + MYSQL_RES *result; + MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, mysql_result, MYSQLI_STATUS_VALID); + + zval dataset; + php_mysqli_fetch_into_hash_aux(&dataset, result, MYSQLI_ASSOC); + + if (Z_TYPE(dataset) == IS_ARRAY) { + object_init_ex(return_value, ce); + HashTable *prop_table = zend_symtable_to_proptable(Z_ARR(dataset)); + zval_ptr_dtor(&dataset); + if (!ce->default_properties_count && !ce->__set) { + Z_OBJ_P(return_value)->properties = prop_table; + } else { + zend_merge_properties(return_value, prop_table); + zend_array_release(prop_table); + } + + if (ce->constructor) { + zend_call_known_function(ce->constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), + /* retval */ NULL, /* argc */ 0, /* params */ NULL, ctor_params); + } + } else { + RETURN_COPY_VALUE(&dataset); + } } /* }}} */ diff --git a/ext/mysqli/mysqli_priv.h b/ext/mysqli/mysqli_priv.h index 97d9300a2b79..32602ef94989 100644 --- a/ext/mysqli/mysqli_priv.h +++ b/ext/mysqli/mysqli_priv.h @@ -47,7 +47,7 @@ extern void php_mysqli_dtor_p_elements(void *data); extern void php_mysqli_close(MY_MYSQL * mysql, int close_type, int resource_status); -extern void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flag, int into_object); +extern void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flag); extern void php_clear_stmt_bind(MY_STMT *stmt); extern void php_clear_mysql(MY_MYSQL *); extern MYSQLI_WARNING *php_get_warnings(MYSQLND_CONN_DATA * mysql); From b2956e0bb326913d91bfa22e1059896c6bd7c6b9 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 17 Aug 2026 20:40:15 -0400 Subject: [PATCH 2/2] Close the keyword UEnumeration on Locale::getKeywords failure uloc_getKeywordValue failure destroyed the result array and returned without uenum_close(). Close the enumeration on that path. The success path already closes it. Audited the other uenum_close site in this file (acceptLanguage). Closes GH-23351 --- NEWS | 2 ++ ext/intl/locale/locale_methods.c | 1 + .../tests/locale_get_keywords_failure.phpt | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 ext/intl/tests/locale_get_keywords_failure.phpt diff --git a/NEWS b/NEWS index 5607f31081b8..e56e562a4c92 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,8 @@ PHP NEWS . Fixed Locale::parseLocale() reading past a trailing '-' or '_'. (iliaal, Xuyang Zhang) . Fixed grapheme_str_split() treating UBRK_DONE as a byte index. (iliaal) + . Fixed a leak in Locale::getKeywords() when a keyword value cannot be + read. (iliaal) - Opcache: . Fixed opcache.protect_memory race under ZTS. (realFlowControl) diff --git a/ext/intl/locale/locale_methods.c b/ext/intl/locale/locale_methods.c index e3894b6f28fb..2643f4920878 100644 --- a/ext/intl/locale/locale_methods.c +++ b/ext/intl/locale/locale_methods.c @@ -776,6 +776,7 @@ PHP_FUNCTION( locale_get_keywords ) zend_string_efree( kw_value_str ); } zend_array_destroy(Z_ARR_P(return_value)); + uenum_close( e ); RETURN_FALSE; } diff --git a/ext/intl/tests/locale_get_keywords_failure.phpt b/ext/intl/tests/locale_get_keywords_failure.phpt new file mode 100644 index 000000000000..823da63ca541 --- /dev/null +++ b/ext/intl/tests/locale_get_keywords_failure.phpt @@ -0,0 +1,18 @@ +--TEST-- +Locale::getKeywords() closes the keyword enumeration on failure +--EXTENSIONS-- +intl +--SKIPIF-- += 59.1'); +} +?> +--FILE-- + +--EXPECT-- +bool(false) +bool(true)