PHP функция для удаления определенных HTML-тегов и атрибутов
В течение долгого времени я пользуюсь функцией PHP HTML “очистки” пользовательского ввода, чтобы гарантировать, что никакой вредоносный код не будет размещен нарочно или случайно. Но при этом я разрешаю использовать определенные HTML-теги и атрибуты:

Большая часть функций для работы с HTML-кодом используют регулярные выражения, а это сплошная головная боль. В течение длительного времени созданная мной функция работала достаточно хорошо, пока мне не понадобилось разрешить внутри некоторых тегов data-атрибуты HTML5.
Они выглядят следующим образом:
data-conversation="none"
После “data” идет имя переменной для соответствующего типа данных. И здесь старая версия моей функции не смогла справиться с этой переменной частью имени, и мне приходилось добавлять каждый тип данных атрибута в используемый мной список.
В итоге я разработал улучшенную функцию, которая использует PHP библиотеку DOMDocument, чтобы создать объектную модель документа и удалить всё кроме тегов и атрибутов, которые необходимо сохранить.
PHP функция для удаления определенных HTML-тегов и атрибутов
Новый подход позволяет использовать регулярное выражение только для выбранных типов данных — не надо искать эти атрибуты в HTML-коде. Задача поиска тегов и атрибутов осуществляется с помощью XPath методов PHP DOMDocument.
Новая функция удаления HTML тегов в PHP принимает строку для “очистки” и два массива в качестве параметров; массивы - список разрешенных тегов и список допустимых атрибутов. Если атрибуты “href” или “src” разрешены, функция проверяет: если значение атрибута – это код JavaScript, то изменяет его на “#”:
<?php
function stripTagsAttributes($html, $allowedTags = array(), $allowedAttributes = array('(?:a^)')) {
if (!empty($html)) {
$theTags = count($allowedTags) ? '<' . implode('><', $allowedTags) . '>' : '';
$theAttributes = '%' . implode('|', $allowedAttributes) . '%i';
$dom = @DOMDocument::loadHTML(
mb_convert_encoding(
strip_tags(
$html,
$theTags
),
'HTML-ENTITIES',
'UTF-8'
)
);
$xpath = new DOMXPath($dom);
$tags = $xpath->query('//*');
foreach ($tags as $tag) {
$attrs = array();
for ($i = 0; $i < $tag->attributes->length; $i++) {
$attrs[] = $tag->attributes->item($i)->name;
}
foreach ($attrs as $attribute) {
if (!preg_match($theAttributes, $attribute)) {
$tag->removeAttribute($attribute);
} elseif (preg_match('%^(?:href|src)$%i', $attribute) and preg_match('%^javascript:%i', $tag->getAttribute($attribute))) {
$tag->setAttribute($attribute, '#');
}
}
}
return (
trim(
strip_tags(
html_entity_decode(
$dom->saveHTML()
),
$theTags
)
)
);
}
}
?>
Перед тем, как с помощью PHP убрать HTML теги, расскажу о нескольких проблемах, связанных с использованием DOMDocument:
- Если вы парсите фрагмент кода вместо всей страницы, и в нем нет тега кодировки символов, то DOMDocument будет считать, что текст закодирован в ISO-8859 вместо UTF-8. Поэтому, прежде чем загрузить фрагмент кода, эта функция использует mb_convert_encoding, чтобы преобразовать любые символы Unicode в объекты HTML. Затем использует html_entity_decode, чтобы преобразовать объекты назад в символы, когда парсинг завершится;
- Когда вы парсите HTML- фрагмент, DOMDocument всегда добавляет DOCTYPE, <HTML> и <Body> теги к фрагменту, и вы не можете отключить эту "опцию". Поэтому после парсинга моя функция использует strip_tags во второй раз, чтобы удалить лишние и ненужные теги.
Два массива тегов и атрибутов похожи на комментарии. Заметьте, что последний элемент в массиве $commentAttributes - простое регулярное выражение, которое соответствует любому типу данных атрибута:
<?php
$commentTags = array(
'b',
'i',
'a',
'strong',
'em',
'blockquote',
'div',
'p',
'br',
'strike',
'del',
'sup',
'sub',
'code',
'pre',
'span',
'img',
'button',
);
$commentAttributes = array(
'href',
'rel',
'target',
'src',
'width',
'height',
'class',
'data-S*'
);
?>
Кстати, при удалении HTML тегов из PHP соблюдаются дополнительные меры санитизации (где добавлены комментарии). Например, изображения могут быть встроены только в комментарии, если они загружены и размещены на сайте, иначе они превращаются в ссылки на внешние изображения. Это необходимо для предотвращения распространения изображений, содержащих вредоносный код, порно и т. д.