web-dev-qa-db-ja.com

テキスト内のURLをHTMLリンクに置き換えます

ただし、ここに設計があります。たとえば、次のようなリンクを配置します

http://example.com

intextareaどうすれば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

何か案は

56
Angel.King.47

要件を見てみましょう。ハイパーリンクされたURLで表示するユーザー指定のプレーンテキストがあります。

  1. 「http://」プロトコルプレフィックスはオプションである必要があります。
  2. ドメインとIPアドレスの両方を受け入れる必要があります。
  3. 有効なトップレベルドメインを承認する必要があります。 .aeroおよび.xn--jxalpdlp。
  4. ポート番号を許可する必要があります。
  5. 通常の文のコンテキストではURLを許可する必要があります。たとえば、「stackoverflow.comにアクセス」では、最終期間はURLの一部ではありません。
  6. 「https://」のURLも許可したいでしょう。他のURLも許可したいでしょう。
  7. いつものように、ユーザーが入力したテキストをHTMLで表示するときは、 クロスサイトスクリプティング (XSS)を防ぐ必要があります。また、URLのアンパサンドは 正しくエスケープされている as amp;にする必要があります。
  8. おそらくIPv6アドレスのサポートは必要ありません。
  9. Edit:コメントに記載されているように、メールアドレスのサポートは間違いなくプラスです。
  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>";
  • <と文字を適切にエスケープするには、処理する前にhtmlspecialcharsを介してテキスト全体をスローします。 htmlエスケープはURL境界の誤検出を引き起こす可能性があるため、これは理想的ではありません。
  • 「そして、覚えておいてください。誰も完璧ではありません。」行(覚えておいてください。スペースがないため、誰もURLとして扱われません)、有効なトップレベルドメインをさらにチェックすることが適切な場合があります。

Edit:次のコードは上記の2つの問題を修正しますが、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)));
118
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を教えてください。

12
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;
?>
4

私はこの機能を使用していますが、それは私のために機能します

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

この正規表現は、これらの新しい3文字以上のトップレベルドメインを除くすべてのリンクと一致する必要があります...

{
 \\ b 
#先頭部分に一致(proto:// hostname、または単にホスト名)
(
#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#two -文字の国コード
)
)
 
#オプションのポート番号を許可
(:\\ 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

Php 7を実行しているVPSの上のコメントの1つで述べたように、警告を発し始めました警告: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

私はこの答えが受け入れられ、この質問はかなり古いことを知っていますが、他の実装を探している他の人にとっては有用です。

これは、2009年7月27日に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を削除しました(どの状況で役立つかわかりません)。
  • メール解析は必要ないので削除しました。
  • 次の形式のURLの認識を許可するもう1つのルールを追加しました:[ドメイン]/*(wwwなし)。例: "example.com/faq/"(複数のtld:domain。{2-3}。{2-4} /)
  • 「http://」で始まる文字列を解析する場合、リンクラベルから削除します。
  • すべてのリンクに「target = '_ blank'」を追加しました。
  • URLは、any(?)タグの直後に指定できます。例:<b> www.example.com </ b>

「SørenLøvborg」が述べているように、この関数はURLをエスケープしません。私は彼/彼女のクラスを試してみましたが、期待通りに機能しませんでした(ユーザーを信頼していない場合は、まず彼/彼女のコードを試してください)。

1
lepe

の線に沿って何か:

<?php
if(preg_match('@^http://(.*)\s|$@g', $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ørenLøvborgのソリューション#2をもう少し冗長にし、リストを更新する手間を省きます。最近では新しいtldが不注意に捨てられています;)

0
Max

これは私のために働いた(答えの1つを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