0

I currently have the following regex to capture link text and a URL in the following format:

[Link](http://link.com)

\[(.+)]\(((https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,}))\)

When I add another expression afterwards to linkify URLs, it messes up ones in the above format.

Is there a singular regular expression to handle both cases?

http://link.com -> <a href="http://link.com" target="_blank">http://link.com</a>

[Link](http://link.com) -> <a href="http://link.com" target="_blank">Link</a>

PHP:

$string = preg_replace('/\[(.+)]\(((https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,}))\)/', '<a href="$2" target="_blank">$1</a>', $string);
frosty
  • 2,779
  • 6
  • 34
  • 63

3 Answers3

2

There's no real ways to identify an url in a string since the url syntax can be very complicated (too complicated to be clear). In other words, you must accept that something that looks like [...](...) stands for a link without to try to verify if the content between ( and ) is really an URL. (You can always use parse_url after, but keep in mind that it may exclude valid urls).

What you are looking for is:

$result = preg_replace('~\[([^]]*)]\([^)]*\)~', '<a href="$2" target="_blank">$1</a>', $str);

// If you want to hunt lonely urls in your text, you can always search
// after extracting text nodes with XPath and a naive pattern like this:

$dom = new DOMDocument;
$dom->loadHTML($result);

$xp = new DOMXPath($dom);
$textNodes = $xp->query('//text()');

foreach($textNodes as $textNode) {
    $textNode->nodeValue = preg_replace('~[hw](?:(?<=\bh)ttps?://|(?<=\bw)ww\.)\S+~i', '<a href="$0" target="_blank">$0</a>~', $textNode->nodeValue);
}

$result = $dom->saveHTML();

Note: for better results, if you absolutely want to check the url, you can use the same pattern with preg_replace_callback, remove the last character of the match until parse_url works and perform the replacement, but it will not be very performant.

Casimir et Hippolyte
  • 88,009
  • 5
  • 94
  • 125
0

Maybe this help you a bit:

/**
 * Linkify Function
 * @param $tweet
 * @return mixed
 */
function linkify_tweet($tweet)
{
//Convert urls to <a> links
$tweet = preg_replace("/([\w]+\:\/\/[\w-?&;#~=\.\/\@]+[\w\/])/", "<a href=\"mailto:w2m@bachecubano.com?subject=WEB $1\">$1</a>", $tweet);

//Convert hashtags to twitter searches in <a> links
$tweet = preg_replace("/#([A-Za-z0-9\/\.]*)/", "<a href=\"#\">#$1</a>", $tweet);

//Convert attags to twitter profiles in <a> links
$tweet = preg_replace("/@([A-Za-z0-9\/\.]*)/", "<a href=\"mailto:w2m@bachecubano.com?subject=MSG @$1\" class=\"userlink\">@$1</a>", $tweet);

return $tweet;
}
Erich García
  • 1,648
  • 21
  • 30
0

First deal with markdown syntax. Then catch plain links that were not processed - you may use similar regexp, but without parethesis. If you want to replace everything that looks like an url within whitespace limits (html won't match) then this will do:

\s(https?:\/\/(?:www\.|(?!www))[^\s.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})

shudder
  • 2,076
  • 2
  • 20
  • 21