在PHP中将纯文本URL转换为HTML超链接

65

我有一个简单的评论系统,人们可以在纯文本字段中提交超链接。当我从数据库中将这些记录显示回网页时,我应该使用PHP中的什么RegExp来将这些链接转换为HTML类型的锚链接呢?

我不希望算法对任何其他类型的链接执行此操作,只想针对http和https。


1
今天有一个类似的问题被问到了:https://dev59.com/vnI-5IYBdhLWcg3wO1rl - BalusC
3
查看问题:*https://dev59.com/PHM_5IYBdhLWcg3w3nbz - Søren Løvborg
15个回答

86

这里有另一个解决方案,它会捕获所有的http/https/www链接并将其转换为可点击的链接。

$url = '~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(?<![\.,:])~i'; 
$string = preg_replace($url, '<a href="$0" target="_blank" title="$0">$0</a>', $string);
echo $string;

或者,如果只是想捕获 http/https,则使用下面的代码。

$url = '/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/';   
$string= preg_replace($url, '<a href="$0" target="_blank" title="$0">$0</a>', $string);
echo $string;

编辑: 以下脚本将捕获所有URL类型,并将它们转换为可点击的链接。

$url = '@(http)?(s)?(://)?(([a-zA-Z])([-\w]+\.)+([^\s\.]+[^\s]*)+[^,.\s])@';
$string = preg_replace($url, '<a href="http$2://$4" target="_blank" title="$0">$0</a>', $string);
echo $string;
新的更新,如果你想去除字符串中的“s”,请使用以下代码块,感谢@AndrewEllis指出。
$url = '@(http(s)?)?(://)?(([a-zA-Z])([-\w]+\.)+([^\s\.]+[^\s]*)+[^,.\s])@';
$string = preg_replace($url, '<a href="http$2://$4" target="_blank" title="$0">$0</a>', $string);
echo $string;

以下是URL未正确显示的非常简单的解决方案。

$email = '<a href="mailto:email@email.com">email@email.com</a>';
$string = $email;
echo $string;

这是一个非常简单的修复方法,但您需要根据自己的目的进行修改。

我提供了多个答案,因为某些服务器的设置不同,因此一种答案可能适用于某些服务器,但对其他服务器可能无效。但我希望这些答案对您有效,如果不行,请告诉我,希望我能提出另一种解决方案。

有多个脚本,因为某些 PHP 文件需要不同的脚本,另外一些服务器的设置也不同,还有每个服务器都有不同的要求,有些只需要 HTTP/S,有些需要 WWW,有些需要 FTP/S,具体取决于用户的脚本如何设置,我在每个脚本中都提供了一些文本,说明它们的功能。


1
更新后的解决方案效果很好,但对于像g.page这样只有一个字符的域名实例除外。修复方法很简单,但除了我的用例之外没有进行广泛测试。只需将模式的第36个位置处的“+”更改为“*”。所以现在变成$url = '@(http(s)?)?(://)?(([a-zA-Z])([-\w]*\.)+([^\s\.]+[^\s]*)+[^,.\s])@'; - affan
小心,它并没有很好地工作...例如,如果有人在文本中输入www.example.com,那么你的URL将会被破坏为https://your.actualserver.url/www.example.com。 - undefined

40

嗯,Volomike的答案更接近正确。为了进一步完善它,我让它忽略了超链接末尾的句点,并考虑了URI片段。

public static function makeClickableLinks($s) {
  return preg_replace('@(https?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@', '<a href="$1" target="_blank">$1</a>', $s);
}

9

请参考这篇文章,它介绍了将URL自动转换为超链接的PHP函数。WordPress也是采用类似的方法解决这个问题。

function _make_url_clickable_cb($matches) {
    $ret = '';
    $url = $matches[2];

    if ( empty($url) )
        return $matches[0];
    // removed trailing [.,;:] from URL
    if ( in_array(substr($url, -1), array('.', ',', ';', ':')) === true ) {
        $ret = substr($url, -1);
        $url = substr($url, 0, strlen($url)-1);
    }
    return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>" . $ret;
}

function _make_web_ftp_clickable_cb($matches) {
    $ret = '';
    $dest = $matches[2];
    $dest = 'http://' . $dest;

    if ( empty($dest) )
        return $matches[0];
    // removed trailing [,;:] from URL
    if ( in_array(substr($dest, -1), array('.', ',', ';', ':')) === true ) {
        $ret = substr($dest, -1);
        $dest = substr($dest, 0, strlen($dest)-1);
    }
    return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>" . $ret;
}

function _make_email_clickable_cb($matches) {
    $email = $matches[2] . '@' . $matches[3];
    return $matches[1] . "<a href=\"mailto:$email\">$email</a>";
}

function make_clickable($ret) {
    $ret = ' ' . $ret;
    // in testing, using arrays here was found to be faster
    $ret = preg_replace_callback('#([\s>])([\w]+?://[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]*)#is', '_make_url_clickable_cb', $ret);
    $ret = preg_replace_callback('#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]*)#is', '_make_web_ftp_clickable_cb', $ret);
    $ret = preg_replace_callback('#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret);

    // this one is not in an array because we need it to run last, for cleanup of accidental links within links
    $ret = preg_replace("#(<a( [^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i", "$1$3</a>", $ret);
    $ret = trim($ret);
    return $ret;
}

7

对于我来说,得分最高的答案无法解决我的问题,下面链接替换不正确:

http://www.fifa.com/worldcup/matches/round255951/match=300186487/index.html#nosticky

经过一些谷歌搜索和测试,我发现了以下方法:

public static function replaceLinks($s) {
    return preg_replace('@(https?://([-\w\.]+)+(:\d+)?(/([\w/_\.%-=#]*(\?\S+)?)?)?)@', '<a href="$1">$1</a>', $s);
}

我并不是正则表达式的专家,实际上它常常让我感到困惑 :)

因此,请随意发表评论并改进这个解决方案。


7
<?
function makeClickableLinks($text)
{

        $text = html_entity_decode($text);
        $text = " ".$text;
        $text = eregi_replace('(((f|ht){1}tp://)[-a-zA-Z0-9@:%_\+.~#?&//=]+)',
                '<a href="\\1" target=_blank>\\1</a>', $text);
        $text = eregi_replace('(((f|ht){1}tps://)[-a-zA-Z0-9@:%_\+.~#?&//=]+)',
                '<a href="\\1" target=_blank>\\1</a>', $text);
        $text = eregi_replace('([[:space:]()[{}])(www.[-a-zA-Z0-9@:%_\+.~#?&//=]+)',
        '\\1<a href="http://\\2" target=_blank>\\2</a>', $text);
        $text = eregi_replace('([_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3})',
        '<a href="mailto:\\1" target=_blank>\\1</a>', $text);
        return $text;
}

// Example Usage
echo makeClickableLinks("This is a test clickable link: http://www.websewak.com  You can also try using an email address like test@websewak.com");
?>

5

以下是我的代码,用于格式化文本中的所有链接,包括带和不带协议的电子邮件和网址。

public function formatLinksInText($text)
{
    //Catch all links with protocol      
    $reg = '/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(\/\S*)?/'; 
    $formatText = preg_replace($reg, '<a href="$0" style="font-weight: normal;" target="_blank" title="$0">$0</a>', $text);

    //Catch all links without protocol
    $reg2 = '/(?<=\s|\A)([0-9a-zA-Z\-\.]+\.[a-zA-Z0-9\/]{2,})(?=\s|$|\,|\.)/';
    $formatText = preg_replace($reg2, '<a href="//$0" style="font-weight: normal;" target="_blank" title="$0">$0</a>', $formatText);

    //Catch all emails
    $emailRegex = '/(\S+\@\S+\.\S+)\b/';
    $formatText = preg_replace($emailRegex, '<a href="mailto:$1" style="font-weight: normal;" target="_blank" title="$1">$1</a>', $formatText);
    $formatText = nl2br($formatText);
    return $formatText;
}

请评论不起作用的网址。我将尝试更新正则表达式。

3
尝试这个:

试试这个:

$s = preg_replace('/(?<!href="|">)(?<!src=\")((http|ftp)+(s)?:\/\/[^<>\s]+)/is', '<a href="\\1" target="_blank">\\1</a>', $s);

如果已经存在 href,它会跳过现有的链接(不会在 href 内添加 href)。否则,它将添加带有空白目标的 a href。


这个与许多其他的不同,考虑到嵌入式图像(即通过富文本编辑器)的潜力 - 关键在于(?<!src=")。 - Tarquin

3

MkVal的回答是可以的,但是如果我们已经有了锚链接,它会以奇怪的格式呈现文本。

这是对我有效的解决方案在两种情况下都可行:

$s = preg_replace ( 
    "/(?<!a href=\")(?<!src=\")((http|ftp)+(s)?:\/\/[^<>\s]+)/i",
    "<a href=\"\\0\" target=\"blank\">\\0</a>",
    $s
);

2
<?php
/**
 * Turn all URLs in clickable links.
 * 
 * @param string $value
 * @param array  $protocols  http/https, ftp, mail, twitter
 * @param array  $attributes
 * @return string
 */
public function linkify($value, $protocols = array('http', 'mail'), array $attributes = array())
{
    // Link attributes
    $attr = '';
    foreach ($attributes as $key => $val) {
        $attr .= ' ' . $key . '="' . htmlentities($val) . '"';
    }
    
    $links = array();
    
    // Extract existing links and tags
    $value = preg_replace_callback('~(<a .*?>.*?</a>|<.*?>)~i', function ($match) use (&$links) { return '<' . array_push($links, $match[1]) . '>'; }, $value);
    
    // Extract text links for each protocol
    foreach ((array)$protocols as $protocol) {
        switch ($protocol) {
            case 'http':
            case 'https':   $value = preg_replace_callback('~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(?<![\.,:])~i', function ($match) use ($protocol, &$links, $attr) { if ($match[1]) $protocol = $match[1]; $link = $match[2] ?: $match[3]; return '<' . array_push($links, "<a $attr href=\"$protocol://$link\">$link</a>") . '>'; }, $value); break;
            case 'mail':    $value = preg_replace_callback('~([^\s<]+?@[^\s<]+?\.[^\s<]+)(?<![\.,:])~', function ($match) use (&$links, $attr) { return '<' . array_push($links, "<a $attr href=\"mailto:{$match[1]}\">{$match[1]}</a>") . '>'; }, $value); break;
            case 'twitter': $value = preg_replace_callback('~(?<!\w)[@#](\w++)~', function ($match) use (&$links, $attr) { return '<' . array_push($links, "<a $attr href=\"https://twitter.com/" . ($match[0][0] == '@' ? '' : 'search/%23') . $match[1]  . "\">{$match[0]}</a>") . '>'; }, $value); break;
            default:        $value = preg_replace_callback('~' . preg_quote($protocol, '~') . '://([^\s<]+?)(?<![\.,:])~i', function ($match) use ($protocol, &$links, $attr) { return '<' . array_push($links, "<a $attr href=\"$protocol://{$match[1]}\">{$match[1]}</a>") . '>'; }, $value); break;
        }
    }
    
    // Insert all link
    return preg_replace_callback('/<(\d+)>/', function ($match) use (&$links) { return $links[$match[1] - 1]; }, $value);
}

这不是我的代码,我从这里获取的:https://gist.github.com/jasny/2000705


2
public static function makeClickableLinks($s) {
    return preg_replace('@(https?://([-\w\.]+)+(:\d+)?(/([\w/_\.-]*(\?\S+)?)?)?)@', '<a href="$1">$1</a>', $s);
}

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