How can i define multiple needles and still perform the same actions below. Im trying to define extra keywords such as numbers, numerals, etc... as of now i have to create a duplicate if loop with the minor keyword change.
if (stripos($data, 'digits') !== false) {
$arr = explode('+', $data);
for ($i = 1; $i < count($arr); $i += 2) {
$arr[$i] = preg_replace('/\d/', '', $arr[$i]);
}
$data = implode('+', $arr);
}
Create a function that loops through an array?
function check_matches ($data, $array_of_needles)
{
foreach ($array_of_needles as $needle)
{
if (stripos($data, $needle)!==FALSE)
{
return true;
}
}
return false;
}
if (check_matches($data, $array_of_needles))
{
//do the rest of your stuff
}
--edit added semicolon
function strposa($haystack, $needles=array(), $offset=0) {
$chr = array();
foreach($needles as $needle) {
$res = strpos($haystack, $needle, $offset);
if ($res !== false) $chr[$needle] = $res;
}
if(empty($chr)) return false;
return min($chr);
}
Usage:
$array = array('1','2','3','etc');
if (strposa($data, $array)) {
$arr = explode('+', $data);
for ($i = 1; $i < count($arr); $i += 2) {
$arr[$i] = preg_replace('/\d/', '', $arr[$i]);
}
$data = implode('+', $arr);
} else {
echo 'false';
}
function taken from https://stackoverflow.com/a/9220624/1018682
Though the previous answers are correct, but I'd like to add all the possible combinations, like you can pass the needle as array or string or integer.
To do that, you can use the following snippet.
function strposAll($haystack, $needles){
if(!is_array($needle)) $needles = array($needles); // if the $needle is not an array, then put it in an array
foreach($needles as $needle)
if( strpos($haystack, $needle) !== False ) return true;
return false;
}
You can now use the second parameter as array or string or integer, whatever you want.
Related
I need to find and extract words (array) in a string. I tried many techniques, and I found how to find first occurrences, but can't get it to work recursively.
$uda_state = [
"uda actif",
"uda stagiaire",
"stagiaire uda",
"uda",
"apprenti actra",
"actra apprenti",
"actra"
];
$arr = [
'<p>Nothing here</p>',
'<p>UDA Stagiaire et ACTRA</p>',
'<p>Stagiaire UDA</p>',
'<p>Should not pop</p>'
];
function contains($str, array $arr)
{
foreach($arr as $a) {
if ($pos = stripos($str,$a)) return true;
}
return false;
}
function extractWord($str, array $arr)
{
foreach($arr as $a) {
if ($pos = stripos($str, $a)) return $arr;
}
}
function find_uda_actra($str, array $arr = []) {
global $uda_state;
if (contains($str, $uda_state)) {
$word = extractWord($str, $uda_state);
$arr[] = $word;
$str = str_ireplace($word, '', $str);
if ($str != '') {
if (contains($str, $uda_state)) {
list($str, $arr) = find_uda_actra($str, $arr);
}
else {
return [$str, $arr];
}
} else {
return [$str, $arr];
}
}
return [$str, $arr];
}
foreach ($arr as $str) {
list($content, $uda) = find_uda_actra($str);
echo 'Content:'.PHP_EOL;
var_dump($content);
echo 'UDA:'.PHP_EOL;
var_dump($uda);
}
All I get now is emptied content and the whole $uda_state in each $uda. I need only the one that was found.
3v4l link for sandbox.
I have an array:
$arr1 = [1,2,2,3,3,3];
Is there any method by which I can delete a duplicate value once like,
some_function($arr1,3);
which gives me the ouput $arr1 = [1,2,2,3,3]
As per the comment, check if there are more than one of the element you're searching for, then find and remove one of them.
function remove_one_duplicate( $arr, $target ) {
if( count( array_keys( $arr, $target ) ) > 1 ) {
unset( $arr[array_search( $target, $arr )] );
}
return $arr;
}
This should work...
function removeduplicate($myarray,$needle) {
if (($nmatches = count($matches = array_keys($myarray,$needle))) >= 2) {
for ($i=0; $i<$nmatches; $i++) {
if ($matches[$i+1] == (1+$matches[$i])) {
array_splice($myarray,$matches[$i],1);
break;
}
}
}
return $myarray;
}
To use the function...
$arr1 = [1,2,2,3,3,3,4,7,3,3];
$newarray = removeduplicate($arr1,3)
print_r($newarray);
EDITED:
If you want the function to modify your original array directly, you can do it (the function will return true if a character has been removed or false if not)...
function removeduplicate(&$myarray,$needle) {
if (($nmatches = count($matches = array_keys($myarray,$needle))) >= 2) {
for ($i=0; $i<$nmatches; $i++) {
if ($matches[$i+1] == (1+$matches[$i])) {
array_splice($myarray,$matches[$i],1);
return true;
}
}
}
return false;
}
So, now you can do...
$arr1 = [1,2,2,2,2,2,3,3,3,4,2,2];
removeduplicate($arr1,2);
print_r($arr1);
This function will copy the array skipping only the second instance of the specified element.
function removeOnce($array, $elem) {
$found = 0;
$result = [];
foreach ($array as $x) {
if ($x == $elem && (++$found) == 2) {
continue;
}
$result[] = $x;
}
return $result;
}
I am trying to make a routing system in php which uses regex. What I need is to match the route specified in route collection and check if the url matches with it, and if it does, return the parameters in the route by matching with the url.
For example
$route="user/{username}/id/{id}/age/{age}/profile";
$url="user/joe/id/99/age/33/profile";
First thing is to check if the $url matches the $route pattern, return false if it doesn't.
Then I need to return an array containing
[
'username'=>'joe',
'id'=>'99',
'age'=>'33',
]
I'm not good at this at all, I had a clumsy go at it.
Here's my current code
<?php
$r="user/username/{name}/id/{id}/age/{age}/profile";
$u="user/username/joe/id/99/age/33/profile";
route::match($r, $u);
class route{
public static function match($route, $url)
{
if(strpos($route, '{')===FALSE)
{
if(strcmp($route, $url)==0)
{
return TRUE;
}
return FALSE;
}
$vars=[];
$umatches=[];
preg_match_all('/\{(.*?)\}/', $route, $matches);
preg_match('/(.*?)\{/', $route, $amatches);
preg_match_all('/\}(.*?)\{/', $route, $bmatches);
$a=preg_split(end($bmatches[1]), $route);
$b=preg_split('/\}(.*?)/', $a[1]);
array_push($umatches, $amatches[1]);
foreach ($bmatches[1] as $key => $value)
{
array_push($umatches, $value);
}
array_push($umatches, $b[1]);
$pattern="/".str_replace('/', '\/', $amatches[1])."/";
$split=preg_split($pattern, $url);
$i=0;
foreach ($umatches as $key => $value) {
$value=str_replace('/', '\/', $value);
$value='/'.$value.'/';
$r=preg_split($value, $url);
$url=$r[1];
if($i>0)array_push($vars, $r[0]);
$i++;
}
print_r($vars);
if(sizeof($matches[1])!=sizeof($vars)) return FALSE;
$params=[];
for ($i=0; $i < sizeof($matches[1]); $i++) {
$params[$matches[1][$i]]=$vars[$i];
}
print_r($params);
return $params;
}
}
Here I ran the code http://ideone.com/blljFM
Not a php guru here. So below is just a quick 2-step solution using
pseudo-code.
global $TemplateArray;
global $UrlArray;
function GetRoutParams ( $strUrlTemplate, $strUrl )
{
$TemplateArray = [];
$UrlArray = [];
// Step 1. Create the regex from the template
$strUrlRegex = preg_replace_callback('~\{([^{}]+)\}~',
function( $matches ){
$repl = '([^/]+)';
// push $matches[1] into a $TemplateArray
return $repl;
},
$strUrlTemplate);
// Step 2. Create the hash from the regex
if ( preg_match($strUrlRegex, $strUrl, $matches) )
{
// Peel off the matches
// (Or, make a hash)
for ( int $i = 0; $i < $matches.count; $i++ )
{
push $UrlArray, $TemplateArray[$i];
push $UrlArray, $matches[$i];
}
// (Or, make a hash)
// $UrlHash[ $TemplateArray[$i] ] = $matches[$i];
}
else
{
return false;
}
return true;
}
Check out this regex with global match against your string:
(user|id|age)\/([^\/]+) it matches the keyword, a '/' and the next path element (which must not contain ANY '/'). Also be aware that for example a "superuser/1/" string would also be matched.
Each match should give you a key-value pair. Looping over the matches you can build your arrays/dicts as you intend.
<?php
$r="user/username/{name}/id/{id}/age/{age}/profile";
$u="user/username/joe/id/99/age/33/profile";
route::match($r, $u);
class route{
public static function match($route, $url)
{
preg_match_all('/(username|id|age)\/([^\/]+)/', $url, $matches);
$params=[];
for ($i=0; $i < sizeof($matches[1]); $i++) {
$params[$matches[1][$i]]=$matches[2][$i];
}
print_r($params);
return $params;
}
}
A little bit more elaborate:
<?php
$r=":user/username/[^/]+/id/[^/]+/age/[^/]+/profile:";
$u="user/username/joe/id/99/age/33/profile";
$k="username|id|age"; # valid keys
print_r(route::match($r, $u, $k));
class route{
public static function match($route, $url, $valid_url_keys)
{
$number_of_expected_key_value_pairs = 3;
$number_of_matches = preg_match_all(":($valid_url_keys)/([^/]+):", $url, $matches);
if($number_of_matches == $number_of_expected_key_value_pairs)
{
$params=[];
for ($i=0; $i < sizeof($matches[1]); $i++) {
$params[$matches[1][$i]]=$matches[2][$i];
}
return $params;
}
else {
return FALSE;
}
}
}
Changed regex delimiter to colon, since the slash is a url-pattern, we need less escaping.
Only returns matches if they match the route specification.
Update: I've implemented sln's solution with a few fixes. Here's it
class Route
{
private static $tmp = array();
public static function GetRoutParams($strUrlTemplate, $strUrl)
{
$strUrlRegex = preg_replace_callback('~\{([^{}]+)\}~',
function ($matches)
{
$repl = '([^)]+)';
self::$tmp[] = $matches[1];
return $repl;
}
, $strUrlTemplate);
$UrlArray = array();
$matches = array();
$strUrlRegex = str_replace('/', '\/', $strUrlRegex);
if (preg_match("/^" . $strUrlRegex . "$/", $strUrl, $matches))
{
for ($i = 0; $i < count(self::$tmp); $i++)
{
$UrlArray[self::$tmp[$i]] = $matches[$i + 1];
}
self::$tmp = array();
return $UrlArray;
}
return false;
}
}
I've completed the code; though it feels inefficient. I'd be grateful if someone helps making the code more efficient.
<?php
$route = "user/{name}/id/{id}/age/{age}/ash";
$url = "user/joe/id/99/age/33/ash";
route::match($route, $url);
class route
{
public static function match($route, $url)
{
if (strpos($route, '{') === FALSE)
{
if (strcmp($route, $url) == 0)
{
return TRUE;
}
return FALSE;
}
$vars = [];
$umatches = [];
//get all parameters in curly braces in $route
preg_match_all('/\{(.*?)\}/', $route, $matches);
//get the string before first occurrence of { in $route
preg_match('/(.*?)\{/', $route, $amatches);
//get all strings between } and { in $route
preg_match_all('/\}(.*?)\{/', $route, $bmatches);
//get the string after last }
if (!empty($bmatches[1])){
$a = preg_split(end($bmatches[1]) , $route);
$b = preg_split('/\}(.*?)/', end($a));
}
else{
$a = preg_split('/' . str_replace('/', '\/', end($amatches)) . '/', $route);
$b = preg_split('/\}(.*?)/', end($a));
}
//push the matches into array $umatches
if (!empty($amatches[1])) array_push($umatches, $amatches[1]);
if (!empty($bmatches[1]))
{
foreach($bmatches[1] as $key => $value)
{
array_push($umatches, $value);
}
}
if (!empty($b[1])) array_push($umatches, $b[1]);
//check if the $url matches with $route template
$prev = - 1;
foreach($umatches as $key => $value)
{
$pos = strpos($url, $value);
if ($pos !== FALSE)
{
if ($prev > $pos) return FALSE;
$prev = $pos;
}
else return FALSE;
}
//push the parameter values in $url into $vars array
$i = 0;
foreach($umatches as $key => $value)
{
$value = str_replace('/', '\/', $value);
$value = '/' . $value . '/';
$r = preg_split($value, $url);
$url = $r[1];
if (!empty($r[0])) array_push($vars, $r[0]);
$i++;
}
if (!empty($r[1])) array_push($vars, $r[1]);
//map the values in $url with the parameters in $route template
$params = [];
for ($i = 0; $i < sizeof($matches[1]); $i++)
{
$params[$matches[1][$i]] = $vars[$i];
}
return $params;
}
}
The code now properly returns the parameters. The problems I had was the code was being preg_split by empty strings in some places. Also I didn't push the value if the parameter occurred in the end of string; for example - user/{username}, here {username} wasn't being pushed properly. Also the comparing the template with given url using sizeof() doesn't always give correct result.
I want to get the position of a sub string from a string using php. I can use strpos(), but it returns only first occurrence. How can I get the positions of multiple occurrence.
From: http://www.php.net/manual/en/function.strpos.php#108426
function strpos_r($haystack, $needle)
{
if(strlen($needle) > strlen($haystack))
trigger_error(sprintf("%s: length of argument 2 must be <= argument 1", __FUNCTION__), E_USER_WARNING);
$seeks = array();
while($seek = strrpos($haystack, $needle))
{
array_push($seeks, $seek);
$haystack = substr($haystack, 0, $seek);
}
return $seeks;
}
This will return an array with the position of occurences.
From the manual, there are such functions in the comments.
function strpos_recursive($haystack, $needle, $offset = 0, &$results = array()) {
$offset = strpos($haystack, $needle, $offset);
if($offset === false) {
return $results;
} else {
$results[] = $offset;
return strpos_recursive($haystack, $needle, ($offset + 1), $results);
}
}
Third parameter of strpos, has a $offset you can use:
$positions_of_string = array();
$str_to_find = "string to find";
$str_length = strlen( $str_to_find );
$last_found = 0 - $str_length;
while( false !== $last_found ) {
$last_found = strpos( $the_string, $str_to_find, $last_found+$str_length );
if( false !== $last_found )
$positions_of_strings[] = $last_found;
}
How do you use the strpos for an array of needles when searching a string? For example:
$find_letters = array('a', 'c', 'd');
$string = 'abcdefg';
if(strpos($string, $find_letters) !== false)
{
echo 'All the letters are found in the string!';
}
Because when using this, it wouldn't work, it would be good if there was something like this
#Dave an updated snippet from http://www.php.net/manual/en/function.strpos.php#107351
function strposa($haystack, $needles=array(), $offset=0) {
$chr = array();
foreach($needles as $needle) {
$res = strpos($haystack, $needle, $offset);
if ($res !== false) $chr[$needle] = $res;
}
if(empty($chr)) return false;
return min($chr);
}
How to use:
$string = 'Whis string contains word "cheese" and "tea".';
$array = array('burger', 'melon', 'cheese', 'milk');
if (strposa($string, $array, 1)) {
echo 'true';
} else {
echo 'false';
}
will return true, because of array "cheese".
Update: Improved code with stop when the first of the needles is found:
function strposa(string $haystack, array $needles, int $offset = 0): bool
{
foreach($needles as $needle) {
if(strpos($haystack, $needle, $offset) !== false) {
return true; // stop on first true result
}
}
return false;
}
$string = 'This string contains word "cheese" and "tea".';
$array = ['burger', 'melon', 'cheese', 'milk'];
var_dump(strposa($string, $array)); // will return true, since "cheese" has been found
str_replace is considerably faster.
$find_letters = array('a', 'c', 'd');
$string = 'abcdefg';
$match = (str_replace($find_letters, '', $string) != $string);
The below code not only shows how to do it, but also puts it in an easy to use function moving forward. It was written by "jesda". (I found it online)
PHP Code:
<?php
/* strpos that takes an array of values to match against a string
* note the stupid argument order (to match strpos)
*/
function strpos_arr($haystack, $needle) {
if(!is_array($needle)) $needle = array($needle);
foreach($needle as $what) {
if(($pos = strpos($haystack, $what))!==false) return $pos;
}
return false;
}
?>
Usage:
$needle = array('something','nothing');
$haystack = "This is something";
echo strpos_arr($haystack, $needle); // Will echo True
$haystack = "This isn't anything";
echo strpos_arr($haystack, $needle); // Will echo False
The question, is the provided example just an "example" or exact what you looking for? There are many mixed answers here, and I dont understand the complexibility of the accepted one.
To find out if ANY content of the array of needles exists in the string, and quickly return true or false:
$string = 'abcdefg';
if(str_replace(array('a', 'c', 'd'), '', $string) != $string){
echo 'at least one of the needles where found';
};
If, so, please give #Leon credit for that.
To find out if ALL values of the array of needles exists in the string, as in this case, all three 'a', 'b' and 'c' MUST be present, like you mention as your "for example"
echo 'All the letters are found in the string!';
Many answers here is out of that context, but I doubt that the intension of the question as you marked as resolved. E.g. The accepted answer is a needle of
$array = array('burger', 'melon', 'cheese', 'milk');
What if all those words MUST be found in the string?
Then you try out some "not accepted answers" on this page.
You can iterate through the array and set a "flag" value if strpos returns false.
$flag = false;
foreach ($find_letters as $letter)
{
if (strpos($string, $letter) !== false)
{
$flag = true;
}
}
Then check the value of $flag.
If you just want to check if certain characters are actually in the string or not, use strtok:
$string = 'abcdefg';
if (strtok($string, 'acd') === $string) {
// not found
} else {
// found
}
This expression searches for all letters:
count(array_filter(
array_map("strpos", array_fill(0, count($letters), $str), $letters),
"is_int")) == count($letters)
You can try this:
function in_array_strpos($word, $array){
foreach($array as $a){
if (strpos($word,$a) !== false) {
return true;
}
}
return false;
}
You can also try using strpbrk() for the negation (none of the letters have been found):
$find_letters = array('a', 'c', 'd');
$string = 'abcdefg';
if(strpbrk($string, implode($find_letters)) === false)
{
echo 'None of these letters are found in the string!';
}
This is my approach. Iterate over characters in the string until a match is found. On a larger array of needles this will outperform the accepted answer because it doesn't need to check every needle to determine that a match has been found.
function strpos_array($haystack, $needles = [], $offset = 0) {
for ($i = $offset, $len = strlen($haystack); $i < $len; $i++){
if (in_array($haystack[$i],$needles)) {
return $i;
}
}
return false;
}
I benchmarked this against the accepted answer and with an array of more than 7 $needles this was dramatically faster.
If i just want to find out if any of the needles exist in the haystack, i use
reusable function
function strposar($arrayOfNeedles, $haystack){
if (count(array_filter($arrayOfNeedles, function($needle) use($haystack){
return strpos($haystack, $needle) !== false;
})) > 0){
return true;
} else {
return false;
}
}
strposar($arrayOfNeedles, $haystack); //returns true/false
or lambda function
if (count(array_filter($arrayOfNeedles, function($needle) use($haystack){
return strpos($haystack, $needle) !== false;
})) > 0){
//found so do this
} else {
//not found do this instead
}
With the following code:
$flag = true;
foreach($find_letters as $letter)
if(false===strpos($string, $letter)) {
$flag = false;
break;
}
Then check the value of $flag. If it is true, all letters have been found. If not, it's false.
I'm writing a new answer which hopefully helps anyone looking for similar to what I am.
This works in the case of "I have multiple needles and I'm trying to use them to find a singled-out string". and this is the question I came across to find that.
$i = 0;
$found = array();
while ($i < count($needle)) {
$x = 0;
while ($x < count($haystack)) {
if (strpos($haystack[$x], $needle[$i]) !== false) {
array_push($found, $haystack[$x]);
}
$x++;
}
$i++;
}
$found = array_count_values($found);
The array $found will contain a list of all the matching needles, the item of the array with the highest count value will be the string(s) you're looking for, you can get this with:
print_r(array_search(max($found), $found));
Reply to #binyamin and #Timo.. (not enough points to add a comment..) but the result doesn't contain the position..
The code below will return the actual position of the first element which is what strpos is intended to do. This is useful if you're expecting to find exactly 1 match.. If you're expecting to find multiple matches, then position of first found may be meaningless.
function strposa($haystack, $needle, $offset=0) {
if(!is_array($needle)) $needle = array($needle);
foreach($needle as $query) {
$res=strpos($haystack, $query, $offset);
if($res !== false) return $res; // stop on first true result
}
return false;
}
Just an upgrade from above answers
function strsearch($findme, $source){
if(is_array($findme)){
if(str_replace($findme, '', $source) != $source){
return true;
}
}else{
if(strpos($source,$findme)){
return true;
}
}
return false;
}
<?php
$Words = array("hello","there","world");
$c = 0;
$message = 'Hi hello';
foreach ($Words as $word):
$trial = stripos($message,$word);
if($trial != true){
$c++;
echo 'Word '.$c.' didnt match <br> <br>';
}else{
$c++;
echo 'Word '.$c.' matched <br> <br>';
}
endforeach;
?>
I used this kind of code to check for hello, It also Has a numbering feature.
You can use this if you want to do content moderation practices in websites that need the user to type