Есть строка
<script>alert(123)</script> <h1>something</h1> <blockquote>something</blockquote>
Всё что мне надо сделать - это функцию, которая будет экранировать тег script и другие, кроме blockquote и пары других тегов (b, u, h1...).
Задача вроде не сложная, подумал я и сделал вот такую штукенцию:
/*
Обработчик текста. Функция экранирует весь контент, кроме определённых тегов ($tagsLetters)
*/
public static function contentProcessing(string $str)
{
$tagsLetters = ['blockquote', 'b', 'i', 'u', 's', 'code', 'h1'];
$result = '';
$openAndCloseTags = []; // openTag => closeTag
// Получил открывающий и закрывающий теги
foreach ($tagsLetters as $tag) {
$openTag = '<' . $tag . '>';
$closeTag = '</' . $tag . '>';
$openAndCloseTags[$openTag] = $closeTag;
}
// Заменил закрывающие теги на сгенерированным разделитель типа: '-?#14#&-'
$replacedCloseTagsString = $str;
$replaceString = '-?#' . rand(10, 20) . '#&-';
foreach ($openAndCloseTags as $openTag => $closeTag) {
$replacedCloseTagsString = str_replace($closeTag, $replaceString, $replacedCloseTagsString);
}
// Разбил главную строку по сгенерированным разделителям ($replacedCloseTagsString)
$exploadedByReplacedCloseTag = explode($replaceString, $replacedCloseTagsString);
// Получил общее количество тегов в строке, которое нужно экранировать (нужно при результирующей обработке)
$countOfNotEncodeTags = 0;
foreach ($exploadedByReplacedCloseTag as $string) {
foreach($openAndCloseTags as $openTag => $closeTag) {
if (is_numeric(strpos($string, $openTag))) {
$countOfNotEncodeTags++;
}
}
}
// Если нет тегов, которые НЕ нужно экранировать, то возвращаем экранированный основной контент
if ($countOfNotEncodeTags == 0) {
return htmlspecialchars($str);
}
// Обработал $exploadedByReplacedCloseTag, получив результирующую строку
$iter = 0;
foreach ($exploadedByReplacedCloseTag as $string) {
foreach ($openAndCloseTags as $openTag => $closeTag) {
// Если есть указанный тег в строке, то разбиваем её по нему и соединяем, экранирую нужный контент
if (is_numeric(strpos($string, $openTag))) {
$explodeContent = explode($openTag, $string);
$result .= htmlspecialchars($explodeContent[0]) . $openTag . htmlspecialchars($explodeContent[1]) . $closeTag;
}
// Если тегов больше не осталось и остался контент, то добавляем его в результирующею строку
if ($iter == ($countOfNotEncodeTags - 1)) {
$result .= htmlspecialchars($exploadedByReplacedCloseTag[$iter + 1]);
break 2;
}
}
$iter++;
}
return $result;
}
Функция рабочая(есть недостатки - не работает со вложенными тегами, но это не сильно мешает), и я поражаюсь как я вообще смог додуматься до такого!
У меня возникает вопрос - у кого-нибудь есть идеи, как решить задачу более красивым, может быть, производительным методом?
Если что, мне хватит объяснений просто на словах...