Skip to content

str_(starts|ends)_with variadic needle #18825

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

divinity76
Copy link
Contributor

@divinity76 divinity76 commented Jun 10, 2025

make str_starts_with and str_ends_with variadic:

str_starts_with(string $haystack, string ...$needle): bool
str_ends_with(string $haystack, string ...$needle): bool

a simple example could be

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 seems easier than

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
}

inspired by Python's str.startswith() which supports python-tuples like if str.startswith(("foo","bar")): ...

 str_starts_with(string $haystack, string ...$needle): bool
@divinity76 divinity76 changed the title str_starts_with variadic needle str_(starts|ends)_with variadic needle Jun 10, 2025
@divinity76

This comment was marked as outdated.

@iluuu1994
Copy link
Member

This requires a discussion on the mailing list and probably an RFC.

@iluuu1994 iluuu1994 added the RFC label Jun 10, 2025
@divinity76
Copy link
Contributor Author

asked internals. https://news-web.php.net/php.internals/127637

@divinity76
Copy link
Contributor Author

divinity76 commented Jun 10, 2025

I see a theoretical issue with using variadic specifically: it will be difficult to add new arguments, like bool $case_sensitive=true in the future.

That can be avoided by accepting string|string[] $needle instead of string ...$needle

That also more closely resemble how Python handles it

@bcremer
Copy link
Contributor

bcremer commented Jun 11, 2025

A signature of str_starts_with(string $haystack, string ...$needle): bool allows the function to be called without a second argument.

Current behavior:

str_starts_with('foo'); // Uncaught ArgumentCountError: str_starts_with() expects exactly 2 arguments, 1 given

If this should be maintained the signature should be

str_starts_with(string $haystack, string $needle, string ...$additionalNeedles): bool

I'm not sure how this is handled with other core functions that accept variadic inputs or if this minor behavior change is considered a bc break.

@divinity76
Copy link
Contributor Author

divinity76 commented Jun 11, 2025

@bcremer this branch, as currently written, still requires a minimum of 2 arguments.

hans@DESKTOP-EE15SLU:~/projects/php-src$ ./sapi/cli/php -r 'str_ends_with("foo");'

Fatal error: Uncaught ArgumentCountError: str_ends_with() expects at least 2 arguments, 1 given in Command line code:1
Stack trace:
#0 Command line code(1): str_ends_with('foo')
#1 {main}
  thrown in Command line code on line 1
hans@DESKTOP-EE15SLU:~/projects/php-src$ ./sapi/cli/php -r 'str_ends_with("foo", ...[]);'

Fatal error: Uncaught ArgumentCountError: str_ends_with() expects at least 2 arguments, 1 given in Command line code:1
Stack trace:
#0 Command line code(1): str_ends_with('foo')
#1 {main}
  thrown in Command line code on line 1

it's subtle, but i believe

        Z_PARAM_VARIADIC('+', needles, num_needles)

means require at least 1 argument while

        Z_PARAM_VARIADIC('*', needles, num_needles)

would mean no arguments required

@bcremer
Copy link
Contributor

bcremer commented Jun 11, 2025

@divinity76
OK, but that in turn would mean the signature in basic_functions.stub.php is not correct and does not match the implementation.

Also tools like phpstan loose the information about the required arguments when using the provided stub. See: https://phpstan.org/r/8c158f00-493b-44c4-8cb6-e5b87b53511e

Is the stub file auto-generated or can it be modified manually?

@divinity76
Copy link
Contributor Author

it can be modified manually. do you mean it should be

str_starts_with(string $haystack, string $needle, string ...$needles): bool

then?

@bcremer
Copy link
Contributor

bcremer commented Jun 11, 2025

Disclaimer, I'm not a core contributor but user of static code analysis in userland code.

But yes, the following signature should match the implementation and produces the same error message in phpstan as before:

function str_starts_with(string $haystack, string $needle, string ...$needles): bool 

https://phpstan.org/r/00ec57e0-6c21-49d7-a9e5-c102ad02e8cc

@TimWolla
Copy link
Member

it's subtle, but i believe

        Z_PARAM_VARIADIC('+', needles, num_needles)

The first parameter is ignored there (probably for backwards compatibility reasons).

@TimWolla
Copy link
Member

Allowing zero needles would not be a BC break (it's widening the signature) and would be consistent with:

array_any($needles, static fn ($needle) => str_starts_with($haystack, $needle));

returning false for an empty array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants