diff --git a/NEWS b/NEWS index af7d45ecd087..7dd8530af905 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,10 @@ PHP NEWS . Fixed GMP integer string parsing to reject strings containing NUL bytes instead of silently truncating them. (Weilin Du) +- Intl: + . Fixed grammatical issues in Normalizer invalid form and IntlCalendar time + zone offset error messages. (Weilin Du) + - ODBC: . Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the driver-reported display size). (iliaal) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 79d5def33823..ae58e7ba8d5b 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -145,6 +145,7 @@ PHP 8.6 INTERNALS UPGRADE NOTES (sendfile, splice, copy_file_range, TransmitFile), and is now used by php_stream_copy_to_stream_ex(). The mmap-based copy fallback was removed. . Added zend_string_equals_cstr_ci(). + . Added zend_string_ends_with() and related variants. ======================== 2. Build system changes diff --git a/Zend/tests/partial_application/bound_arg_strict_types.phpt b/Zend/tests/partial_application/bound_arg_strict_types.phpt new file mode 100644 index 000000000000..d7e2ddcae5c4 --- /dev/null +++ b/Zend/tests/partial_application/bound_arg_strict_types.phpt @@ -0,0 +1,26 @@ +--TEST-- +Partial application checks bound arguments with the creating scope's strict_types +--FILE-- +getMessage(), "\n"; +} + +function make() { + try { + f("3", ?); + } catch (\TypeError $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; + } +} +make(); +?> +--EXPECT-- +TypeError: f(): Argument #1 ($a) must be of type int, string given +TypeError: f(): Argument #1 ($a) must be of type int, string given diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index c51a55bac4c9..f5b70d74aa61 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1153,7 +1153,7 @@ static bool zend_check_intersection_type_from_list( static zend_always_inline bool zend_check_type_slow( const zend_type *type, zval *arg, const zend_reference *ref, - bool is_return_type, bool is_internal) + bool current_frame, bool is_internal) { if (ZEND_TYPE_IS_COMPLEX(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { zend_class_entry *ce; @@ -1199,7 +1199,7 @@ static zend_always_inline bool zend_check_type_slow( /* We cannot have conversions for typed refs. */ return 0; } - if (is_internal && is_return_type) { + if (is_internal && current_frame) { /* For internal returns, the type has to match exactly, because we're not * going to check it for non-debug builds, and there will be no chance to * apply coercions. */ @@ -1207,7 +1207,7 @@ static zend_always_inline bool zend_check_type_slow( } return zend_verify_scalar_type_hint(type_mask, arg, - is_return_type ? ZEND_RET_USES_STRICT_TYPES() : ZEND_ARG_USES_STRICT_TYPES(), + current_frame ? ZEND_RET_USES_STRICT_TYPES() : ZEND_ARG_USES_STRICT_TYPES(), is_internal); /* Special handling for IS_VOID is not necessary (for return types), @@ -1215,7 +1215,7 @@ static zend_always_inline bool zend_check_type_slow( } static zend_always_inline bool zend_check_type( - const zend_type *type, zval *arg, bool is_return_type, bool is_internal) + const zend_type *type, zval *arg, bool current_frame, bool is_internal) { const zend_reference *ref = NULL; ZEND_ASSERT(ZEND_TYPE_IS_SET(*type)); @@ -1229,21 +1229,21 @@ static zend_always_inline bool zend_check_type( return 1; } - return zend_check_type_slow(type, arg, ref, is_return_type, is_internal); + return zend_check_type_slow(type, arg, ref, current_frame, is_internal); } /* We can not expose zend_check_type() directly because it's inline and uses static functions */ ZEND_API bool zend_check_type_ex( - const zend_type *type, zval *arg, bool is_return_type, bool is_internal) + const zend_type *type, zval *arg, bool current_frame, bool is_internal) { - return zend_check_type(type, arg, is_return_type, is_internal); + return zend_check_type(type, arg, current_frame, is_internal); } ZEND_API bool zend_check_user_type_slow( - const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type) + const zend_type *type, zval *arg, const zend_reference *ref, bool current_frame) { return zend_check_type_slow( - type, arg, ref, is_return_type, /* is_internal */ false); + type, arg, ref, current_frame, /* is_internal */ false); } static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf, uint32_t arg_num, zval *arg) diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 48f7e7a72530..eb73b3f3de9e 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -108,9 +108,9 @@ ZEND_API zend_never_inline ZEND_COLD void zend_verify_never_error( const zend_function *zf); ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); ZEND_API bool zend_check_user_type_slow( - const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type); + const zend_type *type, zval *arg, const zend_reference *ref, bool current_frame); ZEND_API bool zend_check_type_ex( - const zend_type *type, zval *arg, bool is_return_type, bool is_internal); + const zend_type *type, zval *arg, bool current_frame, bool is_internal); #if ZEND_DEBUG ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call); diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index 2b5b5f4d3b44..ef335cd805c3 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -1105,8 +1105,14 @@ static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval * arg_info = NULL; } if (arg_info && ZEND_TYPE_IS_SET(arg_info->type) - && UNEXPECTED(!zend_check_type_ex(&arg_info->type, var, 0, 0))) { - zend_verify_arg_error(function, arg_info, offset+1, var); + && UNEXPECTED(!zend_check_type_ex(&arg_info->type, var, + /* current_frame */ true, /* is_internal */ false))) { + zend_string *need_msg = zend_type_to_string_resolved(arg_info->type, + function->common.scope); + zend_argument_type_error_ex(function, offset + 1, + "must be of type %s, %s given", + ZSTR_VAL(need_msg), zend_zval_value_name(var)); + zend_string_release(need_msg); zval_ptr_dtor(result); ZVAL_NULL(result); zp_free_unbound_args(offset, argc, argv); diff --git a/Zend/zend_string.h b/Zend/zend_string.h index cb0502891efa..d9efea00e7bd 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -493,6 +493,32 @@ static zend_always_inline bool zend_string_starts_with_ci(const zend_string *str #define zend_string_starts_with_literal_ci(str, prefix) \ zend_string_starts_with_cstr_ci(str, "" prefix, sizeof(prefix) - 1) +static zend_always_inline bool zend_string_ends_with_cstr(const zend_string *str, const char *suffix, size_t suffix_length) +{ + return ZSTR_LEN(str) >= suffix_length && !memcmp(ZSTR_VAL(str) + ZSTR_LEN(str) - suffix_length, suffix, suffix_length); +} + +static zend_always_inline bool zend_string_ends_with(const zend_string *str, const zend_string *suffix) +{ + return zend_string_ends_with_cstr(str, ZSTR_VAL(suffix), ZSTR_LEN(suffix)); +} + +#define zend_string_ends_with_literal(str, suffix) \ + zend_string_ends_with_cstr(str, "" suffix, sizeof(suffix) - 1) + +static zend_always_inline bool zend_string_ends_with_cstr_ci(const zend_string *str, const char *suffix, size_t suffix_length) +{ + return ZSTR_LEN(str) >= suffix_length && !strncasecmp(ZSTR_VAL(str) + ZSTR_LEN(str) - suffix_length, suffix, suffix_length); +} + +static zend_always_inline bool zend_string_ends_with_ci(const zend_string *str, const zend_string *suffix) +{ + return zend_string_ends_with_cstr_ci(str, ZSTR_VAL(suffix), ZSTR_LEN(suffix)); +} + +#define zend_string_ends_with_literal_ci(str, suffix) \ + zend_string_ends_with_cstr_ci(str, "" suffix, sizeof(suffix) - 1) + /* * DJBX33A (Daniel J. Bernstein, Times 33 with Addition) * diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 2571eb60e42d..d5d20d825652 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -32,11 +32,6 @@ #include #include -/* As of curl 7.11.1 this is no longer defined inside curl.h */ -#ifndef HttpPost -#define HttpPost curl_httppost -#endif - #include "zend_smart_str.h" #include "ext/standard/info.h" #include "ext/standard/file.h" @@ -1110,7 +1105,7 @@ void init_curl_handle(php_curl *ch) memset(&ch->err, 0, sizeof(struct _php_curl_error)); - zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t)curl_free_post, 0); + zend_llist_init(&ch->to_free->post, sizeof(struct curl_httppost *), (llist_dtor_func_t)curl_free_post, 0); zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t)curl_free_cb_arg, 0); zend_hash_init(&ch->to_free->slist, 4, NULL, curl_free_slist, 0); diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp index ed18bba70b9f..c9eb45b0f2d9 100644 --- a/ext/intl/common/common_date.cpp +++ b/ext/intl/common/common_date.cpp @@ -57,7 +57,7 @@ U_CFUNC TimeZone *timezone_convert_datetimezone( if (UNEXPECTED(offset_mins <= -24 * 60 || offset_mins >= 24 * 60)) { intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR, - "object has an time zone offset that's too large"); + "object has a time zone offset that is too large"); return NULL; } diff --git a/ext/intl/normalizer/normalizer_normalize.cpp b/ext/intl/normalizer/normalizer_normalize.cpp index 8a77d37ad4d4..e5b8db14bafb 100644 --- a/ext/intl/normalizer/normalizer_normalize.cpp +++ b/ext/intl/normalizer/normalizer_normalize.cpp @@ -115,7 +115,7 @@ U_CFUNC PHP_FUNCTION( normalizer_normalize ) case NORMALIZER_FORM_KC_CF: break; default: - zend_argument_value_error(2, "must be a a valid normalization form"); + zend_argument_value_error(2, "must be a valid normalization form"); RETURN_THROWS(); } @@ -232,7 +232,7 @@ U_CFUNC PHP_FUNCTION( normalizer_is_normalized ) case NORMALIZER_FORM_KC_CF: break; default: - zend_argument_value_error(2, "must be a a valid normalization form"); + zend_argument_value_error(2, "must be a valid normalization form"); RETURN_THROWS(); } diff --git a/ext/intl/tests/calendar_fromDateTime_error.phpt b/ext/intl/tests/calendar_fromDateTime_error.phpt index b0a6a6d0fe0c..33d9f9cf1259 100644 --- a/ext/intl/tests/calendar_fromDateTime_error.phpt +++ b/ext/intl/tests/calendar_fromDateTime_error.phpt @@ -35,6 +35,6 @@ DateMalformedStringException: Failed to parse time string (foobar) at position 0 NULL string(88) "IntlCalendar::fromDateTime(): DateTime object is unconstructed: U_ILLEGAL_ARGUMENT_ERROR" NULL -string(103) "IntlCalendar::fromDateTime(): object has an time zone offset that's too large: U_ILLEGAL_ARGUMENT_ERROR" +string(103) "IntlCalendar::fromDateTime(): object has a time zone offset that is too large: U_ILLEGAL_ARGUMENT_ERROR" NULL string(127) "IntlCalendar::fromDateTime(): time zone id 'WEST' extracted from ext/date DateTimeZone not recognized: U_ILLEGAL_ARGUMENT_ERROR" diff --git a/ext/intl/tests/calendar_setTimeZone_error2.phpt b/ext/intl/tests/calendar_setTimeZone_error2.phpt index 6b65aa1a0e7c..277311e0c6b6 100644 --- a/ext/intl/tests/calendar_setTimeZone_error2.phpt +++ b/ext/intl/tests/calendar_setTimeZone_error2.phpt @@ -25,5 +25,5 @@ bool(false) string(126) "IntlCalendar::setTimeZone(): time zone id 'WEST' extracted from ext/date DateTimeZone not recognized: U_ILLEGAL_ARGUMENT_ERROR" string(16) "Europe/Amsterdam" bool(false) -string(102) "IntlCalendar::setTimeZone(): object has an time zone offset that's too large: U_ILLEGAL_ARGUMENT_ERROR" +string(102) "IntlCalendar::setTimeZone(): object has a time zone offset that is too large: U_ILLEGAL_ARGUMENT_ERROR" string(16) "Europe/Amsterdam" diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 64a48068f378..660a17c05687 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1974,7 +1974,7 @@ static bool ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, zend_arg_info *arg zend_execute_data *execute_data = EG(current_execute_data); const zend_op *opline = EX(opline); bool ret = zend_check_user_type_slow( - &arg_info->type, arg, /* ref */ NULL, /* is_return_type */ false); + &arg_info->type, arg, /* ref */ NULL, /* current_frame */ false); if (UNEXPECTED(!ret)) { zend_verify_arg_error(EX(func), arg_info, opline->op1.num, arg); return false; @@ -1991,7 +1991,7 @@ static void ZEND_FASTCALL zend_jit_verify_return_slow(zval *arg, const zend_op_a } } if (UNEXPECTED(!zend_check_user_type_slow( - &arg_info->type, arg, /* ref */ NULL, /* is_return_type */ true))) { + &arg_info->type, arg, /* ref */ NULL, /* current_frame */ true))) { zend_verify_return_error((zend_function*)op_array, arg); } } diff --git a/ext/standard/string.c b/ext/standard/string.c index c6e89dcca2e5..823c32a8cf1e 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -1861,13 +1861,7 @@ PHP_FUNCTION(str_ends_with) Z_PARAM_STR(needle) ZEND_PARSE_PARAMETERS_END(); - if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) { - RETURN_FALSE; - } - - RETURN_BOOL(memcmp( - ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - ZSTR_LEN(needle), - ZSTR_VAL(needle), ZSTR_LEN(needle)) == 0); + RETURN_BOOL(zend_string_ends_with(haystack, needle)); } /* }}} */ diff --git a/main/poll/poll_handle.c b/main/poll/poll_handle.c index 0c0628ac49dc..228d44e1e420 100644 --- a/main/poll/poll_handle.c +++ b/main/poll/poll_handle.c @@ -19,26 +19,22 @@ static php_socket_t php_poll_handle_default_get_fd(php_poll_handle_object *handle) { zval retval; - zval obj; - zval func_name; - ZVAL_OBJ(&obj, &handle->std); - - /* Prepare function name as zval */ - ZVAL_STRING(&func_name, "getFileDescriptor"); + /* Grab getFileDescriptor() method pointer which is stored in lowercase in the function table */ + zend_function *method = zend_hash_str_find_ptr_lc(&handle->std.ce->function_table, ZEND_STRL("getfiledescriptor")); + ZEND_ASSERT(method && "no default method???"); /* Call getFileDescriptor() method */ - if (EXPECTED(call_user_function(NULL, &obj, &func_name, &retval, 0, NULL) == SUCCESS)) { - if (Z_TYPE(retval) == IS_LONG) { - php_socket_t fd = Z_LVAL(retval) < 0 ? SOCK_ERR : (php_socket_t) Z_LVAL(retval); - zval_ptr_dtor(&retval); - zval_ptr_dtor(&func_name); /* Clean up function name */ - return fd; - } - zval_ptr_dtor(&retval); + zend_call_known_function(method, &handle->std, handle->std.ce, &retval, 0, NULL, NULL); + + /* No need to deref the return value as the class is final and thus the method cannot be changed to return by-ref */ + if (EXPECTED(Z_TYPE(retval) == IS_LONG)) { + php_socket_t fd = Z_LVAL(retval) < 0 ? SOCK_ERR : (php_socket_t) Z_LVAL(retval); + /* No need to clean the retval as we know it is an integer, and thus it's just on the stack */ + return fd; } - zval_ptr_dtor(&func_name); /* Clean up function name */ + zval_ptr_dtor(&retval); return SOCK_ERR; /* Invalid socket */ }