diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 09f63860f1163..6a3792640c0f5 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -2443,10 +2443,10 @@ function str_contains(string $haystack, string $needle): bool {} * @compile-time-eval * @frameless-function {"arity": 2} */ -function str_starts_with(string $haystack, string $needle): bool {} +function str_starts_with(string $haystack, string ...$needle): bool {} /** @compile-time-eval */ -function str_ends_with(string $haystack, string $needle): bool {} +function str_ends_with(string $haystack, string ...$needle): bool {} /** * @compile-time-eval @@ -3834,3 +3834,4 @@ function sapi_windows_set_ctrl_handler(?callable $handler, bool $add = true): bo function sapi_windows_generate_ctrl_event(int $event, int $pid = 0): bool {} #endif +// diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index d221a221f4132..e40477fd2de00 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: dfd7d2cfd31312f7f6c5074c10cab54e9d1fbccc */ + * Stub hash: 260f73232c43ba0c74d03b554413581c9c4bc516 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -918,9 +918,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_str_contains, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, needle, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_str_starts_with arginfo_str_contains +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_str_starts_with, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, haystack, IS_STRING, 0) + ZEND_ARG_VARIADIC_TYPE_INFO(0, needle, IS_STRING, 0) +ZEND_END_ARG_INFO() -#define arginfo_str_ends_with arginfo_str_contains +#define arginfo_str_ends_with arginfo_str_starts_with ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_chunk_split, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) diff --git a/ext/standard/string.c b/ext/standard/string.c index f21c9be8a7bd2..32de9285548c3 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -1839,14 +1839,32 @@ ZEND_FRAMELESS_FUNCTION(str_contains, 2) /* {{{ Checks if haystack starts with needle */ PHP_FUNCTION(str_starts_with) { - zend_string *haystack, *needle; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(haystack) - Z_PARAM_STR(needle) - ZEND_PARSE_PARAMETERS_END(); - - RETURN_BOOL(zend_string_starts_with(haystack, needle)); + zend_string *haystack; + zval *needles; + size_t num_needles; + + ZEND_PARSE_PARAMETERS_START(2, -1) + Z_PARAM_STR(haystack) + Z_PARAM_VARIADIC('+', needles, num_needles) + ZEND_PARSE_PARAMETERS_END(); + + /* Check each needle */ + for (size_t i = 0; i < num_needles; ++i) { + if (UNEXPECTED(Z_TYPE(needles[i]) != IS_STRING)) { + if (ZEND_ARG_USES_STRICT_TYPES()) { + zend_type_error("Argument %zu passed to str_starts_with() must be of type string, %s given", + i + 2, zend_zval_type_name(&needles[i])); + return; + } else { + convert_to_string(&needles[i]); + } + } + if (zend_string_starts_with(haystack, Z_STR(needles[i]))) { + RETURN_TRUE; + } + } + + RETURN_FALSE; } /* }}} */ @@ -1868,20 +1886,42 @@ ZEND_FRAMELESS_FUNCTION(str_starts_with, 2) /* {{{ Checks if haystack ends with needle */ PHP_FUNCTION(str_ends_with) { - zend_string *haystack, *needle; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(haystack) - 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); + zend_string *haystack; + zval *needles; + size_t num_needles; + + ZEND_PARSE_PARAMETERS_START(2, -1) /* 2 to unlimited args */ + Z_PARAM_STR(haystack) + Z_PARAM_VARIADIC('+', needles, num_needles) + ZEND_PARSE_PARAMETERS_END(); + + size_t haystack_len = ZSTR_LEN(haystack); + + for (size_t i = 0; i < num_needles; ++i) { + if (UNEXPECTED(Z_TYPE(needles[i]) != IS_STRING)) { + if (ZEND_ARG_USES_STRICT_TYPES()) { + zend_type_error("Argument %zu passed to str_ends_with() must be of type string, %s given", + i + 2, zend_zval_type_name(&needles[i])); + return; + } else { + convert_to_string(&needles[i]); + } + } + zend_string *needle = Z_STR(needles[i]); + size_t needle_len = ZSTR_LEN(needle); + if (needle_len > haystack_len) { + continue; + } + /* Check if haystack ends with this needle */ + if (memcmp( + ZSTR_VAL(haystack) + haystack_len - needle_len, + ZSTR_VAL(needle), + needle_len) == 0) { + RETURN_TRUE; + } + } + + RETURN_FALSE; } /* }}} */ diff --git a/ext/standard/tests/strings/str_ends_with.phpt b/ext/standard/tests/strings/str_ends_with.phpt index 92e4fc65f16f5..af6685d1a3cd5 100644 --- a/ext/standard/tests/strings/str_ends_with.phpt +++ b/ext/standard/tests/strings/str_ends_with.phpt @@ -2,6 +2,7 @@ str_ends_with() function - unit tests for str_ends_with() --FILE-- --EXPECT-- bool(true) diff --git a/ext/standard/tests/strings/str_starts_with.phpt b/ext/standard/tests/strings/str_starts_with.phpt index 552b298334f0d..011be09a93dd1 100644 --- a/ext/standard/tests/strings/str_starts_with.phpt +++ b/ext/standard/tests/strings/str_starts_with.phpt @@ -2,6 +2,7 @@ str_starts_with() function - unit tests for str_starts_with() --FILE-- --EXPECT-- bool(true)