【问题标题】:Convert plain text URLs into HTML hyperlinks in PHP在 PHP 中将纯文本 URL 转换为 HTML 超链接
【发布时间】:2010-12-29 22:45:59
【问题描述】:

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

我不希望算法使用任何其他类型的链接来执行此操作,只需 http 和 https。

【问题讨论】:

标签: php regex hyperlink preg-replace


【解决方案1】:

这是另一种解决方案,这将捕获所有 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,每个都可以工作关于用户自己的脚本是如何设置的,我为每个脚本提供了一些文字说明他们所做的事情。

【讨论】:

    【解决方案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

    【讨论】:

      【解决方案3】:

      在 HTML 中查找纯文本链接

      我真的很喜欢this answer - 但我需要一个解决方案来处理非常简单的 HTML 文本中可能存在的纯文本链接:

      <p>I found a really cool site you might like:</p>
      <p>www.stackoverflow.com</p>
      

      这意味着 我需要正则表达式模式来忽略 html 字符 &lt;&gt;

      正则表达式调整

      所以我将部分模式改为[^\s\&gt;\&lt;] 而不是\S

      • \S - 不是空格;匹配任何非空白字符(制表符、空格、换行符)
      • [^] - 一个否定集;匹配任何不在集合中的字符

      这个答案中我的函数版本

      我需要除了 HTML 之外的另一种格式,所以我将正则表达式从它们的替换中分离出来以适应这种情况。

      我还添加了一种方法,只返回找到的链接/电子邮件到一个数组中,这样我就可以将它们保存为我的帖子中的关系(非常适合以后为他们制作元卡片......并且用于分析!)。

      更新:连续期间匹配

      我得到了像 there...it 这样的文本的匹配项 - 所以我想确保我没有得到任何包含连续点的匹配项。

      注意:为了解决这个问题,我添加了一个额外的格式字符串来撤消匹配它们以避免重做这些原本可靠的 url 正则表达式。

      /***
       * based on this answer: https://stackoverflow.com/a/49689245/2100636
       *
       * @var $text String
       * @var $format String - html (<a href=""...), short ([link:https://somewhere]), other (https://somewhere)
       */
      public function formatLinksInString(
          $string,
          $format = 'html', 
          $returnMatches = false
      ) {
          $formatProtocol = $format == 'html'
              ? '<a href="$0" target="_blank" title="$0">$0</a>'
              : ($format == 'short' || $returnMatches ? '[link:$0]' : '$0');
      
          $formatSansProtocol = $format == 'html'
              ? '<a href="//$0" target="_blank" title="$0">$0</a>'
              : ($format == 'short' || $returnMatches ? '[link://$0]' : '$0');
      
          $formatMailto = $format == 'html'
              ? '<a href="mailto:$1" target="_blank" title="$1">$1</a>'
              : ($format == 'short' || $returnMatches ? '[mailto:$1]' : '$1');
      
          $regProtocol = '/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(\/[^\<\>\s]*)?/';
          $regSansProtocol = '/(?<=\s|\A|\>)([0-9a-zA-Z\-\.]+\.[a-zA-Z0-9\/]{2,})(?=\s|$|\,|\<)/';
          $regEmail = '/([^\s\>\<]+\@[^\s\>\<]+\.[^\s\>\<]+)\b/';
          $consecutiveDotsRegex = $format == 'html'
              ? '/<a[^\>]+[\.]{2,}[^\>]*?>([^\<]*?)<\/a>/'
              : '/\[link:.*?\/\/([^\]]+[\.]{2,}[^\]]*?)\]/';
      
          // Protocol links
          $formatString = preg_replace($regProtocol, $formatProtocol, $string);
          // Sans Protocol Links
          $formatString = preg_replace($regSansProtocol, $formatSansProtocol, $formatString); // use formatString from above
          // Email - Mailto - Links
          $formatString = preg_replace($regEmail, $formatMailto, $formatString); // use formatString from above
          // Prevent consecutive periods from getting captured
          $formatString = preg_replace($consecutiveDotsRegex, '$1', $formatString);
      
          if ($returnMatches) {
              // Find all [x:link] patterns
              preg_match_all('/\[.*?:(.*?)\]/', $formatString, $matches);
      
              current($matches); // to move pointer onto groups
              return next($matches); // return the groups
          }
      
          return $formatString;
      }
      

      【讨论】:

        【解决方案4】:

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

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

        请评论无效的网址。我会尝试更新正则表达式。

        【讨论】:

          【解决方案5】:
          $string = 'example.com
          www.example.com
          http://example.com
          https://example.com
          http://www.example.com
          https://www.example.com';
          
          preg_match_all('#(\w*://|www\.)[a-z0-9]+(-+[a-z0-9]+)*(\.[a-z0-9]+(-+[a-z0-9]+)*)+(/([^\s()<>;]+\w)?/?)?#i', $string, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
          foreach (array_reverse($matches) as $match) {
            $a = '<a href="'.(strpos($match[1][0], '/') ? '' : 'http://') . $match[0][0].'">' . $match[0][0] . '</a>';
            $string = substr_replace($string, $a, $match[0][1], strlen($match[0][0]));
          }
          
          echo $string;
          

          结果:

          example.com
          <a href="http://www.example.com">www.example.com</a>
          <a href="http://example.com">http://example.com</a>
          <a href="https://example.com">https://example.com</a>
          <a href="http://www.example.com">http://www.example.com</a>
          <a href="https://www.example.com">https://www.example.com</a>
          

          我喜欢这个解决方案的地方在于它还将www.example.com 转换为http://www.example.com,因为&lt;a href="www.example.com"&gt;&lt;/a&gt; 不起作用(没有http/https 协议它指向yourdomain.com/www.example.com)。

          【讨论】:

            【解决方案6】:

            试试这个:

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

            它会跳过现有的链接(如果我们已经有一个href,它就不会在href 中添加一个href)。否则它将添加带有空白目标的a href。

            【讨论】:

              【解决方案7】:

              如果我是对的,您要做的是将普通文本转换为 http 链接。以下是我认为可以提供帮助的方法:

              <?php
              
                 $list = mysqli_query($con,"SELECT * FROM list WHERE name = 'table content'"); 
                 while($row2 = mysqli_fetch_array($list)) {
              echo "<a target='_blank' href='http://www." . $row2['content']. "'>" . $row2['content']. "</a>";
              
                 }  
              ?>
              

              【讨论】:

                【解决方案8】:

                我正在使用源自question2answer 的函数,它接受纯文本甚至html 中的纯文本链接:

                // $html holds the string
                $htmlunlinkeds = array_reverse(preg_split('|<[Aa]\s+[^>]+>.*</[Aa]\s*>|', $html, -1, PREG_SPLIT_OFFSET_CAPTURE)); // start from end so we substitute correctly
                foreach ($htmlunlinkeds as $htmlunlinked)
                { // and that we don't detect links inside HTML, e.g. <img src="http://...">
                    $thishtmluntaggeds = array_reverse(preg_split('/<[^>]*>/', $htmlunlinked[0], -1, PREG_SPLIT_OFFSET_CAPTURE)); // again, start from end
                    foreach ($thishtmluntaggeds as $thishtmluntagged)
                    {
                        $innerhtml = $thishtmluntagged[0];
                        if(is_numeric(strpos($innerhtml, '://'))) 
                        { // quick test first
                            $newhtml = qa_html_convert_urls($innerhtml, qa_opt('links_in_new_window'));
                            $html = substr_replace($html, $newhtml, $htmlunlinked[1]+$thishtmluntagged[1], strlen($innerhtml));
                        }
                    }
                }   
                echo $html;
                
                function qa_html_convert_urls($html, $newwindow = false)
                /*
                    Return $html with any URLs converted into links (with nofollow and in a new window if $newwindow).
                    Closing parentheses/brackets are removed from the link if they don't have a matching opening one. This avoids creating
                    incorrect URLs from (http://www.question2answer.org) but allow URLs such as http://www.wikipedia.org/Computers_(Software)
                */
                {
                    $uc = 'a-z\x{00a1}-\x{ffff}';
                    $url_regex = '#\b((?:https?|ftp)://(?:[0-9'.$uc.'][0-9'.$uc.'-]*\.)+['.$uc.']{2,}(?::\d{2,5})?(?:/(?:[^\s<>]*[^\s<>\.])?)?)#iu';
                
                    // get matches and their positions
                    if (preg_match_all($url_regex, $html, $matches, PREG_OFFSET_CAPTURE)) {
                        $brackets = array(
                            ')' => '(',
                            '}' => '{',
                            ']' => '[',
                        );
                
                        // loop backwards so we substitute correctly
                        for ($i = count($matches[1])-1; $i >= 0; $i--) {
                            $match = $matches[1][$i];
                            $text_url = $match[0];
                            $removed = '';
                            $lastch = substr($text_url, -1);
                
                            // exclude bracket from link if no matching bracket
                            while (array_key_exists($lastch, $brackets)) {
                                $open_char = $brackets[$lastch];
                                $num_open = substr_count($text_url, $open_char);
                                $num_close = substr_count($text_url, $lastch);
                
                                if ($num_close == $num_open + 1) {
                                    $text_url = substr($text_url, 0, -1);
                                    $removed = $lastch . $removed;
                                    $lastch = substr($text_url, -1);
                                }
                                else
                                    break;
                            }
                
                            $target = $newwindow ? ' target="_blank"' : '';
                            $replace = '<a href="' . $text_url . '" rel="nofollow"' . $target . '>' . $text_url . '</a>' . $removed;
                            $html = substr_replace($html, $replace, $match[1], strlen($match[0]));
                        }
                    }
                
                    return $html;
                }
                

                由于接受包含括号和其他字符的链接,代码有点多,但可能会有所帮助。

                【讨论】:

                  【解决方案9】:

                  评分最高的答案对我没有用,以下链接未正确替换:

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

                  我不是正则表达式方面的专家,实际上这让我很困惑 :)

                  因此,请随时评论和改进此解决方案。

                  【讨论】:

                    【解决方案10】:

                    请参阅http://zenverse.net/php-function-to-auto-convert-url-into-hyperlink/。 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;
                    }
                    

                    【讨论】:

                      【解决方案11】:

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

                      这是在这两种情况下都适用于我的解决方案:

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

                      【讨论】:

                        【解决方案12】:

                        嗯,Volomik 的答案更接近。为了更进一步,这就是我所做的,它忽略了超链接末尾的尾随 句点。我还考虑了 URI 片段。

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

                        【讨论】:

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

                          【讨论】:

                            【解决方案14】:

                            我建议不要像这样在飞行中做很多事情。我更喜欢使用简单的编辑器界面,就像在 stackoverflow 中使用的那样。它被称为Markdown

                            【讨论】:

                              【解决方案15】:
                              <?
                              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");
                              ?>
                              

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 2020-02-15
                                • 1970-01-01
                                • 2022-01-18
                                • 1970-01-01
                                • 2014-04-07
                                • 2012-01-14
                                相关资源
                                最近更新 更多