Регулярные выражения с ограничением

235
24 июля 2018, 07:10

Работаю в PHP. Нужно заменить все ссылки, в которых присутствует расширение jpg, png и т.д. Делаю так:

$row["message"] = preg_replace('/<a\shref=\"(.+?[jpeg|jpg|png])\"\starget=\"_blank\">(.+?)<\/a>/is', '<a data-fancybox="gallery" href="$1"><img src="$1" alt="" class="tmp_class"></a>', $row["message"]);

Для тестирования следующая строка:

<p>С этой формы приходят заявки <a href="https://site.com/lack_tech.php">https://site.com/lack_tech.php</a></p>
<p>Или что ты имеешь ввиду?</p>
<div class="attachment_files_message"> 
   <p>Прикреплённые файлы:</p>
   <a href="http://site.com/public/uploads/kylticket/2670/Screenshot_1.png" target="_blank">Screenshot_1.png</a>
</div>

То есть нужно заменить вторую ссылку, а по моему коду, заменяется все что между началом ссылки <a href=".... и до самого последнего концевого тега </a>

Answer 1

Шаблон [jpeg|jpg|png] идентичен [jegnp|], так как [...] — это символьный класс, который находит 1 символ из указанных в классе. Кроме того, .+? может захватить слишком много текста, так как точка находит любой символ.

Если заменить \"(.+?[jpeg|jpg|png])\" на "([^"]*\.(?:jpe?g|png))", ошибка исчезнет, однако в некоторых случаях это выражение всё равно не сработает (разное количество пробельных символов, отсутствие или расположение в другом месте атрибута target=\"_blank\").

Решение на основе DOMDocument представляется более уместным в таком случае.

См. демо на PHP:

$html = <<<EOD
<p>С этой формы приходят заявки <a href="https://site.com/lack_tech.php">https://site.com/lack_tech.php</a></p>
<p>Или что ты имеешь ввиду?</p>
<div class="attachment_files_message"> 
   <p>Прикреплённые файлы:</p>
   <a href="http://site.com/public/uploads/kylticket/2670/Screenshot_1.png" target="_blank">Screenshot_1.png</a>
</div>
EOD;
$dom = new DOMDocument();    // Создаем DOM
$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); // Парсим DOM
$xpath = new DOMXPath($dom);         // Инициализируем структуру XPath на основе DOM    
foreach ($xpath->query('//a[@target="_blank"]') as $OurNode) {
    if (preg_match('~\.(?:jpe?g|png)$~i', $OurNode->getAttribute('href'))) {
        $fragment = $dom->createDocumentFragment();
        $aNode = $dom->createElement('a');
        $aNode->setAttribute('data-fancybox', 'gallery'); 
        $aNode->nodeValue = ''; 
        $aNode->setAttribute('href', $OurNode->getAttribute('href'));
        $img = $dom->createElement('img');
        $img->setAttribute("src", $OurNode->getAttribute('href'));
        $img->setAttribute("alt", "");
        $img->setAttribute("class", "tmp_class");
        $aNode->appendChild($img);
        $fragment->appendChild($aNode);
        $OurNode->parentNode->replaceChild($fragment, $OurNode);
    }
}
echo mb_convert_encoding($dom->saveHTML(), 'UTF-8', 'HTML-ENTITIES');

Результат

<p>С этой формы приходят заявки <a href="https://site.com/lack_tech.php">https://site.com/lack_tech.php</a><p>Или что ты имеешь ввиду?</p><div class="attachment_files_message"> 
   <p>Прикреплённые файлы:</p>
   <a data-fancybox="gallery" href="http://site.com/public/uploads/kylticket/2670/Screenshot_1.png"><img src="http://site.com/public/uploads/kylticket/2670/Screenshot_1.png" alt="" class="tmp_class"></a>
</div></p>

В foreach ($xpath->query('//a[@target="_blank"]') as $OurNode) мы ищем все теги a с атрибутом target равным _blank. Далее мы проверяем, заканчивается ли значение атрибута href подстроками .jpeg, .jpg или .png, затем создаем новый тег a, добавляем к нему атрибуты, дочерний элемент img с атрибутами, а в конце заменяем старый тег a на новый.

Если и использовать тут регулярное выражение, стоит попробовать

preg_replace('~<a\s+((?:[^\s<>\'"=]+(?:=(?:"[^"]*"|\'[^\']*\'|[^\s\'">]+))?\s+)*)href=(?|"([^"]*\.(?:jpe?g|png))"|\'([^\']*\.(?:jpe?g|png))\'|([^\s\'">]*\.(?:jpe?g|png))(?=[>\s]))((?:\s+[^\s<>\'"=]+(?:=(?:"[^"]*"|\'[^\']*\'|[^\s\'">]+))?)*)\s*>(.*?)</a>~is', 
    '<a $1$3 data-fancybox="gallery" href="$2"><img src="$2" alt="" class="tmp_class"></a>',
    $row["message"])

Демо регулярного выражения

Подробности

  • <a - подстрока <a
  • \s+ - 1+ пробельных символов
  • ((?:[^\s<>'"=]+(?:=(?:"[^"]*"|'[^']*'|[^\s'">]+))?\s+)*) - захватывающая подмаска №1: 0 и более повторов атрибутов с опциональными значениями (т.е. может быть что-то типа required, target='_blank' и т.д.)
  • href=(?|"([^"]*\.(?:jpe?g|png))"|'([^']*\.(?:jpe?g|png))'|([^\s'">]*\.(?:jpe?g|png))(?=[>\s])) - =, за которым следуют опциональные кавычки '/" и захватывающая подмаска №2, которая захватывает текст внутри '...' / "..." или символы, отличные от пробельных, ', " и >, если значение href заканчивается на . + jpeg, jpg или png
  • ((?:\s+[^\s<>'"=]+(?:=(?:"[^"]*"|'[^']*'|[^\s'">]+))?)*) - захватывающая подмаска №3: 0 и более повторов атрибутов с опциональными значениями (т.е. может быть что-то типа required, target='_blank' и т.д.)
  • \s*> - 0+ пробельных символов и >
  • (.*?) - захватывающая подмаска №4: 0 и более любых символов до первого вхождения
  • </a> - подстрока </a>.

Заменить совпадение нужно <a $1$3 data-fancybox="gallery" href="$2"><img src="$2" alt="" class="tmp_class"></a>, так как в захватывающих группах №1 и №3 могут быть дополнительные атрибуты.

См. также PHP-демо:

$message = '<p>С этой формы приходят заявки <a href="https://site.com/lack_tech.php">https://site.com/lack_tech.php</a></p>
<p>Или что ты имеешь ввиду?</p>
<div class="attachment_files_message"> 
   <p>Прикреплённые файлы:</p>
   <a href="http://site.com/public/uploads/kylticket/2670/Screenshot_1.png" target="_blank">Screenshot_1.png</a>
</div>';
$message = preg_replace('~<a\s+((?:[^\s<>\'"=]+(?:=(?:"[^"]*"|\'[^\']*\'|[^\s\'">]+))?\s+)*)href=(?|"([^"]*\.(?:jpe?g|png))"|\'([^\']*\.(?:jpe?g|png))\'|([^\s\'">]*\.(?:jpe?g|png))(?=[>\s]))((?:\s+[^\s<>\'"=]+(?:=(?:"[^"]*"|\'[^\']*\'|[^\s\'">]+))?)*)\s*>(.*?)</a>~is', 
        '<a $1$3 data-fancybox="gallery" href="$2"><img src="$2" alt="" class="tmp_class"></a>',
        $message);
echo $message;

Результат

<p>С этой формы приходят заявки <a href="https://site.com/lack_tech.php">https://site.com/lack_tech.php</a></p>
<p>Или что ты имеешь ввиду?</p>
<div class="attachment_files_message"> 
   <p>Прикреплённые файлы:</p>
   <a  target="_blank" data-fancybox="gallery" href="http://site.com/public/uploads/kylticket/2670/Screenshot_1.png"><img src="http://site.com/public/uploads/kylticket/2670/Screenshot_1.png" alt="" class="tmp_class"></a>
</div>
READ ALSO
Перенос функций из JS в PHP

Перенос функций из JS в PHP

Есть функции, написанные на JS:

204
Как передать в функцию параметры из объекта?

Как передать в функцию параметры из объекта?

Всем привет, я только начинаю изучать ООП и столкнулся вот с такой проблемой

218
Поиск QMainWindow в приложении

Поиск QMainWindow в приложении

Внедряюсь/Инжектируюсь в стороннее Qt-приложениеПеребираю все QWidget, но не нахожу среди них QMainWindow

145
Копирование указателей из одного std::map в другой

Копирование указателей из одного std::map в другой

Мне нужно скопировать данные из одного std::map в другой std::map, при условии что эти данные выражены в виде указателейТо есть с помощью оператора...

169