102

I am trying to detect whether a string contains at least one URL that is stored in an array.

Here is my array:

$owned_urls = array('website1.com', 'website2.com', 'website3.com');

The string is entered by the user and submitted via PHP. On the confirmation page I would like to check if the URL entered is in the array.

I have tried the following:

$string = 'my domain name is website3.com';
if (in_array($string, $owned_urls))
{
    echo "Match found"; 
    return true;
}
else
{
    echo "Match not found";
    return false;
}

No matter what is inputted the return is always "Match not found".

Is this the correct way of doing things?

Kirk Beard
  • 9,569
  • 12
  • 43
  • 47
danyo
  • 5,686
  • 20
  • 59
  • 119

15 Answers15

114

Try this.

$string = 'my domain name is website3.com';
foreach ($owned_urls as $url) {
    //if (strstr($string, $url)) { // mine version
    if (strpos($string, $url) !== FALSE) { // Yoshi version
        echo "Match found"; 
        return true;
    }
}
echo "Not found!";
return false;

Use stristr() or stripos() if you want to check case-insensitive.

Daniele Vrut
  • 2,835
  • 2
  • 22
  • 32
  • 8
    from the manual: `**Note**: If you only want to determine if a particular needle occurs within haystack, use the faster and less memory intensive function strpos() instead.` – Yoshi Oct 18 '13 at 09:30
  • 7
    @danyo this won't work if the user enters a domain like `site3.com`. It will match `mysite3.com` when it shouldn't – billyonecan Oct 18 '13 at 09:44
  • it will be fine for it's intended use, as the urls will be limited to 2 or 3 preset ones – danyo Oct 18 '13 at 09:49
39

This was a lot easier to do if all you want to do is find a string in an array.

$array = ["they has mystring in it", "some", "other", "elements"];
if (stripos(json_encode($array),'mystring') !== false) {
echo "found mystring";
}
Sohel Ahmed Mesaniya
  • 3,344
  • 1
  • 23
  • 29
Joseph Philbert
  • 635
  • 7
  • 8
  • 2
    Your input array is actually a string. – Burgi Dec 22 '17 at 11:16
  • 4
    I think this is the **BEST ANSWER** but didn't receive upvotes because of the simple mistake in the code. @Burgi I edited the answer and now it is array and even more, multiple sub arrays and his method still works very well!! – Tarik Mar 13 '18 at 08:34
  • This works well but it doesn't tell you which key the array matched with. – ahinkle Sep 06 '19 at 18:59
  • 5
    If you are going to convert the array to a string before checking, why not just implode? A good reason NOT to use `json_encode()` is if your array has characters in it that `json_encode()` will escape or mutate. This mutation risk is enough reason not to suggest this technique as a general use tool. **Demonstration of failure: https://3v4l.org/RjPbX** – mickmackusa Feb 04 '21 at 20:22
  • Ha i missed all the comments here but thanks to @Sohel Ahme Mesaniya it got fixed. Anyway, mickmackusa's comment is very relevant. – Thomas Fellinger Aug 31 '21 at 18:52
  • Have edited to change `json_encode` to `implode()` - as @mickmackusa says, with json_encode there's a high risk of getting false positives due to escape characters, delimiters and any other JSON syntax that gets added. Using `implode()` allows you to edit the "glue" (first parameter) and introduces no further characters, which provides a lot more control and helps prevent false positives. It's also faster / less process heavy. – AutoBaker May 09 '23 at 06:59
  • @AutoBaker and @mickmackusa I've tested this myself, and `implode` does not work. I don't know what your edit was, but it may have been rolled back, anyhow simply replacing `json_encode($array)` with `implode(" ", $array)` is not sufficient enough to make it work, at least from my tests. Using `json_encode` is still by far the best answer. – Studocwho Jun 05 '23 at 02:01
28

Try this:

$owned_urls= array('website1.com', 'website2.com', 'website3.com');

$string = 'my domain name is website3.com';

$url_string = end(explode(' ', $string));

if (in_array($url_string,$owned_urls)){
    echo "Match found"; 
    return true;
} else {
    echo "Match not found";
    return false;
}

- Thanks

Anand Solanki
  • 3,419
  • 4
  • 16
  • 27
  • 10
    This assumes that the strings are separated by space. e.g. it won't work for the following string `My url is https://website3.com` – Елин Й. Jul 21 '17 at 07:31
  • 6
    and even wont't work for 'I have the website3.com domain'. This assumes the string is in the end, which you can't when working with user-committed text – Samuël Visser Nov 29 '17 at 14:13
  • I agree with the other two commenters about why too much is assumed of the input data. This is not a great answer for researchers to rely upon because it is narrow in its functionality. Plus, the answer is missing its educational explanation. "Try this" answers miss an opportunity to educate/empower thousands of researchers. – mickmackusa Feb 04 '21 at 20:52
  • `end` function wants an array reference as it's input parameter, it would be better to assign the result of `explode(' ', $string)` to a variable, and then feed it into `end($yourArray)`. Cheers – funder7 Jan 19 '22 at 12:33
26

Simple str_replace with count parameter would work here:

$count = 0;
str_replace($owned_urls, '', $string, $count);
// if replace is successful means the array value is present(Match Found).
if ($count > 0) {
  echo "One of Array value is present in the string.";
}

More Info - https://www.techpurohit.in/extended-behaviour-explode-and-strreplace-php

jitendrapurohit
  • 9,435
  • 2
  • 28
  • 39
  • Nice , I have one doubt .. this works fine for matching the url of string ... I have a string $string = 'you-are-nice'; $string2 = 'you-are-nicer'; and my $match = 'nice'; I need to match the word nice , not nicer even if my match string is nice ... – Srinivas08 Oct 05 '17 at 08:47
  • @Srinivas08 considering word boundaries is a reasonable consideration, but not one that can be implemented with `str_replace()`. – mickmackusa Feb 04 '21 at 20:13
  • 1
    The most prominent reason that devs should not implement this concise technique is because there is no way to "return early". In other words, after a replacement is made, there is absolutely no reason to continue searching for replacements, so the process _should_ stop -- but it cannot. For all tasks where a binary "found" or "not found" result is sought, this technique should not be used as it will do useless additional iterating. – mickmackusa Feb 04 '21 at 20:15
9

I think that a faster way is to use preg_match.

$user_input = 'Something website2.com or other';
$owned_urls_array = array('website1.com', 'website2.com', 'website3.com');

if ( preg_match('('.implode('|',$owned_urls_array).')', $user_input)){
    echo "Match found"; 
}else{
    echo "Match not found";
}
vencedor
  • 663
  • 7
  • 9
  • 4
    Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation](https://meta.stackexchange.com/q/114762) would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please [edit](https://meta.stackoverflow.com/posts/360251/edit) your answer to add some explanation, including the assumptions you’ve made. [ref](https://meta.stackoverflow.com/a/360251/8371915) – Alper t. Turker Jan 15 '18 at 14:07
  • To be more secure, dots must be escaped in pattern : `addcslashes(implode('|', $owned_urls_array, '.'))` – Rey0bs May 03 '18 at 12:10
  • 1
    less code, but definitely much MUCH slower than strpos – hndcrftd Jan 18 '20 at 00:32
  • 1
    Regex will be slower than `strpos()` in a head-to-head battle. I think the only reason to use `preg_` for this type of task is if you wanted to include wordboundaries for increased accuracy. Do not use `addcslashes()` to escape special characters in regex, `preg_quote()` is specifically designed to take care of this necessity. – mickmackusa Feb 04 '21 at 20:28
  • 1
    Also, the capture group is useless in the pattern. – mickmackusa Feb 04 '21 at 20:47
8
$string = 'my domain name is website3.com';
$a = array('website1.com','website2.com','website3.com');

$result = count(array_filter($a, create_function('$e','return strstr("'.$string.'", $e);')))>0; 
var_dump($result );

output

bool(true)
RafH
  • 4,504
  • 2
  • 23
  • 23
  • 4
    for reference; `create_function` is deprecated in PHP 7.2 – Darryl E. Clarke Oct 03 '17 at 19:45
  • This answer is missing its educational explanation. The php manual states that `strstr()` should not be used to check for the existence of a string inside of a string -- it recommends `strpos()` for efficiency reasons. The `count(` and `)>0` can be removed and `(bool)` added before `array_filter()` to convert the empty/non-empty array to `false`/`true` respectively. – mickmackusa Feb 04 '21 at 20:50
6

Here is a mini-function that search all values from an array in a given string. I use this in my site to check for visitor IP is in my permitted list on certain pages.

function array_in_string($str, array $arr) {
    foreach($arr as $arr_value) { //start looping the array
        if (stripos($str,$arr_value) !== false) return true; //if $arr_value is found in $str return true
    }
    return false; //else return false
}

how to use

$owned_urls = array('website1.com', 'website2.com', 'website3.com');

//this example should return FOUND
$string = 'my domain name is website3.com';
if (array_in_string($string, $owned_urls)) {
    echo "first: Match found<br>"; 
}
else {
    echo "first: Match not found<br>";
}

//this example should return NOT FOUND
$string = 'my domain name is website4.com';
if (array_in_string($string, $owned_urls)) {
    echo "second: Match found<br>"; 
}
else {
    echo "second: Match not found<br>";
}

DEMO: http://phpfiddle.org/lite/code/qf7j-8m09

crisc2000
  • 1,082
  • 13
  • 19
4

You can concatenate the array values with implode and a separator of | and then use preg_match to search for the value.

Here is the solution I came up with ...

$emails = array('@gmail', '@hotmail', '@outlook', '@live', '@msn', '@yahoo', '@ymail', '@aol');
$emails = implode('|', $emails);

if(!preg_match("/$emails/i", $email)){
 // do something
}
FarrisFahad
  • 356
  • 1
  • 4
  • 17
  • `preg_match()` is needless overhead for the search after imploding. There is no reason to not use `stripos()` with the same effect. I do not endorse this technique -- it is working harder than it needs to and is at risk of breaking if the `$emails` strings contain characters with special meaning to the regex engine. – mickmackusa Feb 04 '21 at 20:30
3

If your $string is always consistent (ie. the domain name is always at the end of the string), you can use explode() with end(), and then use in_array() to check for a match (as pointed out by @Anand Solanki in their answer).

If not, you'd be better off using a regular expression to extract the domain from the string, and then use in_array() to check for a match.

$string = 'There is a url mysite3.com in this string';
preg_match('/(?:http:\/\/)?(?:www.)?([a-z0-9-_]+\.[a-z0-9.]{2,5})/i', $string, $matches);

if (empty($matches[1])) {
  // no domain name was found in $string
} else {
  if (in_array($matches[1], $owned_urls)) {
    // exact match found
  } else {
    // exact match not found
  }
}

The expression above could probably be improved (I'm not particularly knowledgeable in this area)

Here's a demo

Community
  • 1
  • 1
billyonecan
  • 20,090
  • 8
  • 42
  • 64
  • Literal dots must be escaped in a regex pattern (unless in a character class). `\w` = `[A-Za-z0-9_]`. The `preg_match()` call itself can be placed inside of the first `if` to avoid making the `empty()` function call. – mickmackusa Feb 04 '21 at 20:46
2

You are checking whole string to the array values. So output is always false.

I use both array_filter and strpos in this case.

<?php
$urls= array('website1.com', 'website2.com', 'website3.com');
$string = 'my domain name is website3.com';
$check = array_filter($urls, function($url){
    global $string;
    if(strpos($string, $url))
        return true;
});
echo $check?"found":"not found";
revo
  • 47,783
  • 14
  • 74
  • 117
1
$owned_urls= array('website1.com', 'website2.com', 'website3.com');
    $string = 'my domain name is website3.com';
    for($i=0; $i < count($owned_urls); $i++)
    {
        if(strpos($string,$owned_urls[$i]) != false)
            echo 'Found';
    }   
Sandesh
  • 349
  • 2
  • 8
  • This answer is missing its educational explanation (or compelling statement why a researcher should use this technique over other techniques). The loop does not need to check `count($owned_urls)` on every iteration since the count never changes. The loop should `break` as soon as the first match is found. – mickmackusa Feb 04 '21 at 20:44
0
    $message = "This is test message that contain filter world test3";

    $filterWords = array('test1', 'test2', 'test3');

    $messageAfterFilter =  str_replace($filterWords, '',$message);

    if( strlen($messageAfterFilter) != strlen($message) )
        echo 'message is filtered';
    else
        echo 'not filtered';
  • If bothering to "check by mutation" (I wouldn't), then just use this earlier posted and simpler answer: https://stackoverflow.com/a/40543118/2943403 (both answers suffer from inefficiency as neither have any way of halting when a match is found) – mickmackusa Feb 04 '21 at 20:35
0

I find this fast and simple without running loop.

$array = array("this", "that", "there", "here", "where");
$string = "Here comes my string";
$string2 = "I like to Move it! Move it";

$newStr = str_replace($array, "", $string);

if(strcmp($string, $newStr) == 0) {
    echo 'No Word Exists - Nothing got replaced in $newStr';
} else {
    echo 'Word Exists - Some Word from array got replaced!';
}

$newStr = str_replace($array, "", $string2);

if(strcmp($string2, $newStr) == 0) {
    echo 'No Word Exists - Nothing got replaced in $newStr';
} else {
    echo 'Word Exists - Some Word from array got replaced!';
}

Little explanation!

  1. Create new variable with $newStr replacing value in array of original string.

  2. Do string comparison - If value is 0, that means, strings are equal and nothing was replaced, hence no value in array exists in string.

  3. if it is vice versa of 2, i.e, while doing string comparison, both original and new string was not matched, that means, something got replaced, hence value in array exists in string.

user3767643
  • 674
  • 2
  • 9
  • 25
  • If bothering to "check by mutation" (I wouldn't), then just use this earlier posted and simpler answer: https://stackoverflow.com/a/40543118/2943403 (both answers suffer from inefficiency as neither have any way of halting when a match is found) – mickmackusa Feb 04 '21 at 20:42
0
  $search = "web"
    $owned_urls = array('website1.com', 'website2.com', 'website3.com');
          foreach ($owned_urls as $key => $value) {
         if (stristr($value, $search) == '') {
        //not fount
        }else{
      //found
       }

this is the best approach search for any substring , case-insensitive and fast

just like like im mysql

ex:

select * from table where name = "%web%"

Yahya Ayyoub
  • 322
  • 2
  • 10
  • The php manual states that `strstr()` (or `stristr()` in this context) should not be used to determine the existence of a string in another string. For this purpose, the php manual recommends `strpos()`/`stripos()` for efficiency reasons. – mickmackusa Feb 04 '21 at 20:41
0

I came up with this function which works for me, hope this will help somebody

$word_list = 'word1, word2, word3, word4';
$str = 'This string contains word1 in it';

function checkStringAgainstList($str, $word_list)
{
  $word_list = explode(', ', $word_list);
  $str = explode(' ', $str);

  foreach ($str as $word):
    if (in_array(strtolower($word), $word_list)) {
        return TRUE;
    }
  endforeach;

  return false;
}

Also, note that answers with strpos() will return true if the matching word is a part of other word. For example if word list contains 'st' and if your string contains 'street', strpos() will return true

Da Beginer
  • 78
  • 9
  • This is very inefficient. The OP does not ask for two strings to be exploded on spaces. This answer is unnecessarily complex, risks breaking up a string that should not return a match but then returns a match because unordered individual substrings all qualify. I do not recommend this approach since it is prone to failures. – mickmackusa Feb 04 '21 at 20:40