PHP 8.0: String functions consider an empty string occur at every position in a string

Version8.0
TypeChange

One of the fundamental changes introduced in PHP 8.0 with strings is that it now considers an empty string ("") occur at every position in a string, even in an empty string itself.

This affects a few functions in PHP 8.0 standard library and mbstring functions in subtle ways, but they can be root of bugs given the extremely popularity of functions such as substr and strpos.

Empty string is contained in every string

The simplest way to describe this functionality is that an empty string ("") is present at every position in any given string.

This change has impact on existing functions like substr, and strpos, and the new functions introduced in PHP 8.0 such as str_contains and the str_starts_with / str_ends_with combo are already consistent with this pattern.

strpos

strpos function is likely the most prominent case that this change comes to effect in most code bases. strpos function returns the position of the first occurrence of a given needle string within a given string haystack.

The change in PHP 8.0 is that it now considers an empty string ("") is present at the start (position 0) and at every other position. This applies to an empty string haystack itself.

Prior to PHP 8.0, an empty string needle resulted in a PHP warning, and returned false:

strpos("Foo", "");
PHP Warning: strpos(): Empty needle in ... on line ...

In PHP 8.0, this is allowed and returns 0 as the position because it is now considered that an empty string occurs at every position of any given string. This applies to an empty haystack string too.

strpos("Foo", ""); // 0
strpos("", ""); // 0

stripos

Similar to the change in strpos, the case-insensitive strpos counter part follows the same empty string needle behavior.

stripos is not consistent with strpos function that it did not raise a warning, but silently returned false on an empty string needle.

Prior to PHP 8.0:

stripos("Foo", ""); // false

From PHP 8.0 and later:

stripos("Foo", ""); // 0