it-swarm.com.ru

Заменить 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

Есть идеи

55
Angel.King.47

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

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

Edit: проверить 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:;[email protected]_\':;!a-zA-Z\x7f-\xff]*?)?';
$rexQuery    = '(\?[!$-/0-9:;[email protected]_\':;!a-zA-Z\x7f-\xff]+?)?';
$rexFragment = '(#[!$-/0-9:;[email protected]_\':;!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 из-за недостатка места), дальнейшая проверка допустимых доменов верхнего уровня может быть в порядке.

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)));
117
Søren Løvborg

Вот то, что я нашел, что проверено и проверено

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

Заранее извиняюсь за то, что это обмен экспертами.

14
Angel.King.47

Вы, ребята, говорите о способах продвижения и сложных вещах, которые хороши для некоторой ситуации, но в основном нам нужно простое небрежное решение. Как насчет просто этого?

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

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

11
Raheel Hasan

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

<?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;
?>
3
Dharmendra Jadon

Я использую эту функцию, она работает для меня

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/

Наслаждайтесь!

2
Armand

Этот RegEx должен соответствовать любой ссылке, кроме этих новых доменов верхнего уровня 3+ символов ...

{
 \\ б 
 # Соответствует ведущей части (proto: // имя хоста или просто имя хоста) 
 (
 # http: // или https: // начальная часть 
 (https?): // [- \\ w] + (\\. \\ 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 
 | mil \\ b 
 | net \\ b 
 | org \\ b 
 | [az] [az] \\. [az] [az] \\ b # двухбуквенный код страны 
) 
) 
 
 # Разрешить дополнительный номер порта 
 (: \\ d +)? 

 # Остальная часть URL является необязательной и начинается с /
 (
 /
 # Остальное - эвристика для того, что, кажется, хорошо работает 
 [^.!,?; "\\ '()\[\]\{\}\s\x7F - \\ xFF] * 
 (
 [.!,?] + [^.!,?; "\\ '() \\ [\\]\{\\}\s \\ x7F - \\ xFF ] + 
) * 
)? 
} ix 

Это не написано мной, я не совсем уверен, откуда я взял это, извините, что не могу отдать должное ...

1
fresskoma

это должно получить вам адреса электронной почты:

$string = "bah bah [email protected] 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] => [email protected]
)
1
Stephen Fuhry

Как я упоминал в одном из комментариев выше, мой VPS, на котором запущен php 7, запустился Создание предупреждений Предупреждение: 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;
    }
}
1
Svetoslav Marinov

Я знаю, что этот ответ принят, и этот вопрос довольно старый, но он может быть полезен для других людей, ищущих другие реализации.

Это модифицированная версия кода, размещенного: Angel.King.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-адреса могут быть указаны сразу после любого (?) Тега. Например: <b> www.example.com </ b>

Как заявил «Søren Løvborg», эта функция не экранирует URL. Я попробовал его/ее класс, но он просто не сработал, как я ожидал (если вы не доверяете своим пользователям, сначала попробуйте его/ее код). 

1
lepe

Что-то вроде:

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

Если вы хотите доверять 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øren Løvborg №2 немного менее многословным и избавляет вас от хлопот обновления, в настоящее время новые tlds выбрасываются так небрежно;) 

0
Max

Это сработало для меня (превратил один из ответов в функцию 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);
}
0
Shawn Gervais

Этот 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);
0
amarjit singh