58

How to check, if a string contain multiple specific words?

I can check single words using following code:

$data = "text text text text text text text bad text text naughty";
if (strpos($data, 'bad') !== false) {
    echo 'true';
}

But, I want to add more words to check. Something like this:

$data = "text text text text text text text bad text text naughty";
if (strpos($data, 'bad || naughty') !== false) {
    echo 'true';
}

(if any of these words is found then it should return true)

But, above code does not work correctly. Any idea, what I'm doing wrong?

2
  • Should the function return true if all of the words are in string or if one of the words was found?
    – hek2mgl
    Commented Apr 7, 2013 at 12:29
  • 1
    if one of the words was found then it should return true Commented Apr 7, 2013 at 12:30

8 Answers 8

101

For this, you will need Regular Expressions and the preg_match function.

Something like:

if(preg_match('(bad|naughty)', $data) === 1) { } 

The reason your attempt didn't work

Regular Expressions are parsed by the PHP regex engine. The problem with your syntax is that you used the || operator. This is not a regex operator, so it is counted as part of the string.

As correctly stated above, if it's counted as part of the string you're looking to match: 'bad || naughty' as a string, rather than an expression!

6
  • 7
    Please note that preg_match returns the int value 1 if the pattern matches the given subject, but not true. Either replace === true with === 1 or !== 0. See php.net/manual/en/…
    – Epoc
    Commented Mar 3, 2016 at 9:49
  • Thanks for the note. I did originally have it checking for truthy values instead of type checking, but it was recommended that I edit it to use the triple equals operator. Commented Mar 3, 2016 at 9:51
  • 1
    If performance is important, it's usually better to avoid using preg_match() for this kind of simple string search since it's fairly slow compared to other options like strpos(). Also it's probably worth pointing out that this matches any substrings, not necessarily whole words ("badminton" would also return true).
    – orrd
    Commented Oct 27, 2016 at 20:51
  • @christopher what if bad has | in it, i.e ba|d Commented May 22, 2019 at 11:20
  • 1
    Please note that parentheses are being used as pattern delimiters in this code NOT as a capture group. I discourage the use of parentheses as delimiters because it can trick a developer's eye while debugging. Commented Jun 2, 2021 at 10:03
36

You can't do something like this:

if (strpos($data, 'bad || naughty') !== false) {

instead, you can use regex:

if(preg_match("/(bad|naughty|other)/i", $data)){
 //one of these string found
}
3
  • 3
    This was very nice @christopher example did not worked but yours worked: if(preg_match("/(bad|naughty|other)/i", $data)){ //one of these string found }
    – user285594
    Commented Aug 26, 2015 at 23:35
  • Agree with above comment. Thanks dude.
    – 502_Geek
    Commented Jul 31, 2018 at 1:37
  • This one works because it has the required delimiters added. The optional "i" for case insensitive is a nice touch. The accepted answer is flawed. Commented Jun 26, 2020 at 13:03
14

strpos does search the exact string you pass as second parameter. If you want to check for multiple words you have to resort to different tools

regular expressions

if(preg_match("/\b(bad|naughty)\b/", $data)){
    echo "Found";
}

(preg_match return 1 if there is a match in the string, 0 otherwise).

multiple str_pos calls

if (strpos($data, 'bad')!==false or strpos($data, 'naughty')!== false) {
    echo "Found";
}

explode

if (count(array_intersect(explode(' ', $data),array('bad','naugthy')))) {
    echo "Found";
}

The preferred solution, to me, should be the first. It is clear, maybe not so efficient due to the regex use but it does not report false positives and, for example, it will not trigger the echo if the string contains the word badmington

The regular expression can become a burden to create if it a lot of words (nothing you cannot solve with a line of php though $regex = '/\b('.join('|', $badWords).')\b/';

The second one is straight forward but can't differentiate bad from badmington.

The third split the string in words if they are separated by a space, a tab char will ruins your results.

3
  • Just a small correction in str_pos: add || instead of or : if (strpos($data, 'bad')!==false || strpos($data, 'naughty')!== false) { echo "Found"; }
    – candle
    Commented Sep 23, 2019 at 13:26
  • Just a small re-correction of the misleading correction above. Using or is perfectly fine as it is used in this answer. Commented Jun 26, 2020 at 13:05
  • For the record, the count() call is not necessary. If array_intersect() is a non-empty array, then it will be evaluated as "truthy". Commented Apr 21, 2021 at 23:32
8

if(preg_match('[bad|naughty]', $data) === true) { }

The above is not quite correct.

"preg_match() returns 1 if the pattern matches given subject, 0 if it does not, or FALSE if an error occurred."

So it should be just:

if(preg_match('[bad|naughty]', $data)) { }
3
  • 1
    or if(preg_match('[bad|naughty]', $data) == true){} so it is not a strict comparison...
    – Justin E
    Commented Jan 6, 2015 at 21:05
  • == true is unnecessary syntax. Commented Nov 17, 2020 at 23:12
  • 1
    I find using [ and ] as pattern delimiters to be unnecessarily confusing/misleading. I see lot of new dev failing to understand that the square braces that are intended as a character class are actually pattern delimiters. I never use [] or () as pattern delimiters for clarity's sake. Commented Apr 21, 2021 at 23:34
4

substr_count()

I want to add one more way doing it with substr_count() (above all other answers):

if (substr_count($data, 'bad') || substr_count($data, 'naughty')){
    echo "Found";
}

substr_count() is counting for how many times the string appears, so when it's 0 then you know that it was not found. I would say this way is more readable than using str_pos() (which was mentioned in one of the answers) :

if (strpos($data, 'bad')!==false || strpos($data, 'naughty')!== false) {
    echo "Found";
}
0
2

You have to strpos each word. Now you are checking if there is a string that states

'bad || naughty'

which doesn't exist.

0
0

A simple solution using an array for the words to be tested and the array_reduce() function:

$words_in_data = array_reduce( array( 'bad', 'naughty' ), function ( $carry, $check ) use ( $data ) {
    return ! $carry ? false !== strpos( $data, $check ) : $carry;
} );

Then you can simply use:

if( $words_in_data ){
    echo 'true';
}
3
  • 1
    The disadvantage of using "functional programming" for this task is that you do not get to enjoy the benefits of an "early return" (short circuiting). This effectively means that your iterating technique will continue iterating all values regardless of if it found a qualifying match. In many cases, this is an unwanted behavior. This is not an attack on this answer, it is just a word of caution to researchers. A breakable foreach() is just as verbose, but will have better efficiency/directness. Commented Apr 21, 2021 at 23:38
  • No problem @mickmackusa. But actually, if any of the words have been found in the string, it won't check the rest of them. It will be almost like a short circuiting I believe Commented Apr 21, 2021 at 23:49
  • 1
    It is "somewhere in between" with respect to efficiency/directness. While it won't continue calling strpos() on each iteration, but it will unnecessarily iterate the entire input array. In other words, it is "as good as it gets" for a non-short-circuiting technique. Ultimately, I don't see any compelling reason to use this verbose technique with use() versus a simple foreach(). If a dev wants something tidy that fits inside of if (multiNeedleSearch($needles, $haystack)) {, then just write a custom function containing the foreach(). Commented Apr 22, 2021 at 0:05
0

Here is a function that can perform this operation without using regular expressions which could be slower. Instead of passing a single string for the task, pass an array like

if (strposMultiple($data, ['bad', 'naughty']) !== false) {
    //...
}

Here is the function:

function strposMultiple($haystack, $needle, $offset = 0) {
    if(is_string($needle))
        return strpos($haystack, $needle, $offset);
    else {
        $min = false;
        foreach($needle as $n) {
            $pos = strpos($haystack, $n, $offset);

            if($min === false || $pos < $min) {
                $min = $pos;
            }
        }

        return $min;
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.