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--
 <?php
+declare(strict_types=1);
 $testStr = "beginningMiddleEnd";
 var_dump(str_ends_with($testStr, "End"));
 var_dump(str_ends_with($testStr, "end"));
@@ -20,6 +21,24 @@ var_dump(str_ends_with("a\x00b", "d\x00b"));
 var_dump(str_ends_with("a\x00b", "a\x00z"));
 var_dump(str_ends_with("a", "\x00a"));
 var_dump(str_ends_with("a", "a\x00"));
+// now test varargs
+if (!str_ends_with("foo", "a", "o")) {
+    throw new \Exception("str does not end with o");
+}
+try {
+    if (!str_ends_with("foo1", "a", "o", 1, "x")) {
+        throw new \Exception("str does not end with 1");
+    }
+    throw new \Exception("int(1) did not trigger TypeError");
+} catch (\TypeError $e) {
+    // Expected TypeError due to int(1) + strict_types
+}
+if (!str_ends_with("foo1", "a", "o", "1", "x")) {
+    throw new \Exception("str does not end with 1");
+}
+if (str_ends_with("", "a", "o")) {
+    throw new \Exception("Empty string ends with something");
+}
 ?>
 --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--
 <?php
+declare(strict_types=1);
 $testStr = "beginningMiddleEnd";
 var_dump(str_starts_with($testStr, "beginning"));
 var_dump(str_starts_with($testStr, "Beginning"));
@@ -20,6 +21,24 @@ var_dump(str_starts_with("a\x00b", "a\x00d"));
 var_dump(str_starts_with("a\x00b", "z\x00b"));
 var_dump(str_starts_with("a", "a\x00"));
 var_dump(str_starts_with("a", "\x00a"));
+// now test varargs
+if (!str_starts_with("foo", "a", "f")) {
+    throw new \Exception("str does not start with f");
+}
+try {
+    if (!str_starts_with("1foo", "a", "f", 1, "x")) {
+        throw new \Exception("str does not start with 1");
+    }
+    throw new \Exception("int(1) did not trigger TypeError");
+} catch (\TypeError $e) {
+    // Expected TypeError due to int(1) + strict_types
+}
+if (!str_starts_with("1foo", "a", "f", "1", "x")) {
+    throw new \Exception("str does not start with 1");
+}
+if (str_starts_with("", "a", "f")) {
+    throw new \Exception("Empty string starts with something");
+}
 ?>
 --EXPECT--
 bool(true)