【问题标题】:PHP Linkify Links In ContentPHP Linkify 内容中的链接
【发布时间】:2011-02-22 16:22:27
【问题描述】:

我一直在做一个小项目,我发现自己需要一个 php 函数,它可以链接我数据中的 URL,同时使我能够在我不想链接的链接上设置一些例外.知道如何做到这一点吗?

【问题讨论】:

  • 链接容易识别吗?正确形成“example.com/someuri”?或者只是一些随机的文本,可能是主机名,也可能是目录或查询?
  • 我需要能够过滤掉 youtube 链接 - 所以它可能是 youtube.com/watch?v=cqhsunMOWKQ&feature=feedu
  • 以及不同媒体网站的许多其他链接,如 flickr、vimeo 等
  • 您确定要在 PHP 中执行此操作吗?使用 JQuery 在 javascript 中执行此操作相对容易。
  • 是的,我在 php 中需要它,因为我需要处理数据。是不是可以创建一个函数来传递一个顶级域数组来过滤掉并链接其余的域?我被困在这里了。

标签: php


【解决方案1】:

我在 GitHub 上有一个开源项目:LinkifyURL,您可能想考虑一下。它有一个功能:linkify() 从文本中提取 URL 并将它们转换为链接。请注意,正确完成这不是一项简单的任务! (请参阅:The Problem With URLs - 并且一定要阅读 cmets 的帖子以掌握所有可能出错的内容。)

如果您确实不需要链接特定域(即 vimeo 和 youtube),这里有一个修改后的 PHP 函数 linkify_filtered(以工作测试脚本的形式),可以满足您的需要:

<?php // test.php 20110313_1200

function linkify_filtered($text) {
    $url_pattern = '/# Rev:20100913_0900 github.com\/jmrware\/LinkifyURL
    # Match http & ftp URL that is not already linkified.
      # Alternative 1: URL delimited by (parentheses).
      (\()                     # $1  "(" start delimiter.
      ((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+)  # $2: URL.
      (\))                     # $3: ")" end delimiter.
    | # Alternative 2: URL delimited by [square brackets].
      (\[)                     # $4: "[" start delimiter.
      ((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+)  # $5: URL.
      (\])                     # $6: "]" end delimiter.
    | # Alternative 3: URL delimited by {curly braces}.
      (\{)                     # $7: "{" start delimiter.
      ((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+)  # $8: URL.
      (\})                     # $9: "}" end delimiter.
    | # Alternative 4: URL delimited by <angle brackets>.
      (<|&(?:lt|\#60|\#x3c);)  # $10: "<" start delimiter (or HTML entity).
      ((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+)  # $11: URL.
      (>|&(?:gt|\#62|\#x3e);)  # $12: ">" end delimiter (or HTML entity).
    | # Alternative 5: URL not delimited by (), [], {} or <>.
      (                        # $13: Prefix proving URL not already linked.
        (?: ^                  # Can be a beginning of line or string, or
        | [^=\s\'"\]]          # a non-"=", non-quote, non-"]", followed by
        ) \s*[\'"]?            # optional whitespace and optional quote;
      | [^=\s]\s+              # or... a non-equals sign followed by whitespace.
      )                        # End $13. Non-prelinkified-proof prefix.
      ( \b                     # $14: Other non-delimited URL.
        (?:ht|f)tps?:\/\/      # Required literal http, https, ftp or ftps prefix.
        [a-z0-9\-._~!$\'()*+,;=:\/?#[\]@%]+ # All URI chars except "&" (normal*).
        (?:                    # Either on a "&" or at the end of URI.
          (?!                  # Allow a "&" char only if not start of an...
            &(?:gt|\#0*62|\#x0*3e);                  # HTML ">" entity, or
          | &(?:amp|apos|quot|\#0*3[49]|\#x0*2[27]); # a [&\'"] entity if
            [.!&\',:?;]?        # followed by optional punctuation then
            (?:[^a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]|$)  # a non-URI char or EOS.
          ) &                  # If neg-assertion true, match "&" (special).
          [a-z0-9\-._~!$\'()*+,;=:\/?#[\]@%]* # More non-& URI chars (normal*).
        )*                     # Unroll-the-loop (special normal*)*.
        [a-z0-9\-_~$()*+=\/#[\]@%]  # Last char can\'t be [.!&\',;:?]
      )                        # End $14. Other non-delimited URL.
    /imx';
//    $url_replace = '$1$4$7$10$13<a href="$2$5$8$11$14">$2$5$8$11$14</a>$3$6$9$12';
//    return preg_replace($url_pattern, $url_replace, $text);
    $url_replace = '_linkify_filter_callback';
    return preg_replace_callback($url_pattern, $url_replace, $text);
}
function _linkify_filter_callback($m)
{ // Filter out youtube and vimeo domains.
    $pre  = $m[1].$m[4].$m[7].$m[10].$m[13];
    $url  = $m[2].$m[5].$m[8].$m[11].$m[14];
    $post = $m[3].$m[6].$m[9].$m[12];
    if (preg_match('/\b(?:youtube|vimeo)\.com\b/', $url)) {
        return $pre . $url . $post;
    } // else linkify...
    return $pre .'<a href="'. $url .'">' . $url .'</a>' .$post;
}

// Create some test data.
$data = 'Plain URLs (not delimited):
foo http://example.com bar...
foo http://example.com:80 bar...
foo http://example.com:80/path/ bar...
foo http://example.com:80/path/file.txt bar...
foo http://example.com:80/path/file.txt?query=val&var2=val2 bar...
foo http://example.com:80/path/file.txt?query=val&var2=val2#fragment bar...
foo http://example.com/(file\'s_name.txt) bar... (with \' and (parentheses))
foo http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7348] bar... ([IPv6 literal])
foo http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7348]/file.txt bar... ([IPv6] with path)
foo http://youtube.com bar...
foo http://youtube.com:80 bar...
foo http://youtube.com:80/path/ bar...
foo http://youtube.com:80/path/file.txt bar...
foo http://youtube.com:80/path/file.txt?query=val&var2=val2 bar...
foo http://youtube.com:80/path/file.txt?query=val&var2=val2#fragment bar...
foo http://youtube.com/(file\'s_name.txt) bar... (with \' and (parentheses))
foo http://vimeo.com bar...
foo http://vimeo.com:80 bar...
foo http://vimeo.com:80/path/ bar...
foo http://vimeo.com:80/path/file.txt bar...
foo http://vimeo.com:80/path/file.txt?query=val&var2=val2 bar...
foo http://vimeo.com:80/path/file.txt?query=val&var2=val2#fragment bar...
foo http://vimeo.com/(file\'s_name.txt) bar... (with \' and (parentheses))
';
// Verify it works...
echo(linkify_filtered($data) ."\n");

?>

这使用了一个回调函数来进行过滤。是的,正则表达式很复杂(但事实证明这是个问题!)。您可以在此处查看linkify() 的交互式 Javascript 版本:URL Linkification (HTTP/FTP)

此外,John Gruber 有一个非常好的正则表达式来进行链接。见:An Improved Liberal, Accurate Regex Pattern for Matching URLs。然而,他的正则表达式在某些情况下会遭受灾难性的回溯。 (我已经写信给他,但他还没有回复。)

希望这会有所帮助! :)

【讨论】:

  • @ridgerunner 你将如何修改它以允许用户输入的 URL 没有前缀,例如只是 www.something.com?
  • @stco - 目前没有时间,但我让其他人问过同样的问题。另一位开发者通过this pull request patch on my LinkifyURL github project 提供了解决方案,但我还没有时间查看。 (我不确定它是否有效,但您可能想看看。)
  • +1 用于发布矩阵的正则表达式...我在上下滚动时看了几个小时,然后吃了红色药丸。 :)
【解决方案2】:

嗯,这很好,但是仍然存在很大的问题,字符串中的现有锚点,我想链接文本而不将它们转换为无意义的 sh.t.

【讨论】:

    【解决方案3】:
    $string = "some text and a link http://www.google.com"
    $new_string = ereg_replace("[[:alpha:]]+://[^<>[:space:]]+[[:alnum:]/]","<a href=\"\\0\">\\0</a>", $string)
    

    或使用:

    http://code.iamcal.com/php/lib_autolink/

    【讨论】:

    • 如何过滤掉某种链接(如 youtube/vimeo)以使它们不被链接?
    • 我会先过滤掉那些,而不是使用正则表达式来链接。什么需要过滤掉?
    • 不是从内容中删除它们,而是防止它们被链接。
    • 所以你有一个包含链接的数组?
    • 不,它只是特定类型的链接。假设我不希望 youtube 链接被链接化,我需要一种方法来指定脚本,这样 youtube 链接就不会被链接化。同样,如果我希望 vimeo 链接不与 youtube 链接一起链接,则应该有一种方法来指定...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-04
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    相关资源
    最近更新 更多