Замените URL-адреса в тексте ссылками 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

Идеи

15 55

15 ответов:

давайте посмотрим на требования. У вас есть некоторый пользовательский простой текст, который вы хотите отобразить с гиперссылками url.

  1. префикс протокола "http: / /" должен быть необязательным.
  2. оба домена и IP-адреса должны быть приняты.
  3. должен быть принят любой допустимый домен верхнего уровня, например .aero and .xn--jxalpdlp.
  4. номера портов должны быть разрешены.
  5. URL-адреса должны быть разрешены в обычных контекстах предложений. Например, в "Визит stackoverflow.com", заключительный период не является частью URL.
  6. вы, вероятно, хотите разрешить URL-адреса "https://", а также, возможно, и другие.
  7. как всегда при отображении пользовательского текста в HTML, вы хотите предотвратить межсайтовый скриптинг (XSS). Кроме того, вы хотите, чтобы амперсанды в URL-адресах были правильно сбежал as &.
  8. вам, вероятно, не нужна поддержка IPv6-адресов.
  9. Edit: как отмеченная в комментариях поддержка email-адресов-это определенно плюс.
  10. Edit: должен поддерживаться только простой ввод текста - HTML-теги во входных данных не должны соблюдаться. (Версия Bitbucket поддерживает ввод HTML.)

Edit: проверить Bitbucket для последней версии, с поддержкой адресов электронной почты, аутентифицированных URL-адресов, URL-адресов в кавычках и скобках, ввода HTML, а также обновленного TLD список.

пожалуйста, сообщайте об ошибках и запросах на улучшение с помощью Bitbucket issue tracker. Их легче отслеживать таким образом (и не загромождать область комментариев).

вот мое мнение:

<?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>";
  • чтобы правильно экранировать символы
  • как продемонстрированный "и помните.Никто не совершенен."линия (в которой помните.Никто не рассматривается как URL-адрес из-за отсутствия места), дальнейшая проверка допустимых доменов верхнего уровня может быть в порядке.

Edit: следующий код исправляет вышеупомянутые две проблемы, но довольно многословен, так как я более или менее повторно внедряю 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)));

вот что я нашел, что это испытанный

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="" target="_blank"></a> ', $text_msg);

просто попробуйте и дайте мне знать, какой сумасшедший url он не удовлетворяет.

вот код, использующий регулярные выражения в функции

<?php
//Function definations
function MakeUrls($str)
{
$find=array('`((?:https?|ftp)://\S+[[:alnum:]]/?)`si','`((?<!//)(www\.\S+[[:alnum:]]/?))`si');

$replace=array('<a href="" target="_blank"></a>', '<a href="http://" target="_blank"></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
  # Match the leading part (proto://hostname, or just hostname)
  (
    # http://, or https:// leading part
    (https?)://[-\w]+(\.\w[-\w]*)+
  |
    # or, try to find a hostname with more specific sub-expression
    (?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \. )+ # sub domains
    # Now ending .com, etc. For these, require lowercase
    (?-i: com\b
        | edu\b
        | biz\b
        | gov\b
        | in(?:t|fo)\b # .int or .info
        | mil\b
        | net\b
        | org\b
        | [a-z][a-z]\.[a-z][a-z]\b # two-letter country code
    )
  )

  # Allow an optional port number
  ( : \d+ )?

  # The rest of the URL is optional, and begins with /
  (
    /
    # The rest are heuristics for what seems to work well
    [^.!,?;"\'()\[\]\{\}\s\x7F-\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.Король.47 июля 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/ "(несколько дву: домен.{2-3}.{2-4}/)
  • при разборе строк, начинающихся с " http://", он удаляет его из метки ссылки.
  • добавлено "target=' _blank '" для всех ссылок.
  • URL-адреса могут быть указаны только после любой(?) метка. Например: www.example.com

Как заявил "Серен Левборг", эта функция не избегает URL-адресов. Я попробовал его/ее класс, но он просто не работал, как я ожидал (если вы не доверяете своим пользователям, то сначала попробуйте его/ее код).

Как я уже упоминал в одном из комментариев выше моего VPS, который работает под управлением php 7, запущен излучая предупреждения предупреждение: preg_replace (): модификатор /e больше не поддерживается, используйте preg_replace_callback вместо. Буфер после замены был пуст/false.

я переписал код и сделал некоторые улучшения. Если вы считаете, что вы должны быть в разделе автор, не стесняйтесь редактировать комментарий над именем функции 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-адреса в текст и сохраняя домашний 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, вы можете получить свой текущий список официально поддерживаемых дву, например:

  $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

делает решение № 2 Серена Левборга немного менее подробным и избавляет вас от хлопот по обновлению списка, в настоящее время новые дву выбрасываются так небрежно ;)

это сработало для меня (превратил один из ответов в функцию PHP)

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

Это должно получить вашу ручку twitter, не касаясь вашей электронной почты /(?.])) @([A-Za-z]+[A-Za-z0-9]+) / i

в то время как соответствие полной спецификации url трудно, вот регулярное выражение, которое обычно делает хорошую работу:

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

чтобы использовать это в preg_replace, однако, вам нужно избежать его. Как так:

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