在文本块中使链接可点击的最佳方法

23
我想要:
Here is link: http://google.com
And http://example.com inside.
And another one at the very end: http://test.net

成为:

Here is link: <a href="http://google.com">http://google.com</a>
And <a href="http://example.com">http://example.com</a> inside.
And another one at the very end: <a href="http://test.net">http://test.net</a>
似乎是一项微不足道的任务,但我找不到一个可行的PHP函数。你有什么想法吗?

看起来很简单的任务,但我找不到一个可用的PHP函数。你有什么想法吗?

function make_links_clickable($text){
    // ???
}

$text = 'Here is link: http://google.com
And http://example.com inside.
And another one at the very end: http://test.net';

echo make_links_clickable($text);
5个回答

51

使用以下代码(适用于ftp、http、ftps和https协议):

function make_links_clickable($text){
    return preg_replace('!(((f|ht)tp(s)?://)[-a-zA-Zа-яА-Я()0-9@:%_+.~#?&;//=]+)!i', '<a href="$1">$1</a>', $text);
}

你在正则表达式的开头和结尾放置了什么! - Marco Demaio
1
@NeilHillman:它们被包含在内是为了支持 Cyrillic 域名(А 和 Я 都是 Cyrillic 字符)更多信息维基百科 - rekamoyka
2
这对于不以http://开头的链接是行不通的。我建议添加一个str_replace查找www.并在前面添加http://。然后我会再添加另一个str_replace检查双htt p:// htt p://以将其替换为一个。 - Gilly
1
@Akarun 如果链接是这样的呢?(https://www.sample.com/request)。函数将包含末尾的 ")" 和 ".",并且看起来像这样 <a href="https://www.sample.com/request).">https://www.sample.com/request).</a> - c.k
有没有办法根据链接文本是否包含特定的类或ID来使其工作? - Garconis
显示剩余3条评论

6
function makeClickableLinks($text)
{
$text = html_entity_decode($text);
$text = " ".$text;
$text= preg_replace("/(^|[\n ])([\w]*?)([\w]*?:\/\/[\w]+[^ \,\"\n\r\t<]*)/is", "$1$2<a href=\"$3\" >$3</a>", $text);  
$text= preg_replace("/(^|[\n ])([\w]*?)((www|wap)\.[^ \,\"\t\n\r<]*)/is", "$1$2<a href=\"http://$3\" >$3</a>", $text);
$text= preg_replace("/(^|[\n ])([\w]*?)((ftp)\.[^ \,\"\t\n\r<]*)/is", "$1$2<a href=\"$4://$3\" >$3</a>", $text);  
$text= preg_replace("/(^|[\n ])([a-z0-9&\-_\.]+?)@([\w\-]+\.([\w\-\.]+)+)/i", "$1<a href=\"mailto:$2@$3\">$2@$3</a>", $text);  
$text= preg_replace("/(^|[\n ])(mailto:[a-z0-9&\-_\.]+?)@([\w\-]+\.([\w\-\.]+)+)/i", "$1<a href=\"$2@$3\">$2@$3</a>", $text);  
$text= preg_replace("/(^|[\n ])(skype:[^ \,\"\t\n\r<]*)/i", "$1<a href=\"$2\">$2</a>", $text);  
        return $text;
}

需要与以下网站进行合作:

www.example.com

https://www.example.com

http://www.example.com

wap.example.com

ftp.example.com

user@example.com

skype:example

mailto:user@example.com

atherprotocol://example.com


2
这些连续的替换波浪并没有得到解释。这个答案是不完整的,任何使用它的人都可能会盲目地复制粘贴。这个答案应该被编辑得更加慷慨/教育性。应该只有一个 preg_replace() 调用;应该有一个模式数组和一个替换数组。 - mickmackusa

5
尝试像这样做:

试试以下方法:

function make_links_clickable($text)
{
   return preg_replace ('/http:\/\/[^\s]+/i', "<a href=\"${0}\">${0}</a>", $text);
} 

$result = make_links_clickable($text);

Akarun和Lawrence Cherone建议的链接提供的解决方案更完整,因为它们检查URL是否为有效的URL,还可以检测https和ftp链接,在显示链接之前对其进行URL解码。我的解决方案比较简单粗暴,但对于简单任务应该也能够工作。 - Marco Demaio
这个答案缺少教育性的解释。 - mickmackusa

1

受Akarun答案的启发,下面的函数将仅将非链接文本转换为链接。新增功能是检查捕获文本链接是否已经存在于目标字符串中:

function make_links_from_http($content) {   
    // Links out of text links
    preg_match_all('!(((f|ht)tp(s)?://)[-a-zA-Zа-яА-Я()0-9@:%_+.~#?&;//=]+)!i', $content, $matches);
    foreach ($matches[0] as $key=>$link) {
        if (!preg_match('!<a(.*)'.$link.'(.*)/a>!i', $content))
        {
            $content = str_replace($link, '<a href="'.$link.'" target="_blank">'.$link.'</a>', $content);
        }
    }

    return $content;
} 

通过测试,我发现上述函数在第5行失败了。一个更加复杂的函数可以完成这个任务,如下所示:

function make_links_from_http($content) 
{
    // The link list
    $links = array();

    // Links out of text links
    preg_match_all('!(((f|ht)tp(s)?://)[-a-zA-Zа-яА-Я()0-9@:%_+.~#?&;//=]+)!i', $content, $matches);
    foreach ($matches[0] as $key=>$link) 
    {
        $links[$link] = $link;
    }

    // Get existing
    preg_match_all('/<a\s[^>]*href=([\"\']??)([^\" >]*?)\\1[^>]*>(.*)<\/a>/siU', $content, $matches);
    foreach ($matches[2] as $key=>$value)
    {
        if (isset($links[$value]))
        {
            unset($links[$value]);
        }
    }

    // Replace in content
    foreach ($links as $key=>$link)
    {
        $content = str_replace($link, '<a href="'.$link.'" target="_blank">'.$link.'</a>', $content);
    }

    return $content;
} 

对于新代码,我使用了以下教程: http://www.the-art-of-web.com/php/parse-links/


当您需要在一个 preg_ 调用中嵌套另一个 preg_ 调用时,通常意味着第一个正则表达式模式不够精细。当您只匹配出现次数以迭代并替换所匹配的内容时,这通常意味着您应该使用 preg_replace_callback() - mickmackusa

0
受Akarun答案的启发,我想出了这个函数来处理所有协议和以www.开头的链接。
function make_links($text, $class='', $target='_blank'){
    return preg_replace('!((http\:\/\/|ftp\:\/\/|https\:\/\/)|www\.)([-a-zA-Zа-яА-Я0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?!ism', 
    '<a class="'.$class.'" href="//$3" target="'.$target.'">$1$3</a>', 
    $text);
}

此函数有可选参数,可将类名添加到链接中,也可选择链接的目标,使其在新窗口/选项卡中打开... 默认情况下,该参数会将链接打开到新窗口/选项卡中,但如果您不想这样做,可以更改默认值或在调用函数时更改该值。

这个模式在模式中有大量不必要的转义,让人感到压抑。如果模式中没有任何字符“点”(.),请问自己为什么要使用s模式修饰符。然后请问自己,如果模式中没有锚定符号^$,为什么要使用m模式修饰符。http\:\/\/|https\:\/\/过于冗长。a-zA-Zа-яА-Я也是过于冗长的。即使这段代码能够实现预期的结果,我也不建议任何人使用它,因为它并没有教授最佳实践。 - mickmackusa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接