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

php.internals

Hans Henrik Bergan

1 year ago
Can we make str_(starts|ends)_with variadic? PR: https://github.com/php/php-src/pull/18825 Code like ```php 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"))

Niels Dossche

1 year ago
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

Christian Schneider

1 year ago
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

Tim Düsterhus

1 year ago
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