27

This is for a chat page. I have a $string = "This dude is a mothertrucker". I have an array of badwords: $bads = array('truck', 'shot', etc). How could I check to see if $string contains any of the words in $bad?
So far I have:

        foreach ($bads as $bad) {
        if (strpos($string,$bad) !== false) {
            //say NO!
        }
        else {
            // YES!            }
        }

Except when I do this, when a user types in a word in the $bads list, the output is NO! followed by YES! so for some reason the code is running it twice through.

Moozy
  • 4,735
  • 3
  • 18
  • 18
user1879926
  • 1,283
  • 3
  • 14
  • 24
  • 2
    You can use `in_array` http://php.net/manual/en/function.in-array.php – iLaYa ツ Dec 10 '12 at 06:02
  • 1
    @LearneR The problem with that is that even though 'truck' is a bad word, it doesn't detect 'mothertrucker' since it detects by exactness. I prefer strpos since there is more flexibility. – user1879926 Dec 10 '12 at 06:07
  • Although, with strpos, you could mistake "classic" for a bad word no? You need to add a break when you find a bad word to prevent going through the rest of the list unnecessarily. – AEQ Feb 16 '15 at 08:45

11 Answers11

75
function contains($str, array $arr)
{
    foreach($arr as $a) {
        if (stripos($str,$a) !== false) return true;
    }
    return false;
}
Robert Went
  • 2,944
  • 2
  • 17
  • 18
Nirav Ranpara
  • 13,753
  • 3
  • 39
  • 54
  • 4
    This only works for me if I swap to `stripos($str,$a)` as the string is the haystack – Robert Went May 20 '16 at 11:16
  • 1
    Same as @Robert Went, you shoud change to `stripos($str,$a)` – Néstor Aug 02 '16 at 15:51
  • 2
    This doesn't work if your stopword is part of another word, for example: if one of the words was "shed" and the string contained the word "washed". I was thinking of a more specific case, like the string containing the word "Essex" and trying to block explicit spam. – Tony Payne Mar 02 '17 at 11:10
  • strpos first parameter is string and second one is the word which you need to find .So strpos($a,$str); is right one. – rahul singh Jul 26 '19 at 05:47
  • @TonyPayne do you have a solution for this? – Peps Jan 06 '21 at 10:37
14

1) The simplest way:

if ( in_array( 'three',  ['one', 'three', 'seven'] ))
...

2) Another way (while checking arrays towards another arrays):

$keywords=array('one','two','three');
$targets=array('eleven','six','two');
foreach ( $targets as $string ) 
{
  foreach ( $keywords as $keyword ) 
  {
    if ( strpos( $string, $keyword ) !== FALSE )
     { echo "The word appeared !!" }
  }
}
T.Todua
  • 53,146
  • 19
  • 236
  • 237
  • 4
    Both of these examples perform worse than other [answers](http://stackoverflow.com/a/13795849/630996) to this question. The first as it's written has a Big-O algorithmic complexity of O(1), but to make it work given the OP's original question, one would have to loop through all of the words in the chat message, which would give it an O(N) (where N is the number of words in the string). Since the length of the chat message can vary, performance will degrade as the length of the message increases. And the second is O(M*N) due to the double loop. – AngeloS Dec 24 '14 at 22:42
8

can you please try this instead of your code

$string = "This dude is a mothertrucker";
$bads = array('truck', 'shot');
foreach($bads as $bad) {
    $place = strpos($string, $bad);
    if (!empty($place)) {
        echo 'Bad word';
        exit;
    } else {
        echo "Good";
    }
}
HeavenCore
  • 7,533
  • 6
  • 47
  • 62
Sanjay
  • 761
  • 14
  • 25
  • 1
    This is ill-advised code that should not be used in any project. `false` and `0` are both satisfied by `empty()`. In other words, if the sentence starts with `shot`, then `Good` will be echoed. Proof of failure: https://3v4l.org/tG67U – mickmackusa Dec 16 '20 at 10:46
6

There is a very short php script that you can use to identify bad words in a string which uses str_ireplace as follows:

$string = "This dude is a mean mothertrucker";
$badwords = array('truck', 'shot', 'ass');
$banstring = ($string != str_ireplace($badwords,"XX",$string))? true: false;
if ($banstring) {
   echo 'Bad words found';
} else {
    echo 'No bad words in the string';
}

The single line:

$banstring = ($string != str_ireplace($badwords,"XX",$string))? true: false;

does all the work.

Clinton
  • 1,111
  • 1
  • 14
  • 21
  • 1
    The OP makes no mention of replacing strings. This is the correct answer to a differeent question. – mickmackusa Dec 16 '20 at 10:52
  • 2
    I am not sure that you looked carefully at the answer from @Clinton. The approach only uses str_ireplace() to identify if bad words exist (as part of the conditional), it does not actually replace strings. Quite a neat approach actually and a lot faster than iterating through each of the the words to check. – WPDavid Dec 31 '20 at 11:46
4

You can flip your bad word array and do the same checking much faster. Define each bad word as a key of the array. For example,

//define global variable that is available to too part of php script
//you don't want to redefine the array each time you call the function
//as a work around you may write a class if you don't want global variable
$GLOBALS['bad_words']= array('truck' => true, 'shot' => true);

function containsBadWord($str){
    //get rid of extra white spaces at the end and beginning of the string
    $str= trim($str);
    //replace multiple white spaces next to each other with single space.
    //So we don't have problem when we use explode on the string(we dont want empty elements in the array)
    $str= preg_replace('/\s+/', ' ', $str);

    $word_list= explode(" ", $str);
    foreach($word_list as $word){
        if( isset($GLOBALS['bad_words'][$word]) ){
            return true;
        }
    }
    return false;
}

$string = "This dude is a mothertrucker";

if ( !containsBadWord($string) ){
    //doesn't contain bad word
}
else{
    //contains bad word
}

In this code we are just checking if an index exist rather than comparing bad word with all the words in the bad word list.
isset is much faster than in_array and marginally faster than array_key_exists.
Make sure none of the values in bad word array are set to null.
isset will return false if the array index is set to null.

Abhijit Amin
  • 541
  • 4
  • 9
  • Thank you but how to put bad words in file `.txt` `$GLOBALS['bad_words']= array('truck' => true, 'shot' => true);` i try `$bad_words = file_get_contents('bad_words.txt');` `$GLOBALS['bad_words']= $bad_words ;` does not work – Skora Mar 24 '19 at 12:37
3

Put and exit or die once it find any bad words, like this

foreach ($bads as $bad) {
 if (strpos($string,$bad) !== false) {
        //say NO!
 }
 else {
        echo YES;
        die(); or exit;            
  }
}
Sanjay
  • 761
  • 14
  • 25
  • Thank you @Sanjay! This worked! All I had to change was to put another exit also in the if since if I did not NO would be echoed twice. – user1879926 Dec 10 '12 at 06:11
  • Hi It is because this works in a for each loop and all time it check each words with your bad words array.I don't think it is a correct method to put exit there at No loop , because if the 3rd or 4th word is a bad word it may not give you correct result, so could you please check that. – Sanjay Dec 10 '12 at 06:19
  • Yes, you are correct, the problem persists with the exit in the if part, sorry. Also, I realized that if I chose a bad word further down the indexo of the array, it wouldn't say NO. – user1879926 Dec 10 '12 at 06:36
  • Hi so that i said only put exit in the YES loop only, so once in the string it find any bad words it will stop further execution.Means if it find any bad words in the string it stop execution. I think that you need right? – Sanjay Dec 10 '12 at 06:40
  • If I only put it in the YES loop then if I type in a badword, NO pops up but then YES also pops up. – user1879926 Dec 10 '12 at 06:48
  • Do not use `die()` or `exit()`, use `break` to break a loop instead of halting all execution. – mickmackusa Dec 16 '20 at 10:50
3

You can do the filter this way also

$string = "This dude is a mothertrucker";
if (preg_match_all('#\b(truck|shot|etc)\b#', $string )) //add all bad words here.       
    {
  echo "There is a bad word in the string";
    } 
else {
    echo "There is no bad word in the string";
   }
eldhose
  • 81
  • 7
1

Wanted this?

$string = "This dude is a mothertrucker"; 
$bads = array('truck', 'shot', 'mothertrucker');

    foreach ($bads as $bad) {
        if (strstr($string,$bad) !== false) {
            echo 'NO<br>';
        }
        else {
            echo 'YES<br>';
        }
    }
Lenin
  • 570
  • 16
  • 36
  • 1
    The php manual clearly states not use `strstr()` to check the existence of a substring. https://www.php.net/manual/en/function.strstr.php#:~:text=If%20you%20only,instead. – mickmackusa Dec 16 '20 at 10:51
  • @mickmackusa Thanks for mentioning. I knew but overlooked. Micromanaging are not always that worthy in the long-run. – Lenin Feb 04 '21 at 12:36
0

I would go that way if chat string is not that long.

$badwords = array('mothertrucker', 'ash', 'whole');
$chatstr = 'This dude is a mothertrucker';
$chatstrArr = explode(' ',$chatstr);
$badwordfound = false;
foreach ($chatstrArr as $k => $v) {
    if (in_array($v,$badwords)) {$badwordfound = true; break;}
    foreach($badwords as $kb => $vb) {
        if (strstr($v, $kb)) $badwordfound = true;
        break;
    }
}
if ($badwordfound) { echo 'You\'re nasty!';}
else echo 'GoodGuy!';
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Graben
  • 202
  • 3
  • 8
  • change the foreach line for this : foreach ($chatstrArr as $k => $v) { foreach ($badwords as $kb => $vb) { || strstr($v,$vb) {$badwordfound = true; break;} – Graben Dec 10 '12 at 20:07
  • Damn comment box wont let me enter code modifs... I Changed my response to search for all badwords into all chat string words – Graben Dec 10 '12 at 20:13
  • The php manual clearly states not use `strstr()` to check the existence of a substring. https://www.php.net/manual/en/function.strstr.php#:~:text=If%20you%20only,instead. – mickmackusa Dec 16 '20 at 10:55
0

If you want to do with array_intersect(), then use 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
-2
 $string = "This dude is a good man";   
 $bad = array('truck','shot','etc'); 
 $flag='0';         
 foreach($bad as $word){        
    if(in_array($word,$string))        
    {       
        $flag=1;       
    }       
}       
if($flag==1)
  echo "Exist";
else
  echo "Not Exist";
Jasper
  • 363
  • 5
  • 10