如何修复这个preg_replace代码?

时间:2013-10-30 20:52:03

标签: php url preg-replace

使用以下代码将邮件中的网址转换为HTML链接:

$message = preg_replace("#(http|https|ftp|ftps)://([.]?[&;%=a-zA-Z0-9_/?-])*#",
    "<a href=\"away?to=\\0\" target=\"_blank\">\\0</a>", $message);

$message = preg_replace("#(^| |\n)(www([.]?[&;%=a-zA-Z0-9_/?-])*)#",
    "\\1<a href=\"away?to=http://\\2\" target=\"_blank\">\\2</a>", $message);

除了下列情况外,它几乎适用于所有链接:

1) http://example.com/mediathek#/video/1976914/zoom:-World-Wide

此处的问题是链接中的#:,因为不会转换完整的链接。

2) If someone just writes "www" in a message

示例:<a href="http://www">www</a>

所以问题是在上面的代码中是否有任何方法可以修复这两种情况

3 个答案:

答案 0 :(得分:2)

由于您希望将哈希#)包含在正则表达式中,因此您需要将分隔符更改为正则表达式中未包含的字符,例如!。所以,你的正则表达式应该是这样的:

$message = preg_replace("!(http|https|ftp|ftps)://([.]?[&;%#:=a-zA-Z0-9_/?-])*!",
"<a href=\"away?to=\\0\" target=\"_blank\">\\0</a>", $message);

这有帮助吗?

但是,如果您希望更多地遵循规范(RCF 1738),则可能希望排除URL中不允许的%。还有一些你不包括的允许的字符:

  • $
  • _
  • 。 (点)
  • +
  • <!/ LI>
  • *

如果您要包含这些字符,则应使用%对正则表达式进行分隔。

答案 1 :(得分:1)

夫妻小调整。将\#:添加到第一个正则表达式,然后将*更改为第二个正则表达式中的+

$message = preg_replace("#(http|https|ftp|ftps)://([.]?[&;%=a-zA-Z0-9_/?\#:-])*#",
    "<a href=\"away?to=\\0\" target=\"_blank\">\\0</a>", $message);

$message = preg_replace("#(^| |\n)(www([.]?[&;%=a-zA-Z0-9_/?-])+)#",
    "\\1<a href=\"away?to=http://\\2\" target=\"_blank\">\\2</a>", $message);

答案 2 :(得分:1)

在我看来,解决这个问题是徒劳的。一个很好的选择是通过regex (从协议开始:http,ftp,mail ...或www)找到可能是URL的URL,然后使用FILTER_VALIDATE_URL进行测试。请记住,这个过滤器不是防水方式,因为PHP手册说:

"Note that the function will only find ASCII URLs to be valid; internationalized domain names (containing non-ASCII characters) will fail."

代码示例(未测试):

$message = preg_replace_callback(
    '~(?(DEFINE)
          (?<prot> (?>ht|f) tps?+ :// )         # you can add protocols here
      )
      (?>
          <a\b (?> [^<]++ | < (?!/a>) )++ </a>  # avoid links inside "a" tags
        |
          <[^>]++>                              # and tags attributes.
      ) (*SKIP)(?!)                             # makes fail the subpattern.
      |                                         # OR
      \b(?>(\g<prot>)|www\.)(\S++)              # something that begins with
                                                # "http://" or "www."
     ~xi',
    function ($match) {
        if (filter_var($match[2], FILTER_VALIDATE_URL)) {
            $url = (empty($match[1])) ? 'http://' : '';
            $url .= $match[0];
            return '<a href="away?to=' . $url . '"target="_blank">'
                 . $url . '</a>';
        } else { return $match[0] }
    },
    $message);