I am trying to make a word filter in php, and I have come across a previous Stackoverlow post that mentions the following to check to see if a string contains certain words. What I want to do is adapt this so that it checks for various different words in one go, without having to repeat the code over and over.
$a = 'How are you ?';
if (strpos($a,'are') !== false) {
echo 'true';
}
Will it work if I mod the code to the following ?......
$a = 'How are you ?';
if (strpos($a,'are' OR $a,'you' OR $a,'How') !== false) {
echo 'true';
}
What is the correct way of adding more than one word to check for ?.
To extend your current code you could use an array of target words to search for, and use a loop:
$a = 'How are you ?';
$targets = array('How', 'are');
foreach($targets as $t)
{
if (strpos($a,$t) !== false) {
echo 'one of the targets was found';
break;
}
}
Keep in mind that the use of strpos() in this way means that partial word matches can be found. For example if the target was ample in the string here is an example then a match will be found even though by definition the word ample isn't present.
For a whole word match, there is an example in the preg_match() documentation that can be expanded by adding a loop for multiple targets:
foreach($targets as $t)
{
if (preg_match("/\b" . $t . "\b/i", $a)) {
echo "A match was found.";
} else {
echo "A match was not found.";
}
}
Read it somewhere:
if(preg_match('[word1|word2]', $a)) { }
if (strpos($ro1['title'], $search)!==false or strpos($ro1['description'], $search)!== false or strpos($udetails['user_username'], $search)!== false)
{
//excute ur code
}
If you have a fixed number of words, which is not too big you can easily make it like this:
$a = 'How are you ?';
if (strpos($a,'are') !== false || strpos($a,'you') !== false || strpos($a,'How') !== false) {
echo 'true';
}
I built methods using both str_contains and preg_match to compare speeds.
public static function containsMulti(?string $haystackStr, array $needlesArr): bool
{
if ($haystackStr && $needlesArr) {
foreach ($needlesArr as $needleStr) {
if (str_contains($haystackStr, $needleStr)) {
return true;
}
}
}
return false;
}
preg_match is always a lot slower (2-10 times slower, depending on several factors), but could be useful if you want to extend it for whole-word matching, etc.
public static function containsMulti(?string $haystackStr, array $needlesArr): bool
{
if ($haystackStr && $needlesArr) {
$needlesRegexStr = implode('|', array_map('preg_quote', $needlesArr));
return (bool) preg_match('/(' . $needlesRegexStr . ')/', $haystackStr);
}
return false;
}
If you need a multibyte-save version. try this
/**
* Determine if a given string contains a given substring.
*
* #param string $haystack
* #param string|string[] $needles
* #param bool $ignoreCase
* #return bool
*/
public static function contains($haystack, $needles, $ignoreCase = false)
{
if($ignoreCase){
$haystack= mb_strtolower($haystack);
$needles = array_map('mb_strtolower',$needles);
}
foreach ((array) $needles as $needle) {
if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
return true;
}
}
return false;
}
Related
Am using code below to check presence of certain character via php and it works fine.
with code below, I can check if character a is presence in the variable and is working.
$mystring = 'my food is okay. its delicious';
$findme = 'a';
$pos = strpos($mystring, $findme);
if ($pos !== false) {
echo 'data found';
}
Here is my issue: I need to check the also presence of multiple characters like m,e,s etc. any idea on how to achieve that.
There are many ways to do this, from using multiple strpos to comparisons after a str_replace. Here we can split the string into an array and calculate the intersection with another array:
$mystring = 'my food is okay. its delicious';
$findme = ['m', 'e', 's'];
Check for ANY of the characters in the array:
if(array_intersect($findme, str_split($mystring))) {
echo "1 or more found";
}
Check for ALL of the characters in the array:
if(array_intersect($findme, str_split($mystring)) == $findme) {
echo "all found";
}
And for fun, run the array through a callback and filter it based upon whether it is in the string. This will check for ANY:
if(array_filter($findme, function($v) use($mystring) {
return strpos($mystring, $v) !== false;
}))
{
echo "1 or more found";
}
This will check for ALL:
if(array_filter($findme, function($v) use($mystring) {
return strpos($mystring, $v) !== false;
}) == $findme)
{
echo "all found";
}
Another way to do this is with trim.
$mystring = 'my food is okay. its delicious';
$findme = 'ames';
$any = trim($findme, $mystring) != $findme;
$all = !trim($findme, $mystring);
This function will do the trick for you:
/**
* Takes an array of needles and searches a given string to return all
* needles found in the string. The needles can be words, characters,
* numbers etc.
*
* #param array $needles
* #param string $haystack
* #return array|null
*/
function searchString(array $needles, string $haystack): ?array
{
$itemsFound = [];
foreach($needles as $needle) {
if (strpos($haystack, $needle) !== false) {
$itemsFound[] = $needle;
}
}
return $itemsFound;
}
https://3v4l.org/5oXGp
I'm basically just trying to store or access the found string as usable variable (that can be echo'd outside of this function).
Here's the code for my function;
function strposa($haystack, $needle, $offset=0)
{
if(!is_array($needle)) $needle = array($needle);
foreach($needle as $query)
{
if(stripos($haystack, $query, $offset) !== false) return $query;
}
return false;
}
So once it finds a match, how do I then echo out the match in my html using
<?php echo $found; ?>
Here's an idea of how it's being used....
$haystack = 'May contain traces of nuts';
$needle = array('gluten','nuts','milk');
$found = strposa($haystack, $needle, $offset);
if(strposa($haystack, $needle, 0)) {
echo $found;
};
This is currently producing bool(false) using the above function.
It's kind of odd that needle is an array and not haystack, but ignoring that, you have two issues:
First, you need to return the value and not the key:
function strposa($haystack, $needle, $offset=0)
{
if(!is_array($needle)) $needle = array($needle);
foreach($needle as $query)
{
if(stripos($haystack, $query, $offset) !== false) return $query;
}
return false;
}
Second, you need to assign the return to something:
<?php $found = strposa($haystack, $needle, $offset); ?>
<?php echo $found; ?>
But since it may be returning false, you'll need to test for that in the above assignment or before echoing:
if($found = strposa($haystack, $needle, $offset)) {
echo $found;
}
Using preg_match() with piped needles in your case-insensitive regex pattern removes the need for a custom function that uses a loop AND will allow you to better customize your search if/when your terms yield false positives due to the string existing inside of the a larger/different word.
I have displayed 3 working examples. The first is without an offset and works as expected. The second is without an offset and highlights a potential pitfall with any string search method. The third uses an offset and works as expected.
$needle = array('gluten','nuts','milk');
$regex="/".implode("|",$needle)."/i";
// $regex = /gluten|nuts|milk/i
$haystack = 'May contain traces of nuts';
if(preg_match($regex,$haystack,$match)){
$found=$match[0];
}else{
$found="Not Found"; // false
}
echo "<div>Found = $found</div>";
// TRUE POSITIVE:
// Output: Found = nuts
$haystack = 'This recipe is gluten-free!';
if(preg_match($regex,$haystack,$match)){
$found=$match[0];
}else{
$found="Not Found"; // false
}
echo "<div>Found = $found</div>";
// FALSE POSITIVE:
// Output: Found = gluten
// improve your regex pattern as needed
// for instance use: $regex="/\b".implode("\b|\b",$needle)."\b/i";
// against $haystack="May contain bits of shell from coconuts";
// and it will return "Not Found" because of the word boundaries
$haystack = 'Who put nuts in my milk?!';
$offset=12; // start beyond nuts
if(preg_match($regex,substr($haystack,$offset),$match)){
$found=$match[0];
}else{
$found="Not Found"; // false
}
echo "<div>Found = $found</div>";
// TRUE POSITIVE:
// Output: Found = milk
I have read all thread about how to filter a string for certain needles and I think stripos() is what will do the job, however, the function does not return true when for example the first needle is found as if it is filtering the haystack using only the second value of my array.
Example :
$String = 'IWantToSolveThisProblem!!';
$needle = array('want', 'solve');
foreach ($needle as $value) {
$res = stripos($String, $value,0);
if($res !==false){
echo 'found';
}
else {
echo 'not found'; }}
In the example above the output will echo 'Found' because both values are present in my string.
The problem is that it is only using the last value of my array to loop. If the first value is present within the String and not the second it will return as false.
I dont want to run multiple if statements
You should do:
if(stripos($value,$array[0]) !== false){ //check for the first element
echo "found want";
}
if(stripos($value,$array[1]) !== false){ //check for the second element
echo "found solve";
}
I tested with this code:
<?php
$str = "IWantToSolveThisProblem!";
$str2 = 'IWantAnAnswer';
$needles = array('want', 'solve');
foreach ($needles as $needle){
$res = stripos($str2, $needle, 0);
if ($res !== false){
echo "$needle found\n";
}
else {
echo "$needle not found\n";
}
}
echo "\n";
?>
and it outputs:
$php testme.php
want found
solve not found
So it seems to work for me here... you might check for typos maybe..??!
<?php
$String = 'IWantToSolveThisProblem!!';
$needle = array('want', 'solve');
foreach ($needle as $value) {
$res = stripos($String, $value,0);
if($res !==false){
echo 'found';
}
else {
echo 'not found'; }}
?>
Output:
foundfound
If you "convert" your foreach-loop to if statements it would be the same as:
if(stripos($String,$needle[0]) !== false){ //check for the first element
echo "found";
}
if(stripos($String,$needle[1]) !== false){ //check for the second element
echo "found";
}
Output:
foundfound
So the foreach - loop does several if-conditions (one for each element in the array)
$String = 'IWantToSolveThisProblem!!'; //you were missing this semicolon
$needle = array('want', 'solve'); //no spaces before the array (not needed, I just like nospaces :P)
foreach($needle as $value) {
$res = stripos($String, $value,0);
if($res !==false){
echo 'found';
}
else {
echo 'not found'; }}
This doesn't make sense now... It's not that it's only comparing the last element. It just isn't comparing the first. See this example...
$haystack = "string to be tested";
$needles = array("string","to","pineapple","tested");
foreach($needles as $needle){
if(stripos($haystack,$needle,0)){
echo "The word \"$needle\" was found in the string \"$haystack\".";
}
else{
echo "The word \"$needle\" was NOT found in the string \"$haystack\".";
}
}
Expected Output:
The word "string" was found in the string "string to be tested".
The word "to" was found in the string "string to be tested".
The word "pineapple" was NOT found in the string "string to be tested".
The word "tested" was found in the string "string to be tested".
Actual Output:
The word "string" was NOT found in the string "string to be tested".
The word "to" was found in the string "string to be tested".
The word "pineapple" was NOT found in the string "string to be tested".
The word "tested" was found in the string "string to be tested".
It all makes sense now... From the docs:
"This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function."
So, changing if(stripos($haystack,$needle,0)) to if(stripos($haystack,$needle,0) !== False) fixes the logic.
$String = 'IWantToSolveThisProblem!!';
$needle = array('want', 'solve', 42);
foreach($needle as $value) {
if(stripos($String, $value,0) !== FALSE){
echo "found \"$value\" in \"$String \"";
}
else {
echo "$value not found";
}
}
I've interpreted your question as a search to find a function that returns true when all needles are present in the given haystack, and here's a function that does just that...
/**
* Search the haystack for all of the given needles, returning true only if
* they are all present.
*
* #param string $haystack String to search
* #param array $needles Needles to look for
* #return boolean
*/
function stripos_array($haystack, array $needles)
{
foreach ($needles as $needle)
{
if (stripos($haystack, $needle) === false) return false;
}
return true;
}
// Test the function
$string = 'IWantToSolveThisProblem!!';
$search = array('want', 'solve');
$found = stripos_array($string, $search);
echo $found ? 'found' : 'not found', "\n"; // found
$search = array('want', 'cheese');
$found = stripos_array($string, $search);
echo $found ? 'found' : 'not found', "\n"; // not found
$search = array('cheese', 'problem');
$found = stripos_array($string, $search);
echo $found ? 'found' : 'not found', "\n"; // not found
It would be trivial to change this function to return true if any of the needles are found, instead of all needles.
/**
* Search the haystack for any of the given needles, returning true if any of
* them are present.
*
* #param string $haystack String to search
* #param array $needles Needles to look for
* #return boolean
*/
function stripos_array($haystack, array $needles)
{
foreach ($needles as $needle)
{
if (stripos($haystack, $needle) !== false) return true;
}
return false;
}
You are getting "not found" because when $value becomes "solve" it is not found in $String = 'IWantAnAnswer'.
I am trying to search a list of files and only perform work on the files that have names that contain a few values in an array. I am hoping to not have to loop through the array each time I have a new filename.
ala-
$needle = array('blah', 'bleh');
foreach($file_list as $file)
{
foreach($needle as $need)
if(strstr($file, $need) !== false)
{
// do something...
}
}
I just need to know if one of the strings in the array is in the filename, not the location of the string.
I would like to use something similar to strstr() but it does not allow an array to be used as the needle.
i.e.-
if(strstr($haystack, array('blah', 'bleh')))
{
// do something...
}
I would prefer to stay away from regular expressions, it seems to be a sledge for a hammer's job. any ideas?
IMO it is fine to use regular expressions here:
$pattern = '/' . implode('|', array_map('preg_quote', $needle)) . '/i';
foreach($file_list as $file) {
if(preg_match($pattern, $file)) {
// do something
}
}
Reference: preg_quote
Here is a more "creative" way to do it (but it uses internal loops and is very likely slower, as you actually loop several times over the array):
function cstrstr($haystack, $needle) {
return strstr($haystack, $needle) !== false;
}
foreach($file_list as $file) {
if(array_sum(array_map('cstrstr',
array_pad(array($file), count($needle), $file),
$needle))) {
// do something
}
}
But the advantages with the regex should be obvious: You have to create the pattern only once whereas in the "funny" solution you always have to create an array of length count($needle) for every $file.
Patrick,
You can use in_array(). Example:
foreach($file_list as $file){
if(in_array($file, $needle)){
//--------------
// Needle found
}
}
You can find more examples here: http://php.net/manual/en/function.in-array.php
This will perform an action on all the files that contain all the needles inside their name
// Loop through files
foreach($files as $file){
foreach($needles as $needle){
// if filename does not contain this needle
if(strpos($file, $needle) === false)
continue 2; // skip to next file
}
// this file matches criteria. do smth on this file
}
in_array may work just fine, however it would require exact matches in your needle array.
You could implode the array with some hash-string, and use the result with strstr or strpos —str variant returns the first occurrence, pos only the position of the first occurrence— Make sure to have a very unique string, with both an uncommon prefix and suffix. This is rather a quick and dirty approach, but it may just work in your situation.
EDIT I was just thinking, why don't you want to use a loop? It'll be much faster than concatenating all elements from an array. Your question if there is a function to find partial matches using a reference array: it's not build-in, so you'd better use your own loop (or implement some function to do it, using a loop of course)
if( strstr( implode( " ", $array ), $search ) { //found }
But a function with loop and return when found is faster and less memory consuming.
function searchArrayFast( $array, $search )
{
foreach ( $array as $a )
{
if( strstr( $a, $search)){
return true;
}
}
return false;
}
Do not use preg_match() if you only want to check if one string is contained in another string. Use strpos() instead as it will be faster.
http://php.net/manual/en/function.preg-match.php
$needle = array('blah', 'bleh');
foreach ($file_list as $file) {
$result = $this->searchStrpos($file, $needle);
if ($result !== false) {
// do something...
}
}
/**
*
* #param String $string String search
* #param mixed $needle
* #param Boolean $onlyFirst return true but
* #return Boolean Return boolean for search
*/
private function searchStrpos($string, $needle, $onlyFirst = true) {
if (is_array($needle)) {
foreach ($needle as $item) {
$result = strpos($string, $item);
if ($result !== false && $onlyFirst) {
break;
}
}
} else {
$result = strpos($string, $needle);
}
return $result;
}
I need to search a string for any occurances of another string in PHP. I have some code that I've been playing with, but it doesn't seem to be working.
Here is my code:
while (list($key, $val) = each($keyword)) {
$pos = strpos($location, $val);
if($pos == false) {
echo "allow";
exit;
} else {
echo "deny";
exit;
}
}
I have tried some of the options below, but it still does not find the match. Here is what I'm searching:
I need to find:*
blah
In:
http://blah.com
Nothing seems to find it. The code works in regular sentences:
Today, the weather was very nice.
It will find any word from the sentence, but when it is all together (in a URL) it can't seem to find it.
When checking for boolean FALSE in php, you need to use the === operator. Otherwise, when a string match is found at the 0 index position of a string, your if condition will incorrectly evaluate to true. This is mentioned explicitly in a big red box in the php docs for strpos().
Also, based on a comment you left under another answer, it appears as though you need to remove the exit statement from the block that allows access.
Putting it all together, I imagine your code should look like this:
while (list($key, $val) = each($keyword)) {
$pos = strpos($location, $val);
if($pos === false) { // use === instead of ==
echo "allow";
} else {
echo "deny";
exit;
}
}
Update:
With the new information you've provided, I've rewritten the logic:
function isAllowed($location) {
$keywords = array('blah', 'another-restricted-word', 'yet-another-restricted-word');
foreach($keywords as $keyword) {
if (strpos($location, $keyword) !== FALSE) {
return false;
}
}
return true;
}
$location = 'http://blah.com/';
echo isAllowed($location) ? 'allow' : 'deny';