IT story

텍스트의 URL을 HTML 링크로 교체

hot-time 2020. 12. 28. 22:03
반응형

텍스트의 URL을 HTML 링크로 교체


여기에 디자인이 있습니다. 예를 들어 다음과 같은 링크를 넣습니다.

http://example.com

에서 텍스트 영역 . 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은 일반 문장 컨텍스트에서 허용되어야합니다. 예를 들어 "Visit stackoverflow.com."에서 최종 마침표는 URL의 일부가 아닙니다.
  6. "https : //"URL도 허용하고 싶을 것입니다.
  7. 사용자가 제공 한 텍스트를 HTML로 표시 할 때 항상 그렇듯이 교차 사이트 스크립팅 (XSS) 을 방지하려고합니다 . 또한 URL의 앰퍼샌드가 & amp;로 올바르게 이스케이프 되기를 원할 것입니다 .
  8. IPv6 주소에 대한 지원이 필요하지 않을 수 있습니다.
  9. 편집 : 의견에서 언급했듯이 이메일 주소 지원은 확실히 플러스입니다.
  10. 편집 : 일반 텍스트 입력 만 지원됩니다. 입력의 HTML 태그는 존중되지 않아야합니다. (Bitbucket 버전은 HTML 입력을 지원합니다.)

편집 : 이메일 주소, 인증 된 URL, 따옴표와 괄호로 묶인 URL, HTML 입력 및 업데이트 된 TLD 목록을 지원하는 최신 버전의 GitHub확인하세요 .

내 의견은 다음과 같습니다.

<?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로 처리되지 않습니다. 공백이 누락 되었기 때문에), 유효한 최상위 도메인에 대한 추가 검사가 순서에있을 수 있습니다.

편집 : 다음 코드는 위의 두 가지 문제를 해결하지만 .NET을 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

전문가-교류가되기 위해 미리 물어보십시오.


당신은 어떤 상황에 좋은 발전 방법과 복잡한 것들을 이야기하고 있지만 대부분 우리는 단순한 부주의 한 해결책이 필요합니다. 이건 어때?

preg_replace('/(http[s]{0,1}\:\/\/\S{4,})\s{0,}/ims', '<a href="$1" target="_blank">$1</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="$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/으로 이동합니다 .

즐겨!


이 RegEx는 새로운 3 자 이상의 최상위 도메인을 제외한 모든 링크와 일치해야합니다.

{
  \\비
  # 선행 부분 일치 (proto : // hostname 또는 호스트 이름 만)
  (
    # http : // 또는 https : // 선행 부분
    (https?) : // [-\\ w] + (\\. \\ w [-\\ w] *) +
  |
    # 또는 더 구체적인 하위 표현식으로 호스트 이름을 찾으십시오.
    (? i : [a-z0-9] (? : [-a-z0-9] * [a-z0-9])? \\.) + # 하위 도메인
    # 이제 .com 등으로 끝납니다. 이러한 경우 소문자가 필요합니다.
    (? -i : com \\ b
        | 에듀 \\ b
        | biz \\ b
        | gov \\ b
        | in (? : t | fo) \\ b # .int 또는 .info
        | 밀 \\ b
        | net \\ b
        | 조직 \\ b
        | [az] [az] \\. [az] [az] \\ b # 2 자리 국가 코드
    )
  )

  # 선택적 포트 번호 허용
  (: \\ d +)?

  # 나머지 URL은 선택 사항이며 /로 시작합니다.
  (
    /
    # 나머지는 잘 작동하는 것으로 보이는 휴리스틱입니다.
    [^.!,?; "\\ '() \ [\] \ {\} \ 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
)

나는이 대답이 받아 들여졌고이 질문이 꽤 오래되었다는 것을 알고 있지만, 다른 구현을 찾는 다른 사람들에게 유용 할 수 있습니다.

이것은 7 월 27,09에 Angel.King.47에 의해 게시 된 코드의 수정 된 버전입니다 :

$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을 제거했습니다 (어떤 상황에서 유용한 지 잘 모르겠습니다).
  • 정말로 필요하지 않기 때문에 이메일 구문 분석을 제거했습니다.
  • [도메인] / * (www없이) 형식의 URL 인식을 허용하는 규칙을 하나 더 추가했습니다. 예 : "example.com/faq /"(복수 tld : domain. {2-3}. {2-4} /)
  • "http : //"로 시작하는 문자열을 구문 분석 할 때 링크 레이블에서 제거합니다.
  • 모든 링크에 "target = '_ blank'"를 추가했습니다.
  • URL은 임의의 (?) 태그 바로 뒤에 지정할 수 있습니다. 예 : <b> www.example.com </ b>

"Søren Løvborg"가 말했듯이이 함수는 URL을 이스케이프하지 않습니다. 나는 그의 / 그녀의 수업을 시도했지만 예상대로 작동하지 않았습니다 (사용자를 신뢰하지 않는다면 먼저 그의 / 그녀의 코드를 시도하십시오).


php 7을 실행하는 VPS 위의 주석 중 하나에서 언급했듯이 경고를 표시하기 시작했습니다. 경고 : 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이 텍스트로 변경되고 홈 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

Makes Søren Løvborg's solution #2 a bit less verbose and spares you the hassle of updating the list, nowadays new tlds are thrown out so carelessly ;)


This worked for me (turned one of the answers into a PHP function)

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

This should get your twitter handle without touching on your email /(?<=^|(?<=[^a-zA-Z0-9-.]))@([A-Za-z]+[A-Za-z0-9]+)/i


While matching the full url spec is difficult, here's a regular expression that generally does a good job:

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

To use this in preg_replace, however, you need to escape it. As so:

$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="$0" title="$0">$0</a>', $text);

ReferenceURL : https://stackoverflow.com/questions/1188129/replace-urls-in-text-with-html-links

반응형