用HTML链接替换文本中的网址

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

http://example.com

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

 print "<a href='http://www.example.com'>http://www.example.com</a>"; 

我记得之前做过这样的事情,但这并不是傻瓜证明,因为复杂的链接不断出现。

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

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>"; 

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

有任何想法吗

我们来看看需求。 你有一些用户提供的纯文本,你想要显示超链接的URL。

  1. “http://”协议前缀应该是可选的。
  2. 域名和IP地址都应该被接受。
  3. 应该接受任何有效的顶级域名,例如.aero和.xn – jxalpdlp。
  4. 端口号码应该是允许的。
  5. 在正常的句子环境下,URL必须被允许。 例如,在“访问stackoverflow.com”中,最后一段时间不是URL的一部分。
  6. 您可能还想要允许“https://”网址,也许还有其他网址。
  7. 像往常一样,在HTML中显示用户提供的文本时,您希望防止跨站点脚本 (XSS)。 此外,您还需要将URL中的&符号正确地转义为&amp ;.
  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>"; 
  • 为了正确地转义<和&字符,我在处理之前通过htmlspecialchars抛出整个文本。 这并不理想,因为html转义会导致URL边界的错误检测。
  • 正如“记住,没有人是完美的”所表明的那样。 行(记住,没有人被视为URL,因为缺少空间),进一步检查有效的顶级域名可能是有序的。

编辑 :下面的代码修复了上面的两个问题,但是比较冗长,因为我使用preg_match或多或少地重新实现了preg_replace_callback

 // 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))); 

这里是我发现,是尝试和测试

 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 ); } 

它适用于我。 它适用于电子邮件和URL,抱歉回答我自己的问题。 🙁

但是这是唯一有用的

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

Sry提前作为专家交流。

你们说话的方式是先进和复杂的东西,这对某些情况是有好处的,但是大多数我们需要一个简单的粗心的解决方案。 那么简单呢?

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

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

这里是使用正则表达式功能的代码

 <?php //Function definations function MakeUrls($str) { $find=array('`((?:https?|ftp)://\S+[[:alnum:]]/?)`si','`((?<!//)(www\.\S+[[:alnum:]]/?))`si'); $replace=array('<a href="$1" target="_blank">$1</a>', '<a href="http://$1" target="_blank">$1</a>'); return preg_replace($find,$replace,$str); } //Function testing $str="www.cloudlibz.com"; $str=MakeUrls($str); echo $str; ?> 

我一直在使用这个功能,它适用于我

 function AutoLinkUrls($str,$popup = FALSE){ if (preg_match_all("#(^|\s|\()((http(s?)://)|(www\.))(\w+[^\s\)\<]+)#i", $str, $matches)){ $pop = ($popup == TRUE) ? " target=\"_blank\" " : ""; for ($i = 0; $i < count($matches['0']); $i++){ $period = ''; if (preg_match("|\.$|", $matches['6'][$i])){ $period = '.'; $matches['6'][$i] = substr($matches['6'][$i], 0, -1); } $str = str_replace($matches['0'][$i], $matches['1'][$i].'<a href="http'. $matches['4'][$i].'://'. $matches['5'][$i]. $matches['6'][$i].'"'.$pop.'>http'. $matches['4'][$i].'://'. $matches['5'][$i]. $matches['6'][$i].'</a>'. $period, $str); }//end for }//end if return $str; }//end AutoLinkUrls 

所有学分转到 – http://snipplr.com/view/68586/

请享用!

此正则表达式应匹配除这些新的3+字符顶级域以外的任何链接…

  {
   \\ b
   #匹配前导部分(原名://主机名或主机名)
   (
     #http://,或https://主要部分
     (HTTPS):// [ -  \\ W] +(。\\瓦特[ -  \\ W] *)+
   |
     #或者,尝试找到更具体的子表达式的主机名
     (?i:[a-z0-9](?:[ -  a-z0-9] * [a-z0-9])?\\。)+#个子域
     #现在结束.com等,对于这些,要求小写
     (?-i:com \\ b
         |  EDU \\ b
         |  BIZ \\ b
         |  GOV \\ b
         |  in(?:t | fo)\\ b#.int或.info
         | 密耳\\ b
         | 净\\ b
         | 组织\\ b
         |  [az] [az] \\。[az] [az] \\ b#双字母国家代码
     )
   )

   #允许一个可选的端口号
   (:\\ d +)?

   #网址的其余部分是可选的,并以/
   (
     /
    其余的是启发式的似乎运作良好
     [!?^;“\\'()\ [\] \ {\} \ S \ 0x7F部分 -  \\ XFF] *
     (
       [。!,?] + [^。!,?;“\\'()\\ [\\] \ {\\} \ s \\ x7F  -  \\ xFF] +
     )*
   )?
 } IX

这不是我写的,我不太确定我从哪里得到它,抱歉,我不能不信任…

这应该让你的电子邮件地址:

 $string = "bah bah steve@gmail.com foo"; $match = preg_match('/[^\x00-\x20()<>@,;:\\".[\]\x7f-\xff]+(?:\.[^\x00-\x20()<>@,;:\\".[\]\x7f-\xff]+)*\@[^\x00-\x20()<>@,;:\\".[\]\x7f-\xff]+(?:\.[^\x00-\x20()<>@,;:\\".[\]\x7f-\xff]+)+/', $string, $array); print_r($array); // outputs: Array ( [0] => steve@gmail.com ) 

我知道这个答案已被接受,这个问题是相当古老的,但它可以为其他人寻找其他实现有用。

这是一个修改版本的代码张贴:Angel.King.47 7月27日09:

 $text = preg_replace( array( '/(^|\s|>)(www.[^<> \n\r]+)/iex', '/(^|\s|>)([_A-Za-z0-9-]+(\\.[A-Za-z]{2,3})?\\.[A-Za-z]{2,4}\\/[^<> \n\r]+)/iex', '/(?(?=<a[^>]*>.+<\/a>)(?:<a[^>]*>.+<\/a>)|([^="\']?)((?:https?):\/\/([^<> \n\r]+)))/iex' ), array( "stripslashes((strlen('\\2')>0?'\\1<a href=\"http://\\2\" target=\"_blank\">\\2</a>&nbsp;\\3':'\\0'))", "stripslashes((strlen('\\2')>0?'\\1<a href=\"http://\\2\" target=\"_blank\">\\2</a>&nbsp;\\4':'\\0'))", "stripslashes((strlen('\\2')>0?'\\1<a href=\"\\2\" target=\"_blank\">\\3</a>&nbsp;':'\\0'))", ), $text ); 

变化:

  • 我删除了规则#2和#3(我不确定在哪些情况下是有用的)。
  • 删除了电子邮件解析,因为我真的不需要它。
  • 我添加了一个更多的规则,允许识别URL的形式:[域] / *(不含www)。 例如:“example.com/faq/”(Multiple tld:domain。{2-3}。{2-4} /)
  • 解析以“http://”开头的字符串时,会将其从链接标签中移除。
  • 将“target ='_ blank'”添加到所有链接。
  • 可以在任何(?)标签后面指定Url。 例如:<b> http://www.example.com </ b>

正如“SørenLøvborg”所说,这个功能并不能逃避这些URL。 我尝试了他/她的班级,但没有像我期望的那样工作(如果你不相信你的用户,那么先尝试他/她的代码)。

正如我在上面的一个评论中提到的,我的VPS正在运行php 7,开始发出警告Warning:preg_replace():不再支持/ e修饰符,请改用preg_replace_callback 。 替换后的缓冲区为空/虚假。

我重写了代码并做了一些改进。 如果您认为您应该在作者部分,请随意编辑上面的注释,名称为make_links_blank。 我故意不使用关闭php?>来避免在输出中插入空格。

 <?php class App_Updater_String_Util { public static function get_default_link_attribs( $regex_matches = [] ) { $t = ' target="_blank" '; return $t; } /** * App_Updater_String_Util::set_protocol(); * @param string $link * @return string */ public static function set_protocol( $link ) { if ( ! preg_match( '#^https?#si', $link ) ) { $link = 'http://' . $link; } return $link; } /** * Goes through text and makes whatever text that look like a link an html link * which opens in a new tab/window (by adding target attribute). * * Usage: App_Updater_String_Util::make_links_blank( $text ); * * @param str $text * @return str * @see http://stackoverflow.com/questions/1188129/replace-urls-in-text-with-html-links * @author Angel.King.47 | http://dashee.co.uk * @author Svetoslav Marinov (Slavi) | http://orbisius.com */ public static function make_links_blank( $text ) { $patterns = [ '#(?(?=<a[^>]*>.+?<\/a>) (?:<a[^>]*>.+<\/a>) | ([^="\']?)((?:https?|ftp):\/\/[^<> \n\r]+) )#six' => function ( $matches ) { $r1 = empty( $matches[1] ) ? '' : $matches[1]; $r2 = empty( $matches[2] ) ? '' : $matches[2]; $r3 = empty( $matches[3] ) ? '' : $matches[3]; $r2 = empty( $r2 ) ? '' : App_Updater_String_Util::set_protocol( $r2 ); $res = ! empty( $r2 ) ? "$r1<a href=\"$r2\">$r2</a>$r3" : $matches[0]; $res = stripslashes( $res ); return $res; }, '#(^|\s)((?:https?://|www\.|https?://www\.)[^<>\ \n\r]+)#six' => function ( $matches ) { $r1 = empty( $matches[1] ) ? '' : $matches[1]; $r2 = empty( $matches[2] ) ? '' : $matches[2]; $r3 = empty( $matches[3] ) ? '' : $matches[3]; $r2 = ! empty( $r2 ) ? App_Updater_String_Util::set_protocol( $r2 ) : ''; $res = ! empty( $r2 ) ? "$r1<a href=\"$r2\">$r2</a>$r3" : $matches[0]; $res = stripslashes( $res ); return $res; }, // Remove any target attribs (if any) '#<a([^>]*)target="?[^"\']+"?#si' => '<a\\1', // Put the target attrib '#<a([^>]+)>#si' => '<a\\1 target="_blank">', // Make emails clickable Mailto links '/(([\w\-]+)(\\.[\w\-]+)*@([\w\-]+) (\\.[\w\-]+)*)/six' => function ( $matches ) { $r = $matches[0]; $res = ! empty( $r ) ? "<a href=\"mailto:$r\">$r</a>" : $r; $res = stripslashes( $res ); return $res; }, ]; foreach ( $patterns as $regex => $callback_or_replace ) { if ( is_callable( $callback_or_replace ) ) { $text = preg_replace_callback( $regex, $callback_or_replace, $text ); } else { $text = preg_replace( $regex, $callback_or_replace, $text ); } } return $text; } } 

有些东西是:

 <?php if(preg_match('@^http://(.*)\s|$@g', $textarea_url, $matches)) { echo '<a href=http://", $matches[1], '">', $matches[1], '</a>'; } ?> 

这个class将url更改为文本,同时保持home url的原样。 我希望这会帮助你,节省时间。

 class RegClass { function preg_callback_url($matches) { //var_dump($matches); //Get the matched URL text <a>text</a> $text = $matches[2]; //Get the matched URL link <a href ="http://www.test.com">text</a> $url = $matches[1]; if($url=='href ="http://www.test.com"'){ //replace all a tag as it is return '<a href='.$url.' rel="nofollow"> '.$text.' </a>'; }else{ //replace all a tag to text return " $text " ; } } function ParseText($text){ $text = preg_replace( "/www\./", "http://www.", $text ); $regex ="/http:\/\/http:\/\/www\./" $text = preg_replace( $regex, "http://www.", $text ); $regex2 = "/https:\/\/http:\/\/www\./"; $text = preg_replace( $regex2, "https://www.", $text ); return preg_replace_callback('/<a\s(.+?)>(.+?)<\/a>/is', array( &$this, 'preg_callback_url'), $text); } } $regexp = new RegClass(); echo $regexp->ParseText($text); 

如果您想信任IANA,您可以获得当前正在使用的官方支持的顶级域名(TLD)的列表,如:

  $validTLDs = explode("\n", file_get_contents('http://data.iana.org/TLD/tlds-alpha-by-domain.txt')); //get the official list of valid tlds array_shift($validTLDs); //throw away first line containing meta data array_pop($validTLDs); //throw away last element which is empty 

使SørenLøvborg的解决方案#2稍微冗长一点,并且省去了更新列表的麻烦,现在新的Tld被不小心抛出了;)

这对我有用(将其中一个答案转换为PHP函数)

 function make_urls_from_text ($text){ return preg_replace('/(http[s]{0,1}\:\/\/\S{4,})\s{0,}/ims', '<a href="$1" target="_blank">$1 </a>', $text); } 

(?<= ^ |(?<= [^ a-zA-Z0-9- 。]))@([A-Za-z] + [A-Za -z0-9 ] +)/ i

虽然匹配完整的url规范是困难的,这是一个正则表达式,通常做得很好:

 ([\w-]+(\.[\w-]+)*@([a-z0-9-]+(\.[a-z0-9-]+)*?\.[az]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?) 

然而,要在preg_replace中使用它,则需要将其转义。 如此:

 $pattern = "/([\\w-]+(\\.[\\w-]+)*@([a-z0-9-]+(\\.[a-z0-9-]+)*?\\.[az]{2,6}|(\\d{1,3}\\.){3}\\d{1,3})(:\\d{4})?)/"; $replaced_texttext = preg_replace($pattern, '<a href="$0" title="$0">$0</a>', $text);