wake-up-neo.com

Ersetzen Sie URLs im Text durch HTML-Links

Hier ist allerdings ein Design: Zum Beispiel setze ich einen Link wie

http://example.com

in textarea. Wie kann ich PHP feststellen, dass es sich um einen http://-Link handelt, und diesen dann als drucken

print "<a href='http://www.example.com'>http://www.example.com</a>";

Ich erinnere mich, dass ich so etwas schon einmal gemacht hatte, aber es war nicht dumm, dass es bei komplexen Links immer wieder brach.

Eine andere gute Idee wäre, wenn Sie einen Link wie

http://example.com/test.php?val1=bla&val2blablabla%20bla%20bla.bl

reparieren Sie es so

print "<a href='http://example.com/test.php?val1=bla&val2=bla%20bla%20bla.bla'>";
print "http://example.com/test.php";
print "</a>";

Dies ist nur ein Nachdenken. Stackoverflow könnte dies wahrscheinlich auch verwenden: D

Irgendwelche Ideen

55
Angel.King.47

Schauen wir uns die Anforderungen an. Sie haben einen benutzerdefinierten Klartext, den Sie mit Hyperlinks anzeigen möchten.

  1. Das Protokollpräfix "http: //" sollte optional sein.
  2. Sowohl Domänen als auch IP-Adressen sollten akzeptiert werden.
  3. Jede gültige Top-Level-Domain sollte akzeptiert werden, z. .aero und .xn - jxalpdlp.
  4. Portnummern sollten erlaubt sein.
  5. URLs müssen in normalen Satzzusammenhängen zulässig sein. In "Visit stackoverflow.com" ist der letzte Zeitraum beispielsweise nicht Teil der URL.
  6. Sie möchten wahrscheinlich auch "https: //" - URLs zulassen und möglicherweise auch andere.
  7. Wie immer, wenn Sie vom Benutzer bereitgestellter Text in HTML anzeigen, möchten Sie Cross-Site-Scripting (XSS) verhindern. Außerdem möchten Sie, dass Et-Zeichen in URLs korrekt mit Escape-Zeichen als & amp; verbunden werden.
  8. Sie benötigen wahrscheinlich keine Unterstützung für IPv6-Adressen.
  9. Edit: Wie in den Kommentaren erwähnt, ist die Unterstützung von E-Mail-Adressen definitiv ein Plus.
  10. Edit: Nur reine Texteingabe soll unterstützt werden - HTML-Tags in der Eingabe sollten nicht berücksichtigt werden. (Die Bitbucket-Version unterstützt die HTML-Eingabe.)

Edit: Check out Bitbucket für die neueste Version mit Unterstützung für E-Mail-Adressen, authentifizierte URLs, URLs in Anführungszeichen und Klammern, HTML-Eingabe sowie eine aktualisierte TLD-Liste.

Bitte melden Sie Fehler und Verbesserungsvorschläge mit dem Bitbucket Issue Tracker . Sie sind leichter auf diese Weise zu verfolgen (und stören den Kommentarbereich nicht).

Hier ist mein Take:

<?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>";
  • Um die Zeichen <und & richtig zu kennzeichnen, werfe ich den gesamten Text vor der Verarbeitung durch htmlspecialchars. Dies ist nicht ideal, da die HTML-Escape-Funktion dazu führen kann, dass URL-Grenzen falsch erkannt werden.
  • Wie das "And Remember." zeigt, ist niemand perfekt. Zeile (in der sichmerker.Körper wegen des fehlenden Speicherplatzes als URL behandelt wird), kann eine Überprüfung der gültigen Domänen der obersten Ebene möglicherweise in Ordnung sein.

Edit: Der folgende Code behebt die beiden oben genannten Probleme, ist jedoch etwas ausführlicher, da ich preg_replace_callback mit preg_match mehr oder weniger neu implementiere.

// 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

Ich habe hier etwas gefunden, das sich bewährt hat

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

Für mich geht das. Und es funktioniert für E-Mails und URLs. Sorry, um meine eigene Frage zu beantworten. :(

Aber nur dies funktioniert

Hier ist der Link, wo ich ihn gefunden habe: http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_21878567.html

Sry im Voraus, weil es ein Expertenaustausch ist.

14
Angel.King.47

Sie reden viel zu weit und komplex, was in manchen Situationen gut ist, aber meistens brauchen wir eine einfache, nachlässige Lösung. Wie wäre es einfach damit?

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

Probieren Sie es einfach aus und lassen Sie mich wissen, welche verrückte URL es nicht befriedigt.

11
Raheel Hasan

Hier ist der Code, der reguläre Ausdrücke in Funktion verwendet

<?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

Ich habe diese Funktion verwendet, sie funktioniert für mich

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

Alle Kredite gehen an - http://snipplr.com/view/68586/

Genießen!

2
Armand

Dieses RegEx sollte mit jedem Link übereinstimmen, außer für diese neuen Toplevel-Domänen mit 3+ Zeichen.

{
 \\ b 
 # Übereinstimmung mit dem führenden Teil (Proto: // Hostname oder nur Hostname) 
 (
 # http: // oder https: // führender Teil 
 (https?): // [- \\ w] + (\\. \\ w [- \\ w] *) + 
 | 
 # oder versuchen Sie, einen Hostnamen mit einem spezifischeren Unterausdruck zu finden 
 (? i: [a-z0-9] (?: [- a-z0-9]) * [a- z0-9])? \\.) + # Subdomains 
 # Endet .com, usw. Für diese müssen Sie Kleinbuchstaben 
 (? -i: com \\ b 
 | edu \\ b 
 | biz \\ b 
 | gov \\ b 
 | in (?: t | fo) \\ b # .int oder .info 
 | mil \\ b 
 | net \\ b 
 | org \\ b 
 | [az] [az] \\. [az] [az] \\ b # Landescode mit zwei Buchstaben 
) 
) 
 
 # Erlauben Sie eine optionale Portnummer 
 (: \\ d +)? 

 # Der Rest der URL ist optional und beginnt mit /
 (
 /
 # Der Rest ist eine Heuristik für das, was scheinbar gut funktioniert 
 [^.!,?; "\\ '()\[\]\{\}\s\x7F - \\ xFF] * 
 (
 [.!,?] + [^.!,?; "\\ '() \\ [\\]\{\\}\s \\ x7F - \\ xFF ] + 
) * 
)? 
} ix 

Es ist nicht von mir geschrieben, ich bin mir nicht ganz sicher, woher ich es habe, es tut mir leid, dass ich keinen Kredit geben kann ...

1
fresskoma

dies sollte Ihnen E-Mail-Adressen geben:

$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

Wie ich bereits in einem der obigen Kommentare erwähnt habe, wurde mein VPS, auf dem PHP 7 ausgeführt wird, gestartet Warnungen ausgeben Warnung: preg_replace (): Der Modifizierer/e wird nicht mehr unterstützt. Verwenden Sie stattdessen preg_replace_callback . Der Puffer nach dem Austausch war leer/falsch.

Ich habe den Code umgeschrieben und einige Verbesserungen vorgenommen. Wenn Sie der Meinung sind, dass Sie sich im Autorenbereich befinden sollten, können Sie den Kommentar über der Funktion make_links_blank name .. bearbeiten. Ich benutze absichtlich nicht das schließende PHP> Vermeiden Sie, Leerzeichen in die Ausgabe einzufügen.

<?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

Ich weiß, dass diese Antwort akzeptiert wurde und dass diese Frage ziemlich alt ist, aber sie kann für andere Leute nützlich sein, die nach anderen Implementierungen suchen.

Dies ist eine modifizierte Version des Codes, veröffentlicht von: Angel.King.47 am 27. Juli 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
);

Änderungen:

  • Ich habe die Regeln Nr. 2 und Nr. 3 entfernt (ich bin nicht sicher, in welchen Situationen nützlich ist). 
  • E-Mail-Analyse entfernt, da ich es wirklich nicht brauche.
  • Ich habe eine weitere Regel hinzugefügt, die das Erkennen von URLs in der folgenden Form ermöglicht: [Domäne] /* (Ohne WWW). Beispiel: "example.com/faq/" (Mehrere tld: Domäne. {2-3}. {2-4} /)
  • Bei der Analyse von Zeichenfolgen, die mit "http: //" beginnen, wird die Zeichenfolge aus dem Link-Label entfernt.
  • "Target = '_ blank'" wurde allen Links hinzugefügt.
  • URLs können direkt nach einem (?) Tag angegeben werden. Zum Beispiel: <b> www.example.com </ b>

Wie "Søren Løvborg" festgestellt hat, werden die URLs durch diese Funktion nicht entzogen. Ich habe seine/ihre Klasse ausprobiert, aber es funktionierte einfach nicht wie erwartet (Wenn Sie Ihren Benutzern nicht vertrauen, versuchen Sie es erst mit ihrem Code). 

1
lepe

Etwas im Sinne von:

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

Wenn Sie der IANA vertrauen möchten, können Sie Ihre aktuelle Liste der offiziell unterstützten TLDs dort verwenden, z.

  $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øvborgs Lösung # 2 ist etwas weniger wortreich und erspart Ihnen den Aufwand, die Liste zu aktualisieren. Heutzutage werden neue Tlds so achtlos weggeworfen;) 

0
Max

Das hat für mich funktioniert (eine der Antworten wurde in eine PHP -Funktion umgewandelt)

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

Diese class wandelt die URLs in Text um und behält dabei die Heimat-URL bei. Ich hoffe, dass dies Ihnen helfen wird und Ihnen Zeit spart.

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