PHP 8.0: New str_contains function

Version8.0
TypeNew Feature

One of the usability improvements that comes with PHP 8.0 is the new str_contains function. As the name suggests, it checks if the given haystack string contains a given string needle.

Without this function, the usual way to find if a given string contains another string is to use to the strpos() function:

if (strpos('Foo Bar Baz', 'Bar') !== false) {
  echo 'Found';
}

strpos() function returns the position of the needle string, or false if the needle is not found. This is error-prone, because if the needle is found at the position 0 of the haystack, it evaluates to false unless strict comparison (===) used.

To explain further, the following snippet is not correct:

if (strpos('Foo Bar Baz', 'Foo')) {
  echo 'Found';
}

Because Foo is found at the beginning of the haystack, the return value of strpos() call will be 0, which evaluates to false, and the if block will not run.

With the new str_contains function, it is easy to do this:

if (str_contains('Foo Bar Baz', 'Foo')) {
  echo 'Found';
}

Case sensitivity

str_contains() function is case-sensitive. There is no case-insensitive variant of this function. There is no technical reason to not have a case-insensitive str_icontains() function, but there isn't one for now to keep things simple.

Multi-byte strings

For strpos() function, there is a multi-byte safe mb_strpos() variant. However, for str_contains() function, there is no mb_str_contains() function. This is because internally, PHP strings are streams of bytes, and an mb_str_contains() function will be identical to the functionality of str_contains() as it would be checking for a sequence of bytes in another sequence of bytes anyway.

Conflicts with user-land implementations

There are several user-land implementations of this functionality, often with the exact same name. As of this moment, there are over 192K str_contains() matches on Github, and over 6K search results of str_contains() function declarations.

Most notably, Laravel offers a helper function str_contains(), but this function accepts an array of needles for the second parameter as well, which is not compatible with PHP core implementation.

Empty strings

If you search for an empty needle (""), PHP will always return true. To quote Nikita:

As of PHP 8, behavior of '' in string search functions is well defined, and we consider '' to occur at every position in the string, including one past the end. As such, both of these will (or at least should) return true. The empty string is contained in every string.

This means the following will always returns true:

str_contains('Foo', ''); // true
str_contains('', ''); // true

Polyfills

A PHP 7.0+ compatible polyfill is straight forward and simple:

if (!function_exists('str_contains')) {
    function str_contains(string $haystack, string $needle): bool {
        return '' === $needle || false !== strpos($haystack, $needle);
    }
}

Backwards compatibility impact

str_contains() is a new function; Unless you already have a str_contains() function declared, there should be no BC impact.

Most user-land implementations only declare its own str_contains() function only if that function does not exists. You might run into obscure bugs if the user-land implementations are incompatible with PHP core's. See the section above about user-land implementations.


RFC Externals.io discussion Implementation