Поиск текста между тегами
Допустим, у нас есть следующий текст:
$text = '<p>Ищем <span>эту</span> и <span>может быть эту</span> строки в тексте.</p>';
И из него нужно достать текст, который находится между тегами <span> и </span>.
Проще всего это сделать с помощью регулярных выражений:
$text = '<p>Ищем <span>эту</span> и <span>может быть эту</span> строки в тексте.</p>';
if(preg_match_all('|<span>(.*)</span>|Uis', $text, $result))
{
foreach($result[1] as $span_text)
echo $span_text . '<br>';
}
else
echo 'Совпадений нет';
Функция preg_match_all() принимает 3 параметра: шаблон поиска, сам текст и переменную, в которую эта функция сохранит результаты поиска.
Поскольку функция возвращает количество найденных строк (или false в случае ошибки), мы можем сразу подставить её в оператор if.
Массив с результатами поиска (в нашем случае $result) состоит из двух частей: в $result[0] будут найденные строки вместе с открывающим и закрывающим тегами span, а в $result[1] будут те же строки без тега span, т.е. тот текст, что находится в круглых скобках.
Маска регулярного выражения находится между вертикальными чертами |. В шаблоне (.*) точка означает любой символ, звёздочка - любое количество символов (т.е. суммарно получаем "любое количество любых символов").
Скобки говорят, что найденный текст нам нужно получить отдельно. Без скобок мы получим только $result[0], а $result[1] не будет существовать.
Чтобы найти только не пустые теги, можно заменить .* на .+. Плюсик означает любое количество символов, но не меньше одного.
Uis - модификаторы. U означает работу с UTF-8, i - регистронезависимый поиск, s - что символ точка включает в себя переносы строк, т.е. поиск будет по всем строкам, а не по одной.
Простая замена текста или тегов (preg_replace)
Заменить текст без замены тегов можно следующим образом:
$text = '<p>Строки <strong>один</strong> и <strong>два</strong> в тексте.</p>';
$new_text = preg_replace('|(<strong>).*(</strong>)|Uis', '$1три$2', $text);
echo $new_text;
// Выведет: <p>Строки <strong>три</strong> и <strong>три</strong> в тексте.</p>
$1 и $2 содержат открывающий и закрывающий теги соответственно, поскольку мы поместили их в скобки.
А в следующем примере меняются только теги, сам текст остаётся нетронутым:
<?php
$text = '<p>Строки <strong>один</strong> и <strong>два</strong> в тексте.</p>';
$new_text = preg_replace('|<strong>(.*)(</strong>)|Uis', '<span>$1</span>', $text);
echo $new_text;
// <p>Строки <span>один</span> и <span>два</span> в тексте.</p>
Замена текста собственной функцией (preg_replace_callback)
Самое вкусное. Допустим, мы хотим использовать на сайте что-то вроде BBCode, т.е. собственные теги, которые потом должны заменяться на обычный HTML код:
<div>
{h1}Заголовок{/h1}
</div>
Заменить тег {h1} на обычный HTML тег <h1> можно так:
<?php
$text = '<div>{h1}Заголовок{/h1}</div>';
$new_text = preg_replace_callback('|{h1}(.*){/h1}|Uis', function($matches) {
return '<h1>' . $matches[1] . '</h1>';
}, $text);
echo $new_text;
Функция preg_replace_callback передаёт каждую найденную строку в нашу безымянную функцию, затем заменяет найденный текст на то, что наша функция возвращает.
Не знаком с безымянными функциями? Тогда можно сделать так:
function replaceH1($matches) {
return '<h1>' . $matches[1] . '</h1>';
}
$text = '<div>{h1}Заголовок{/h1}</div>';
$new_text = preg_replace_callback('|{h1}(.*){/h1}|Uis', 'replaceH1', $text);
echo $new_text;
Вторым параметром передаём название нашей функции. Код отработает точно также, как и предыдущий.
Вывод фрагментов исходного HTML и PHP кода
Частая проблема разработчиков, которым хочется вести свой блог. Есть HTML статья, внутри которой некоторые фрагменты кода нужно прогонять через htmlspecialchars(), чтобы они выводились как обычный текст:
$text ='
<h2>Пример кода</h2>
{code}<strong>Этот текст не должен быть жирным</strong>{/code}
<p>Какое-то описание кода выше.</p>
{code}<strong>Второй не жирный текст</strong>{/code}
';
Теперь в этом нет ничего сложного:
$text = '
<h2>Пример кода</h2>
{code}<strong>Этот текст не должен быть жирным</strong>{/code}
<p>Какое-то описание кода выше.</p>
{code}<strong>Второй не жирный текст</strong>{/code}
';
$new_text = preg_replace_callback('|{code}(.*){/code}|Uis', function($matches) {
return htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
}, $text);
echo $new_text;
Продвинутый BBCode с атрибутами
Иногда описанного выше функционала бывает недостаточно, например если нужно передать в функцию какие-либо параметры:
{link url="/some_url" title="Заголовок ссылки"}текст ссылки{/link}
Вместо собственного велосипеда рекомендую использовать готовую библиотеку Shortcode.
В ней из коробки уже есть возможность использования атрибутов, а также события и куча других полезных фишек. Вот как может выглядеть пример обработки тега с атрибутами:
use Thunder\Shortcode\ShortcodeFacade;
$text = 'текст с тегами';
$shortcode = new ShortcodeFacade();
$shortcode->addHandler('link', function($s) {
$content = $s->getContent();
$url = $s->getParameter('url');
$title = $s->getParameter('title');
if(!$title)
$title = 'Стандартный заголовок';
// Ещё какие-нибудь действия
return $content;
});
$new_text = $shortcode->process($text);
Только нужно учесть, что в Shortcode по-умолчанию парсятся атрибуты в квадратных скобках []. Я и сам использую квадратные, но в примерах использовал фигурные, чтобы избежать возможных конфликтов тегов на этом сайте.