58 votes

Remplacer les URL dans le texte par des liens HTML

Voici une conception bien: Par exemple j'ai mis un lien tel que

http://example.com

dans le textarea. Comment puis-je obtenir PHP pour détecter c'est un http:// lien et ensuite l'imprimer comme

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

Je me souviens de faire quelque chose comme ça avant, cependant, il n'était pas infaillible, il a gardé la rupture de liens complexes.

Une autre bonne idée serait si vous avez un lien tel que

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

corrigé de sorte qu'il n'

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

C'est seulement après avoir pensé.. stackoverflow pourrait aussi probablement à l'utiliser ainsi :D

Toutes Les Idées

123voto

Søren Løvborg Points 3425

Regardons les exigences. Vous avez quelques fournies par l'utilisateur en texte brut que vous souhaitez afficher avec liens hypertexte Url.

  1. La "http://" préfixe de protocole devrait être facultatif.
  2. Les deux domaines et les adresses IP doivent être acceptées.
  3. Pour être valable, toute de domaine de premier niveau doivent être acceptées, par exemple .aero et .xn--jxalpdlp.
  4. Les numéros de Port devraient être autorisés.
  5. Les url doivent être autorisés à peine contextes. Par exemple, dans "Visite stackoverflow.com.", la période finale ne fait pas partie de l'URL.
  6. Vous voulez probablement pour permettre aux "https://" Url en tant que bien, et peut-être d'autres.
  7. Comme toujours lors de l'affichage de l'utilisateur fourni de texte en HTML, vous voulez éviter de cross-site scripting (XSS). Aussi, vous aurez envie arobases dans les Url pour être correctement échappé comme &amp;.
  8. Vous n'avez probablement pas besoin d'un soutien pour les adresses IPv6.
  9. Edit: Comme indiqué dans les commentaires, le soutien pour l'email-adresses est certainement un plus.
  10. Edit: Seulement de saisie de texte brut est pris en charge des balises HTML dans l'entrée ne doit pas être honorée. (Le Bitbucket version prend en charge le HTML d'entrée.)

Edit: découvrez Bitbucket pour la dernière version, avec le soutien adresses e-mail, authentifié Url, l'Url dans des guillemets et des parenthèses, HTML entrée, ainsi qu'une mise à jour de TLD liste.

Merci de signaler les bugs et les demandes d'amélioration à l'aide de la Bitbucket issue tracker. Ils sont plus faciles à garder une trace de cette façon (et ne pas encombrer la zone de commentaire).

Voici mon point de vue:

<?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>";
  • Afin d'échapper correctement < et & caractères, j'ai jeter l'ensemble du texte par htmlspecialchars avant le traitement. Ce n'est pas l'idéal, comme le html échappement peut provoquer misdetection de l'URL de limites.
  • Comme l'a montré le "Et rappelez-vous.Personne n'est parfait." en ligne (dans lequel se rappeler.Personne n'est considérée comme une URL, en raison du manque d'espace), une vérification plus poussée en matière de validité des domaines de premier niveau pourrait être dans l'ordre.

Edit: Le code suivant résout les deux problèmes ci-dessus, mais il est tout à fait un peu plus de commentaires depuis que je suis plus ou moins re-mise en œuvre d' preg_replace_callback l'aide 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)));

15voto

Angel.King.47 Points 2736

Voici quelque chose que j'ai trouvé c'est essayé et testé

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

Il fonctionne pour moi. Et cela fonctionne pour les e-mails et les URL, Désolé pour la réponse à ma propre question. :(

Mais celui-ci est le seul qui fonctionne

Voici le lien où j'ai trouvé ça : http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_21878567.html

Sry à l'avance pour qu'il soit un des experts-échange.

1voto

x3ro Points 12721

Cette expression doit correspondre à un lien à l'exception pour ces nouveaux 3+ caractère toplevel domaines...

{
\\b
 # Correspond à la partie de tête (proto://nom d'hôte, ou tout simplement le nom d'hôte)
(
 # http:// ou https:// rôle principal
(https?)://[-\\w]+(\\.\\w[-\\w]*)+
|
 # ou, essayez de trouver un nom de domaine plus spécifique de la sous-expression
 (?j': [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \\. )+ # les sous-domaines
 # Aujourd'hui à sa fin .com, etc. Pour ceux-ci, exiger minuscules
 (?-j': com\\b
 | edu\\b
 | biz\\b
 | gov\\b
 | (?:t|fo)\\b # .int ou .info
 | mil\\b
 | net\\b
 | org\\b
 | [a-z][a-z]\\.[a-z][a-z]\\b # code pays à deux lettres
)
)

 # Permettre à un numéro de port éventuel
 ( : \\d+ )?

 # Le reste de l'URL est facultatif, et commence avec /
(
/
 # Le reste sont des heuristiques pour ce qui semble bien fonctionner
[^.!,?;"\\'()\[\]\{\}\s\x7F-\\xFF]*
(
 [.!,?]+ [^.!,?;"\\'()\\[\\]\{\\}\s\\x7F-\\xFF]+
)*
)?
}ix

Il n'est pas écrit par moi, je ne suis pas tout à fait sûr où je l'ai eu à partir, désolé que je n'ai pas de crédit...

1voto

Stephen J. Fuhry Points 3549

cela devrait vous donner des adresses email:

 $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
)
 

1voto

lepe Points 8959

Je sais que cette réponse a été acceptée et que cette question est assez vieux, mais il peut être utile pour d'autres personnes à la recherche d'autres implémentations.

Ceci est une version modifiée du code posté par: Ange.Le roi.47 juillet 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
);

Changements:

  • J'ai enlevé les règles #2 et #3 (je ne suis pas sûr que dans les situations qui sont utiles).
  • E-mail supprimé l'analyse que je n'ai vraiment pas besoin.
  • J'ai ajouté une règle qui permet la reconnaissance des Url de la forme: [domaine]/* (sans les www). Par exemple: "example.com/faq/" (Plusieurs tld: domaine.{2-3}.{2-4}/)
  • Lors de l'analyse des chaînes de caractères commençant par "http://", il supprime le lien de l'étiquette.
  • Ajouté "target='_blank'" à tous les liens.
  • Les url peuvent être spécifiés après tout(?) la balise. Par exemple: <b>www.example.com</b>

"Søren Løvborg", a indiqué, cette fonction ne doit pas échapper à l'Url. J'ai essayé sa classe, mais ça n'a pas fonctionné comme je l'espérais (Si vous ne faites pas confiance à vos utilisateurs, puis essayer son premier code).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X