50

How can i check if a $string contains any of the items expressed in an array?

$string = 'My nAmE is Tom.';
$array = array("name","tom");
if(contains($string,$array))
{
// do something to say it contains
}

Any ideas?

tarnfeld
  • 25,992
  • 41
  • 111
  • 146
  • There is no indication of if partial word matching is desired. Do you need word boundaries? – mickmackusa Jan 22 '22 at 08:36
  • Hi @mickmackusa, what about partial string matching? – manas paul Jun 24 '22 at 03:01
  • My question to the asker is asking if `My username is Tom.` should result in a match for `name`. This is a common fork in business requirements for developers. Sometimes, making a partial match is absolutely fine; other times, it is critical that only full-words are matched. – mickmackusa Jun 24 '22 at 03:38

15 Answers15

111

I don't think there is a built-in function that will handle what you want. You could easily write a contains() function however:

function contains($str, array $arr)
{
    foreach($arr as $a) {
        if (stripos($str,$a) !== false) return true;
    }
    return false;
}
zombat
  • 92,731
  • 24
  • 156
  • 164
  • 5
    Why not regexp? I think its not to diffucult to interpret preg_match('/'.implode('|', $arr).'/i', $str) – velop Apr 01 '15 at 08:50
  • 2
    @velop While a valid approach, that is not functionally equivalent. The array would need to be written in consideration of being matched via regex. Otherwise if any strings in the array include a / or | or any other modifiers it will provide unexpected results. – Elle H Jan 22 '18 at 20:56
  • 7
    Tip for developers using Laravel: this method exists in the framework from 5.7 onwards as `Str::contains(string $haystack, array|string $needles)` and as `str_contains(string $haystack, array|string $needles)` for version 5.6. https://laravel.com/docs/8.x/helpers#method-str-contains – alexkb May 21 '21 at 05:44
  • @alexkb Would be cool with a Str:: helper function that would either return the index: number or index(s): number[] from the Str::contains instead of a boolean – ii iml0sto1 Aug 04 '22 at 16:43
25

is that what you wanted? i hope that code is compiling :)

$string = 'My nAmE is Tom.';
$array = array("name","tom");
if(0 < count(array_intersect(array_map('strtolower', explode(' ', $string)), $array)))
{
  //do sth
}
TomaszSobczak
  • 2,900
  • 21
  • 24
  • 16
    This would fail on the strings: "Tom, what do you think?" "His 'name' is Tom." among many others. – hobodave Jan 24 '10 at 04:52
  • Why call `strlower()` n times after exploding? It makes better sense to call `strtolower()` before exploding. The `0 <` is unnecessary -- the return from `count()` will be a truthy value when greater than zero. This answer is missing its educational explanation. – mickmackusa Jun 24 '22 at 03:41
  • This assumes the array only contains single words and not phrases. – miker Nov 02 '22 at 19:36
17

Using the accepted answer:

$string = 'My nAmE is Tom.';
$array = array("name","tom");
if(0 < count(array_intersect(array_map('strtolower', explode(' ', $string)), $array)))
{
  //do sth
}

Just a side note that the if statement could be changed to:

if(0 < count(array_intersect(explode(' ', strtolower($string)), $array)))

since it's not really necessary to use array_map to apply strtolower to each element. instead apply it to the initial string.

kkonstantinov
  • 179
  • 1
  • 3
  • This (along with the accepted answer) matches whole words rather than substrings so if you need to match more than whole words you should probably use @zombat 's answer. – apokryfos Oct 16 '15 at 09:16
  • Note that this answer will not match `tom` to `tom.` because the explosion on spaces does not trim the punctuation character. You might enjoy `str_word_count()` here with a format parameter of `1`. – mickmackusa Jun 24 '22 at 03:47
10

One more workaround for contains function

function contains($string, $array, $caseSensitive = true)
{
    $strippedString = $caseSensitive ? str_replace($array, '', $string) : str_ireplace($array, '', $string);
    return $strippedString !== $string;
}

PS. as for me, I'm just using it without function...

if (str_replace($array, '', $string) !== $string) {
    // do it
}
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Ivan Ternovtsiy
  • 121
  • 1
  • 7
  • The cleaner/leaner version of this answer is [here](https://stackoverflow.com/a/66985649/2943403) because it doesn't bother making two unnecessary `strlen()` calls. – mickmackusa Jun 24 '22 at 03:49
7

Something like this would work:

$string = 'My nAmE is Tom.';
$array = array("name", "tom");
foreach ($array as $token) {
    if (stristr($string, $token) !== FALSE) {
        print "String contains: $token\n";
    }
}
Paul Osman
  • 4,119
  • 2
  • 26
  • 20
7

We can check if any element of array is exists in a given string.

$string = 'My nAmE is Tom.';
$array = array("name","tom");

if(str_replace($array, '', strtolower($string)) !== strtolower($string)) {
   // If String contains an element from array      
   // Do Something
}
MaYaNk
  • 392
  • 1
  • 7
  • 16
  • 1
    Nice solution. I would prefer [mb_strtolower](https://www.php.net/manual/de/function.mb-strtolower.php) for utf8 characters. – user706420 Nov 18 '21 at 10:16
  • By calling `strtolower()` (or `mb_strtolower()`) and assigning to `$lower` before the `if` condition, you can simplify the condition to `if (str_replace($needles, '', $lower) !== $lower) {`. – mickmackusa Jun 24 '22 at 03:45
  • From an academic point of view, this answer cannot enjoy an early return if it findd a match with the first needle in the array ...it would keep re-scanning and replacing strings until the entire array of needles was iterated. – mickmackusa Jun 24 '22 at 04:20
1

Will this do the job?

$words = explode(" ", $string);
$wordsInArray = array();
foreach($words as $word) {
    if(in_array($word, $array)) {
        $wordsInArray[] = $word;
    }
}
robertbasic
  • 4,325
  • 1
  • 27
  • 25
1
<?php

$input = preg_quote('blu', '~'); // don't forget to quote input string!
$data = array('orange', 'blue', 'green', 'red', 'pink', 'brown', 'black');

$result = preg_grep('~' . $input . '~', $data);
print_r($result);

?>
1

This is an ideal task to familiarize yourself with regular expressions so that you have a robust, easily adaptable, and direct script.

It is important to understand your own criteria for matching.

  1. Do you want case-insensitive matching?
  2. Do you want whole word or partial matching?
  3. Do you need to support the possibility of encountering multibyte/unicode characters?

Here is a battery of patterns that demonstrate a few likely combinations. Notice that most of the tooling is done via "pattern modifiers" after the closing pattern delimiter. The \b means a "word boundary"; if you are not familiar with this metacharacter, please invest in more research and find other posts on Stack Overflow that implement them.

Code: (Demo)

$string = 'My nAmE ïs Tom.';

// case-sensitive matching, including partial matching
$array = ['foo', 'nAmE'];
$regex[] = '/' . implode('|', array_map(fn($v) => preg_quote($v, '/'), $array)) . '/';


// case-insensitive matching, including partial matching
$array = ['foo', 'om'];
$regex[] = '/' . implode('|', array_map(fn($v) => preg_quote($v, '/'), $array)) . '/i';

// case-insensitive matching, full word matching only
$array = ['foo', 'tom'];
$regex[] = '/\b(?:' . implode('|', array_map(fn($v) => preg_quote($v, '/'), $array)) . ')\b/i';

// case-insensitive matching, full word matching only, multibyte aware
$array = ['foo', 'ïs'];
$regex[] = '/\b(?:' . implode('|', array_map(fn($v) => preg_quote($v, '/'), $array)) . ')\b/iu';


foreach ($regex as $r) {
    if (preg_match($r, $string, $m)) {
        echo "found '$m[0]' using $r on $string\n";
    } else {
        echo "no match using $r on $string\n";
    }
}
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
1

Here's a reusable helper function that uses the PHP 8+ function str_contains:

function str_contains_any($haystack, $needles, $case_sensitive)
{
    foreach ($needles as $needle)
    {
        if (str_contains($haystack, $needle) || (($case_sensitive === false) && str_contains(strtolower($haystack), strtolower($needle))))
        {
            return true;
        }
    }
    
    return false;
}

Usage example:

$haystack = 'This is a load of shizzle';
$needles = ['fudge', 'shizzle'];
$match_found = str_contains_any($haystack, $needles, true); //true
Pikamander2
  • 7,332
  • 3
  • 48
  • 69
0
function contains($str, $arr)
{
  $ptn = '';
  foreach ($arr as $s) {
    if ($ptn != '') $ptn .= '|';
    $ptn .= preg_quote($s, '/');
  }
  return preg_match("/$ptn/i", $str);
}

echo contains('My nAmE is Tom', array('name', 'tom'));
jspcal
  • 50,847
  • 7
  • 72
  • 76
0

Another way to do with array_intersect() function, Try below code :

function checkString(array $arr, $str) {

  $str = preg_replace( array('/[^ \w]+/', '/\s+/'), ' ', strtolower($str) ); // Remove Special Characters and extra spaces -or- convert to LowerCase

  $matchedString = array_intersect( explode(' ', $str), $arr);

  if ( count($matchedString) > 0 ) {
    return true;
  }
  return false;
}
Irshad Khan
  • 5,670
  • 2
  • 44
  • 39
0

I have done some testing because I needed to check user inputs against a list of words we didn't allow.

I have found that converting everything to lowercase (because my list is lowercase) and then using array intersect was by far the fastest.

    **First Option I Tested**
    $tempString= explode(' ',strtolower($string));
    $foundWords = array_intersect($tempString,$profanities);
    Time taken: 0.00065207481384277 

    **The second option I tested**
    $tempWords = explode(' ',$words);
    foreach ($tempWords as $word)
    {
        foreach ($profanities as $profanity)
        {
            if (stripos($word,$profanity) !== false) return true;
        }
    }
    Time Taken: 0.024131059646606
Austyn
  • 41
  • 1
  • Exploding on spaces then comparing whole elements is not reliable then there may be ANY kind of punctuation in the input string. – mickmackusa Jun 24 '22 at 03:53
0

there is easier Method

   $string = 'My nAmE is Tom.';
   $convert=explode(" ",$string,5);
   if(in_array("My", $convert)){

      echo "Ja";
   }else{

      echo "Nein";
   }
Samee
  • 67
  • 6
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 04 '22 at 22:01
  • This answer is ignoring the asker's requirement to search the string with multiple needles. – mickmackusa Jun 24 '22 at 04:04
0
/**
 * ! Only assumes that $needles strings does not contain the character '|'
 */
function contains(string $haystack, array $needles)
{
    $regex = '/' . str_replace('\|', '|', preg_quote(implode('|', $needles))) . '/i';

    return preg_match($regex, $haystack);
}

Code demo: https://3v4l.org/lY6qo#v8.1.4

Regex demo: https://www.phpliveregex.com/p/E4s

medilies
  • 1,811
  • 1
  • 8
  • 32
  • 1
    This is not a solid approach. If one of the needles needs to match a literal pipe, then your script will make that literal pipe into an OR metacharacter in the regex. Another reason that I do not endorse this answer is because [`/` is not escaped by default via `preg_quote()`](https://www.php.net/manual/en/function.preg-quote.php). If a needle contains a forward slash, then the pattern will break. You need to explicitly nominate `/` as the delimiter parameter. – mickmackusa Jun 24 '22 at 03:56