Search code examples
phpstrpos

PHP stripos not returning all positions


I am returning all the array where the stripos function matches like this

$requestedservice = request('service');
$result_array = array_filter($allservicesname, function($item) use ($requestedservice) {
return (stripos($item, $requestedservice) !== false);
        });

This only works for the first occurrence, e.g- if the haystack is Walk dance and rock if the needle is Walk it reutrns Walk dance and rock, but if the needle is Walk rock it does not return Walk dance and rock. Why is this happening?

Is there a way to find for all occurrences, so that, it matches the haystack Walk dance and rock with the needle Walk rock?

And also if the needle is Walk run it should not match Walk dance and rock, this is kind of normal use case


Solution

  • It appears you will be able to achieve your desired filtration with a loop of case-insensitive keyword checks.

    Code: (Demo)

    $allservicesname=['dance','Walk rock','Walk dance and rock','Walk'];  // array of haystacks
    
    $requestedservice='walk rock';  // string of needles
    
    $result_array=array_filter($allservicesname,function($string)use($requestedservice){
        foreach(explode(' ',$requestedservice) as $keyword){
            if(stripos($string,$keyword)===false){
                return false;
            }
        }
        return true;
    });
    
    var_export($result_array);
    

    Output:

    array (
      1 => 'Walk rock',
      2 => 'Walk dance and rock',
    )
    

    Notice the original keys are preserved in the output array. Also, I should mention that the keywords can appear in any order and maintain accuracy.


    Alternatively, if you are not scared off by the idea of regex, preg_grep() can concisely perform this task. It is the function that is designed to filter arrays using regex on each element.

    Code: (Demo) (Regex Pattern Demo)

    $allservicesname=['Walk dance and rock walk','dance','walk','rock and walk','sidewalk and rockwall'];  // array of haystacks
    
    $requestedservice='walk rock';  // string of needles
    $pattern='/(?=.*\Q'.str_replace(' ','\E)(?=.*\Q',$requestedservice).'\E)/i';  // with literal substring matching, no wordboundaries
    //$pattern='/(?=.*\b\Q'.str_replace(' ','\E\b)(?=.*\b\Q',$requestedservice).'\E\b)/i';  // with wordboundaries  
    echo $pattern,"\n";
    
    $result_array=preg_grep($pattern,$allservicesname);
    
    var_export($result_array);
    

    Output:

    /(?=.*\Qwalk\E)(?=.*\Qrock\E)/i
    array (
      0 => 'Walk dance and rock walk',
      3 => 'rock and walk',
      4 => 'sidewalk and rockwall',
    )
    

    If you don't want element [4] to qualify, then word boundaries are necessary - use the second (commented out) pattern declaration instead.

    \Q..\E ensures that no characters with special meanings in regex can interfere with the execution. The substrings inside of them are interpreted "literally".