用HTML链接替换文本中的URL

Ang*_*.47 56 php regex url preg-replace linkify

这是一个设计:例如,我把一个链接,如

http://example.com

textarea.如何让PHP检测到它是一个http://链接,然后将其打印为

print "<a href='http://www.example.com'>http://www.example.com</a>";
Run Code Online (Sandbox Code Playgroud)

我记得之前做过这样的事情,然而,这并不是为了复杂的链接而破坏它.

另一个好主意是如果你有一个链接,如

http://example.com/test.php?val1=bla&val2blablabla%20bla%20bla.bl

解决这个问题吧

print "<a href='http://example.com/test.php?val1=bla&val2=bla%20bla%20bla.bla'>";
print "http://example.com/test.php";
print "</a>";
Run Code Online (Sandbox Code Playgroud)

这个只是一个想法.. stackoverflow也可能也使用这个:D

有任何想法吗

Sør*_*org 118

我们来看看要求.您有一些用户提供的纯文本,您希望使用超链接的URL显示.

  1. "http://"协议前缀应该是可选的.
  2. 应接受域和IP地址.
  3. 应接受任何有效的顶级域名,例如.aero和.xn - jxalpdlp.
  4. 应允许端口号.
  5. 必须在正常的句子上下文中允许使用URL.例如,在"访问stackoverflow.com."中,最后一个句点不是URL的一部分.
  6. 您可能也想要允许"https://"网址,也许还有其他网址.
  7. 与在HTML中显示用户提供的文本一样,您希望阻止跨站点脚本(XSS).此外,您还希望将URL中的&符号正确转义为& ;.
  8. 您可能不需要支持IPv6地址.
  9. 编辑:正如评论中所述,对电子邮件地址的支持绝对是一个优势.
  10. 编辑:仅支持纯文本输入 - 不应遵守输入中的HTML标记.(Bitbucket版本支持HTML输入.)

编辑:查看Bitbucket的最新版本,支持电子邮件地址,经过身份验证的URL,引号和括号中的URL,HTML输入以及更新的TLD列表.

请使用Bitbucket问题跟踪器报告错误和增强请求.它们更容易跟踪这种方式(并且不会使评论区域混乱).

这是我的看法:

<?php
$text = <<<EOD
Here are some URLs:
stackoverflow.com/questions/1188129/pregreplace-to-detect-html-php
Here's the answer: http://www.google.com/search?rls=en&q=42&ie=utf-8&oe=utf-8&hl=en. What was the question?
A quick look at http://en.wikipedia.org/wiki/URI_scheme#Generic_syntax is helpful.
There is no place like 127.0.0.1! Except maybe http://news.bbc.co.uk/1/hi/england/surrey/8168892.stm?
Ports: 192.168.0.1:8080, https://example.net:1234/.
Beware of Greeks bringing internationalized top-level domains: xn--hxajbheg2az3al.xn--jxalpdlp.
And remember.Nobody is perfect.

<script>alert('Remember kids: Say no to XSS-attacks! Always HTML escape untrusted input!');</script>
EOD;

$rexProtocol = '(https?://)?';
$rexDomain   = '((?:[-a-zA-Z0-9]{1,63}\.)+[-a-zA-Z0-9]{2,63}|(?:[0-9]{1,3}\.){3}[0-9]{1,3})';
$rexPort     = '(:[0-9]{1,5})?';
$rexPath     = '(/[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]*?)?';
$rexQuery    = '(\?[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';
$rexFragment = '(#[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';

// Solution 1:

function callback($match)
{
    // Prepend http:// if no protocol specified
    $completeUrl = $match[1] ? $match[0] : "http://{$match[0]}";

    return '<a href="' . $completeUrl . '">'
        . $match[2] . $match[3] . $match[4] . '</a>';
}

print "<pre>";
print preg_replace_callback("&\\b$rexProtocol$rexDomain$rexPort$rexPath$rexQuery$rexFragment(?=[?.!,;:\"]?(\s|$))&",
    'callback', htmlspecialchars($text));
print "</pre>";
Run Code Online (Sandbox Code Playgroud)
  • 为了正确转义<和&字符,我在处理之前将整个文本通过htmlspecialchars抛出.这并不理想,因为html转义会导致错误检测URL边界.
  • 正如"记住.没有人是完美的"所证明的那样.line(在其中记住.由于缺少空间,没有人被视为URL),可能需要进一步检查有效的顶级域名.

编辑:以下代码修复了上述两个问题,但由于我或多或少地重新实现preg_replace_callback使用,因此更加冗长preg_match.

// Solution 2:

$validTlds = array_fill_keys(explode(" ", ".aero .asia .biz .cat .com .coop .edu .gov .info .int .jobs .mil .mobi .museum .name .net .org .pro .tel .travel .ac .ad .ae .af .ag .ai .al .am .an .ao .aq .ar .as .at .au .aw .ax .az .ba .bb .bd .be .bf .bg .bh .bi .bj .bm .bn .bo .br .bs .bt .bv .bw .by .bz .ca .cc .cd .cf .cg .ch .ci .ck .cl .cm .cn .co .cr .cu .cv .cx .cy .cz .de .dj .dk .dm .do .dz .ec .ee .eg .er .es .et .eu .fi .fj .fk .fm .fo .fr .ga .gb .gd .ge .gf .gg .gh .gi .gl .gm .gn .gp .gq .gr .gs .gt .gu .gw .gy .hk .hm .hn .hr .ht .hu .id .ie .il .im .in .io .iq .ir .is .it .je .jm .jo .jp .ke .kg .kh .ki .km .kn .kp .kr .kw .ky .kz .la .lb .lc .li .lk .lr .ls .lt .lu .lv .ly .ma .mc .md .me .mg .mh .mk .ml .mm .mn .mo .mp .mq .mr .ms .mt .mu .mv .mw .mx .my .mz .na .nc .ne .nf .ng .ni .nl .no .np .nr .nu .nz .om .pa .pe .pf .pg .ph .pk .pl .pm .pn .pr .ps .pt .pw .py .qa .re .ro .rs .ru .rw .sa .sb .sc .sd .se .sg .sh .si .sj .sk .sl .sm .sn .so .sr .st .su .sv .sy .sz .tc .td .tf .tg .th .tj .tk .tl .tm .tn .to .tp .tr .tt .tv .tw .tz .ua .ug .uk .us .uy .uz .va .vc .ve .vg .vi .vn .vu .wf .ws .ye .yt .yu .za .zm .zw .xn--0zwm56d .xn--11b5bs3a9aj6g .xn--80akhbyknj4f .xn--9t4b11yi5a .xn--deba0ad .xn--g6w251d .xn--hgbk6aj7f53bba .xn--hlcj6aya9esc7a .xn--jxalpdlp .xn--kgbechtv .xn--zckzah .arpa"), true);

$position = 0;
while (preg_match("{\\b$rexProtocol$rexDomain$rexPort$rexPath$rexQuery$rexFragment(?=[?.!,;:\"]?(\s|$))}", $text, &$match, PREG_OFFSET_CAPTURE, $position))
{
    list($url, $urlPosition) = $match[0];

    // Print the text leading up to the URL.
    print(htmlspecialchars(substr($text, $position, $urlPosition - $position)));

    $domain = $match[2][0];
    $port   = $match[3][0];
    $path   = $match[4][0];

    // Check if the TLD is valid - or that $domain is an IP address.
    $tld = strtolower(strrchr($domain, '.'));
    if (preg_match('{\.[0-9]{1,3}}', $tld) || isset($validTlds[$tld]))
    {
        // Prepend http:// if no protocol specified
        $completeUrl = $match[1][0] ? $url : "http://$url";

        // Print the hyperlink.
        printf('<a href="%s">%s</a>', htmlspecialchars($completeUrl), htmlspecialchars("$domain$port$path"));
    }
    else
    {
        // Not a valid URL.
        print(htmlspecialchars($url));
    }

    // Continue text parsing from after the URL.
    $position = $urlPosition + strlen($url);
}

// Print the remainder of the text.
print(htmlspecialchars(substr($text, $position)));
Run Code Online (Sandbox Code Playgroud)

  • 我建议检测网址是否被<a href=''> </a>括起来.如果是这样,什么也不做. (3认同)
  • 我做了一个C#实现这个伟大的答案.http://codepaste.net/ngamud (3认同)
  • @Sajad:这两个问题列在最后一个"编辑"的上方,最重要的是`htmlspecialchars`可以将有效的URL转换为无效的URL.你不应该使用这里显示的任何一个版本; 使用[Bitbucket上的最新版本](https://bitbucket.org/kwi/urllinker/).这里的代码只是演示了一般的想法,而Bitbucket版本包含许多错误修正. (3认同)
  • @Rahul:简单地制作正则表达式[不区分大小写](http://php.net/manual/en/regexp.reference.delimiters.php):在调用`preg_match`时,在最后一个后添加一个`i`正则表达式中的`}`. (2认同)

Ang*_*.47 14

这是我发现的经过试验和测试的东西

function make_links_blank($text)
{
  return  preg_replace(
     array(
       '/(?(?=<a[^>]*>.+<\/a>)
             (?:<a[^>]*>.+<\/a>)
             |
             ([^="\']?)((?:https?|ftp|bf2|):\/\/[^<> \n\r]+)
         )/iex',
       '/<a([^>]*)target="?[^"\']+"?/i',
       '/<a([^>]+)>/i',
       '/(^|\s)(www.[^<> \n\r]+)/iex',
       '/(([_A-Za-z0-9-]+)(\\.[_A-Za-z0-9-]+)*@([A-Za-z0-9-]+)
       (\\.[A-Za-z0-9-]+)*)/iex'
       ),
     array(
       "stripslashes((strlen('\\2')>0?'\\1<a href=\"\\2\">\\2</a>\\3':'\\0'))",
       '<a\\1',
       '<a\\1 target="_blank">',
       "stripslashes((strlen('\\2')>0?'\\1<a href=\"http://\\2\">\\2</a>\\3':'\\0'))",
       "stripslashes((strlen('\\2')>0?'<a href=\"mailto:\\0\">\\0</a>':'\\0'))"
       ),
       $text
   );
}
Run Code Online (Sandbox Code Playgroud)

这个对我有用.它适用于电子邮件和URL,很抱歉回答我自己的问题.:(

但这一个是唯一有效的

这是我找到它的链接:http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_21878567.html

提前讽刺它是专家交流.

  • 要防止跨站点脚本,您绝不允许访问者向页面添加任意HTML代码.一个简单的例子是一个表单处理程序,它简单地执行`print($ _ POST ["text"]);`.防止这种情况的最简单(也是最安全)方法是通过`htmlspecialchars()`运行所有用户提供的文本,*转义*HTML标记和实体,有效地将它们转换为纯文本.对于这个问题,你想在输出中允许*some*HTML(即链接标签),这使问题变得复杂,因为我们不能再简单地使用`htmlspecialchars()`. (3认同)
  • 正如stackoverflow那样,你可以将`rel ="nofollow"添加到用户链接 (2认同)

Rah*_*san 12

你们正在谈论推进和复杂的东西,这对某些情况有好处,但大多数我们需要一个简单的粗心解决方案.这个怎么样?

preg_replace('/(http[s]{0,1}\:\/\/\S{4,})\s{0,}/ims', '<a href="$1" target="_blank">$1</a> ', $text_msg);
Run Code Online (Sandbox Code Playgroud)

试试吧,让我知道它不满足的疯狂网址.

  • 很好的解决方案,但如果你在字符串中有HTML,那么你可能想用`[^ ​​<]`替换`\ S` (3认同)