Dirk J. Faber
Dirk J. Faber

Reputation: 4701

string replace two matches that have an exact (partly) match within one string

I have a variable $text which is a plain text that can contain one or more email addresses in a line of text. I use a regular expression to find these email addresses and then transform them into clickable <a href="mailto:....etc addresses. This is my code with an example that work fine:

$text = "this is the text that has a [email protected] in it and also [email protected].";

if(preg_match_all('/[\p{L}0-9_.-]+@[0-9\p{L}.-]+\.[a-z.]{2,6}\b/u',$text,$mails)){

foreach($mails[0] as $mail ){
    $text = str_replace($mail,'<a href="mailto:'.$mail.'">'.$mail.'</a>',$text);
    }
}

Or see this live demo. Problems occur when in my variable $text there are two email adresses that have an exact (partial) match. For example [email protected] and [email protected]. Here's another live demo. The problem is the string replace happens within the partial match as well (because it is also a full match). How to bypass this issue?

Upvotes: 1

Views: 168

Answers (3)

Stephanie Temple
Stephanie Temple

Reputation: 351

Like so...

<?php

$string = "this is the text that has a [email protected] in it and also [email protected].";

$search = array ( "!(\s)([_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3})!i",  
"!^([_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3})!i" );

$replace = array ( "\\1<a href=\"mailto:\\2\">\\2</a>", 
"<a href=\"mailto:\\1\">\\1</a>" );

echo preg_replace ( $search, $replace, $string );

?>

result...

this is the text that has a <a href="mailto:[email protected]">[email protected]</a> in it and also <a href="mailto:[email protected]">[email protected]</a>.

Upvotes: 1

BritishWerewolf
BritishWerewolf

Reputation: 3968

Why not use preg_replace?
str_replace can overwrite previous matches.

This should be good for you:

echo preg_replace(
    '/([\p{L}0-9_.-]+@[0-9\p{L}.-]+\.[a-z.]{2,6}\b)/u',
    '<a href="mailto:$1">$1</a>',
    $text
);

Notice that I had to slightly modify the regular expression and wrap it in parentheses.
This is so that I can reference it in the replacement.

Live demo

Upvotes: 2

Romain B.
Romain B.

Reputation: 654

You have to catch the caracter before your match to be sure it's a full match :

if(preg_match_all('/(.)([\p{L}0-9_.-]+@[0-9\p{L}.-]+\.[a-z.]{2,6}\b)/u',$text,$mails))

-----------------------------------^

Then you just have to modify a bit your str_replace parameter
var_dump($mails);

$id = 0;
foreach($mails[2] as $mail ){
   $text = str_replace($mails[1][$id].$mail,'$mails[1][$id].<a href="mailto:'.$mail.'">'.$mail.'</a>',$text);
   $id ++;
}

For example : https://3v4l.org/qYpHo

Upvotes: 1

Related Questions