Конвертировать url в ссылки из строки, за исключением случаев, когда они находятся в атрибуте тега html

Я пытаюсь преобразовать из ввода textarea ($_POST['content']) всех URL-адресов для ссылки.

$content = preg_replace('!(\s|^)((https?://)+[a-z0-9_./?=&-]+)!i', ' <a href="$2" target="_blank">$2</a> ', nl2br($_POST['content'])." ");
$content = preg_replace('!(\s|^)((www\.)+[a-z0-9_./?=&-]+)!i', '<a target="_blank" href="#" onclick="location.href='http://$2'; return false;"  target="_blank">$2</a> ', $content." ");

Форматы целевых ссылок: www.hello.com или http(s)://(www).hello.com

Но это, похоже, нарушает любой iframe, изображение или подобное,

Как есть /- правильное регулярное выражение, которое будет игнорировать URL-адреса в тэгах html?

Примечание. Я знаю, что мне нужно два выражения; один из них не обнаруживает никаких связей протокола (например, www.hello.com, поэтому мне нужно добавить его), а другой - для обнаружения URL-адресов с протоколом (поэтому нет необходимости добавлять).

Ответ 1

Ваш код в этом случае не должен быть проблемой внутри iframe и т.д., потому что там обычно есть " перед вашим URL, а не пробел, как того требует ваш шаблон.

Однако здесь есть другое решение. Он может не работать на 100%, если у вас есть один < или > в комментариях HTML или что-то подобное. Но в любом другом случае он должен хорошо вас обслуживать (и я не делаю это для вас проблемой или нет). Он использует отрицательный результат, чтобы убедиться, что закрытие > до открытия < (потому что это означает, что вы находитесь внутри тега).

$content = preg_replace('$(\s|^)(https?://[a-z0-9_./?=&-]+)(?![^<>]*>)$i', ' <a href="$2" target="_blank">$2</a> ', $content." ");
$content = preg_replace('$(\s|^)(www\.[a-z0-9_./?=&-]+)(?![^<>]*>)$i', '<a target="_blank" href="http://$2"  target="_blank">$2</a> ', $content." ");

В случае, если вы не знакомы с этой техникой, вот немного более подробная информация.

(?!        # starts the lookahead assertion; now your pattern will only match, if this subpattern does not match
[^<>]      # any character that is neither < nor >; the > is not strictly necessary but might help for optimization
*          # arbitrary many of those characters (but in a row; so not a single < or > in between)
>          # the closing >
)          # ends the lookahead subpattern

Обратите внимание, что я изменил разделители регулярных выражений, потому что теперь я использую ! в регулярном выражении.

Если вам нужен первый подшаблон (\s|^) для URL-адресов за пределами тегов, вы также можете удалить его (и уменьшить переменные захвата при замене).

$content = preg_replace('$(https?://[a-z0-9_./?=&-]+)(?![^<>]*>)$i', ' <a href="$1" target="_blank">$1</a> ', $content." ");
$content = preg_replace('$(www\.[a-z0-9_./?=&-]+)(?![^<>]*>)$i', '<a target="_blank" href="http://$1"  target="_blank">$1</a> ', $content." ");

И, наконец... вы намерены не заменять URL-адреса, содержащие анкоры в конце? Например. www.hello.com/index.html#section1? Если вы случайно это пропустили, добавьте # к вашим допустимым URL-адресам:

$content = preg_replace('$(https?://[a-z0-9_./?=&#-]+)(?![^<>]*>)$i', ' <a href="$1" target="_blank">$1</a> ', $content." ");
$content = preg_replace('$(www\.[a-z0-9_./?=&#-]+)(?![^<>]*>)$i', '<a target="_blank" href="http://$1"  target="_blank">$1</a> ', $content." ");

EDIT: Также, как насчет + и %? Также есть несколько других символов, которые могут отображаться в URL-адресе без кодирования. Смотрите это. КОНЕЦ РЕДАКТИРОВАНИЯ

Я думаю, что это должно сделать трюк для вас. Однако, если бы вы могли предоставить пример, показывающий рабочие и сломанные URL-адреса (с кодом, который у вас есть), мы могли бы фактически предоставить решения, которые будут проверены для работы во всех ваших случаях.

Последняя мысль. Правильное решение было бы использовать парсер DOM. Тогда вы можете просто применить регулярное выражение, которое у вас уже есть, только для текстовых узлов. Тем не менее, ваша забота о структуре HTML очень ограничена, и это делает вашу проблему регулярной снова (пока у вас нет непревзойденных "<" или " > " в ​​комментариях HTML или JavaScript или CSS на странице). Если у вас есть эти особые случаи, вы должны действительно заглянуть в парсер DOM. Ни одно из решений, представленных здесь (пока), в этом случае будет безопасным.

Ответ 2

  • По-моему, url - это все, что начинается с https?:// и заканчивается пробелом или концом строки (вертикальное пространство или так называемая новая строка).
  • Из-за первой точки изображения, ссылки и т.д. не будут заменены, потому что все они начинаются с "или > (кроме случаев, когда ссылка <a href=" http..."> начинается с пробела, но это недопустимый html).
  • Модификатор /m указывает регулярному выражению соответствовать каждой строке (так что работа, описанная в первой точке, будет работать).
  • Функция nl2br() должна использоваться после замены (из-за ссылок, начинающихся в начале строки).
  • Пробелы до и после добавляются только в том случае, если изначально существовало пространство в $content (см. $1 и $3 во втором параметре функции preg_replace()).
  • Это решение поддерживает имена доменов со специальными символами, например www.moški.si.

Ввод:

INPUT

Код:

<?php

$content =
    preg_replace(
        '~(\s|^)(https?://.+?)(\s|$)~im', 
        '$1<a href="$2" target="_blank">$2</a>$3', 
        $content
    );
$content = 
    preg_replace(
        '~(\s|^)(www\..+?)(\s|$)~im', 
        '$1<a href="http://$2" target="_blank">$2</a>$3', 
        $content
    );
$content = nl2br($content);

Вывод:

Output

Изменить:

Пример ссылок без https?:// префиксов + пример одиночного вызова preg_replace() (шаблоны и замены - это массив):

$content = 
    preg_replace(
        array(
            '~(\s|^)(www\..+?)(\s|$)~im', 
            '~(\s|^)(https?://)(.+?)(\s|$)~im', 
        ),
        array(
            '$1http://$2$3', 
            '$1<a href="$2$3" target="_blank">$3</a>$4', 
        ),
        $content
    );
$content = nl2br($content);

enter image description here

Ответ 3

Позвольте мне предложить что-то менее прямое: разделите текст ввода на части html и non-html, а затем обработайте части, отличные от html, с вашим регулярным выражением, объединяющим текст обратно в одну часть. Что-л. как:

  <?php
  $chunks = preg_split('/(<.*>)/Ums', $_POST['content'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
  $result = '';
  foreach ($chunks as $chunk) {
    if (substr($chunk,0,1) != '<') {
      /* do your processing on $chunk */
    }
    $result .= $chunk;
  }

Некоторые дополнительные советы:

  • попытайтесь сохранить исходный текст и сделать преобразование при его отображении. Это позволит вам улучшить/исправить код рендеринга, если в будущем вы найдете новую проблему/идею.
  • (https?://) + не должен находиться в скобках и вам не нужно +, потому что оно соответствует "https://https://some.com" - просто поместите https?://[ а-z0-9 _./?=& усилителя; -] +
  • то же самое о (www.) +:)

Ответ 4

Это было сделано сотни раз раньше. На этой странице m-buettner и glavić работают нормально, хотя мне нравится более яркое выражение лица.

Вот хороший ресурс php для этого: http://code.iamcal.com/php/lib_autolink/

Повторяет на Stackoverflow:

Достойная углубленная статья: - http://buildinternet.com/2010/05/how-to-automatically-linkify-text-with-php-regular-expressions/