URL замены в тексте со ссылками HTML

Если бы Ваш закрытый метод весьма непосредственно тестируется общедоступными методами тестирования API, и он должен быть протестирован тогда, я делегировал бы Ваш основной класс к другому вторичному классу.

public Stack {
    public ... push(...) {...}
    public ... pop(...) {...}
    public ... isEmpty(...) {...}

    // secondary class
    private StackSupport stackSupport;
    public StackSupport getStackSupport() {...}
    public void setStackSupport(StackSupport stackSupport) {...}
}

public StackSupport {
    public ...yourOldPrivateMethodToTest(...) {...}
}

Тогда Ваш закрытый метод является открытым методом в другом классе. И можно протестировать тот открытый метод в другом классе тесты.:-)

56
задан Austin Burk 9 November 2014 в 23:16
поделиться

6 ответов

Давайте посмотрим на требования. У вас есть предоставленный пользователем простой текст, который вы хотите отображать с URL-адресами с гиперссылками.

  1. Префикс протокола «http: //» должен быть необязательным.
  2. Оба домена и IP-адреса должны быть приняты.
  3. Любой должен быть принят допустимый домен верхнего уровня, например .aero и .xn - jxalpdlp.
  4. Номера портов должны быть разрешены.
  5. URL-адреса должны быть разрешены в обычных контекстах предложений. Например, в «Посетите stackoverflow.com.» Последняя точка не является частью URL-адреса.
  6. Вы, вероятно, захотите разрешить также URL-адреса «https: //» и, возможно, другие.
  7. Как всегда при отображении введенного пользователем текста в HTML вы хотите предотвратить межсайтовый скриптинг (XSS). Кроме того, вам нужно, чтобы амперсанды в URL-адресах были правильно экранированы как & amp ;.
  8. Вам, вероятно, не нужна поддержка адресов IPv6.
  9. Изменить : Как отмечалось в комментариях, поддержка адресов электронной почты определенно является плюсом.
  10. Изменить : ввод только обычного текста должен поддерживаться - теги HTML во входных данных не должны соблюдаться. (Версия Bitbucket поддерживает ввод HTML.)

Изменить : проверьте GitHub , чтобы узнать последнюю версию с поддержкой адресов электронной почты, аутентифицированных URL-адресов, URL-адресов в кавычках и скобках, ввода HTML, как а также обновленный список TLD.

Вот мой вариант:

<?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-адрес из-за отсутствия места), может потребоваться дальнейшая проверка допустимых доменов верхнего уровня.

Изменить : Следующий код устраняет две вышеуказанные проблемы, но немного больше подробный, поскольку я более или менее повторно реализую 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
ответ дан 26 November 2019 в 17:08
поделиться

Хотя сопоставить полную спецификацию URL сложно, вот регулярное выражение, которое обычно хорошо работает:

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

Однако, чтобы использовать это в preg_replace, вам нужно его избежать. Как так:

$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);
-2
ответ дан 26 November 2019 в 17:08
поделиться

Что-то вроде:

<?php
if(preg_match('@^http://(.*)\s|$@g', $textarea_url, $matches)) {
    echo '<a href=http://", $matches[1], '">', $matches[1], '</a>';
}
?>
0
ответ дан 26 November 2019 в 17:08
поделиться

Вот кое-что, что я обнаружил, испытанное и протестированное

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
ответ дан 26 November 2019 в 17:08
поделиться

это должно дать вам адреса электронной почты:

$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
)
1
ответ дан 26 November 2019 в 17:08
поделиться

Этот RegEx должен соответствовать любой ссылке, кроме этих новых доменов верхнего уровня из 3+ символов ...

{
  \\b
  # Match the leading part (proto://hostname, or just hostname)
  (
    # http://, or https:// leading part
    (https?)://[-\\w]+(\\.\\w[-\\w]*)+
  |
    # or, try to find a hostname with more specific sub-expression
    (?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \\. )+ # sub domains
    # Now ending .com, etc. For these, require lowercase
    (?-i: com\\b
        | edu\\b
        | biz\\b
        | gov\\b
        | in(?:t|fo)\\b # .int or .info
        | mil\\b
        | net\\b
        | org\\b
        | [a-z][a-z]\\.[a-z][a-z]\\b # two-letter country code
    )
  )

  # Allow an optional port number
  ( : \\d+ )?

  # The rest of the URL is optional, and begins with /
  (
    /
    # The rest are heuristics for what seems to work well
    [^.!,?;"\\'()\[\]\{\}\s\x7F-\\xFF]*
    (
      [.!,?]+ [^.!,?;"\\'()\\[\\]\{\\}\s\\x7F-\\xFF]+
    )*
  )?
}ix

Это написано не мной, я не совсем уверен, откуда я его взял, извините что я не могу отдать должное ...

1
ответ дан 26 November 2019 в 17:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: