[PHP-DEV] Can we make str_(starts|ends)_with variadic?

Can we make str_(starts|ends)_with variadic?
PR: str_(starts|ends)_with variadic needle by divinity76 · Pull Request #18825 · php/php-src · GitHub
Code like

if (str_starts_with($url, "http://") || str_starts_with($url, "https://")) {
    // url
}
if (str_ends_with($filename, ".pdf") || str_ends_with($filename,
".doc") || str_ends_with($filename, ".docx")) {
    // document
}
$isValidExtension = false;
foreach ($validExtensions as $needle) {
    if (str_ends_with($str, $needle)) {
        $isValidExtension = true;
        break;
    }
}
if ($isValidExtension) {
    // valid extension
}

could then be replaced with

if (str_starts_with($url, "http://", "https://")) {
    // url
}
if (str_ends_with($filename, ".pdf", ".doc", ".docx")) {
    // document
}
if(str_ends_with($str, ...$validExtensions){
    // valid extension
}

Fwiw Python support str.endswith((".pdf", ".doc", ".docx"))

I think I like it, it seems convenient and I had to do stuff like this in the past using a loop.
I don't immediately see a technical reason why this would be a bad idea.

Kind regards
Niels

Am 10.06.2025 um 21:46 schrieb Hans Henrik Bergan <hans@loltek.net>:

Can we make str_(starts|ends)_with variadic?

if (str_starts_with($url, "http://", "https://")) {
   // url
}
if (str_ends_with($filename, ".pdf", ".doc", ".docx")) {
   // document
}
if(str_ends_with($str, ...$validExtensions){
   // valid extension
}

Hmm, being old-fashioned I would probably do this with a regex:
str_starts_with($url, "http://", "https://") => preg_match('#^https?://#', $url)
str_ends_with($filename, ".pdf", ".doc", ".docx") => preg_match('#\.(pdf|doc|docx)$#', $filename)
str_ends_with($str, ...$validExtensions) => preg_match('#\.(' . implode('|', array_map(fn($v) => preg_quote($v, '#'), $ validExtensions)) . ')$#', $str)

And even though the variadic versions could easily be implemented in user-land I see your point about your version being clearer and less error-prone, even though my old brain is worried about a hidden loop (but that's an implementation detail and can be avoided if one really wants to).

Another way of thinking about it would be
array_any($validExtensions, fn($ext) => str_ends_with($str, $ext))
which is quite concise as well and avoids reimplementing iteration for individual functions separately.

Which leaves me +-0 on this,
- Chris

Hi

Am 2025-06-11 11:58, schrieb Christian Schneider:

Am 10.06.2025 um 21:46 schrieb Hans Henrik Bergan <hans@loltek.net>:

Can we make str_(starts|ends)_with variadic?

if (str_starts_with($url, "http://", "https://")) {
   // url
}
if (str_ends_with($filename, ".pdf", ".doc", ".docx")) {
   // document
}
if(str_ends_with($str, ...$validExtensions){
   // valid extension
}

[…]

Which leaves me +-0 on this,
- Chris

I agree that the examples are not particularly well-chosen, because the correct implementation for the two examples would be:

     if (\in_array(\pathinfo($filename, PATHINFO_EXTENSION), ['pdf', 'doc', 'docx'], true)) { }

and

     if (\in_array(\Uri\Rfc3986\Uri::parse($url)?->getScheme(), ['http', 'https'], true)) { }

and then leaving it up to the code developers / the engien to possibly optimize `\in_array()` into `match()` or similar when only a small number of values need to be matched.

Best regards
Tim Düsterhus