Kullanıcının ihtiyaçlarına bakalım. Eğer köprü URL'leri ile görüntülemek istediğiniz bazı kullanıcı tarafından sağlanan düz metin var.
- "Http://" protokol öneki isteğe olmalıdır.
- Her iki etki ve IP adresleri kabul edilmelidir.
- Herhangi geçerli bir üst düzey alan, kabul edilmelidir, örneğin . Aero ve xn -. Jxalpdlp.
- Port numaraları izin verilmelidir.
- URL'ler normal bir cümle bağlamlarda izin verilmelidir. Sözgelimi, "stackoverflow.com ziyaret edin.", Son dönem URL parçası değildir.
- Muhtemelen de "https://" URL'leri izin vermek istiyorum, ve belki diğerleri de.
- HTML kullanıcı sağlanan metni görüntülerken her zaman olduğu gibi, cross-site scripting (XSS) engellemek istiyorum. Ayrıca, URL'leri imi correctly escaped & gibi olmak isteyeceksiniz.
- Muhtemelen IPv6 adresleri için destek gerekmez.
- Edit: açıklamalarda belirtildiği gibi, e-posta adresleri için destek kesinlikle bir artı.
- Edit: Sadece düz metin girişi destek olmaktır - giriş HTML etiketleri onur edilmemelidir. (Bitbucket sürümü HTML girişini destekler.)
Edit: e-posta adresleri, doğrulanmış URL'ler, tırnak ve parantez içinde URL'ler, HTML girdi, yanı sıra güncellenmiş TLD listesinde desteği ile, son sürümü için Bitbucket göz atın.
Please report bugs and enhancement requests using the Bitbucket issue tracker. Onlar bu şekilde takip etmek (ve açıklama alanı karmaşasını yok) daha kolaydır. Em>
İşte benim almak bulunuyor:
<?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>";
- Düzgün kaçmak için < ve & karakterleri, ben işlemeden önce htmlspecialchars'dan tüm metin atmak. Html öncelemeli URL sınırların yanlış algılanması neden olabilir, bu durum ideal değildir.
- Gösterdiği gibi, "Ve remember.Nobody mükemmel." daha geçerli bir üst düzey etki alanları üzerinde kontrol hattı (hangi remember.Nobody çünkü eksik alan bir URL olarak kabul edilir), sırayla olabilir.
Edit: Aşağıdaki kodu yukarıdaki iki sorunu giderir, ama ben daha çok ya da daha az preg_replace_callback
ile preg_match
yeniden uygulanması değilim çünkü biraz daha ayrıntılı olmalıdır.
// 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)));