#php #preg-replace
#php #предварительная замена
Вопрос:
Я пытаюсь разрешить некоторые теги и атрибуты с использованием массива и удалить остальные
вот мой пример:
$allowed=array("img", "p", "style");
$text='<img src="image.gif" onerror="myFunction()" style="background:red" onclick="myFunction()">
<p>A function is triggered if an error occurs when loading the image. The function shows an alert box with a text.
In this example we refer to an image that does not exist, therefore the onerror event occurs.</p>
<script>
function myFunction() {
alert('The image could not be loaded.');
}
</script>';
использование $text= preg_replace('#<script(.*?)>(.*?)</script>#is', '', $text);
Я мог бы удалить тег скрипта с содержимым, но мне нужно удалить все, что не находится в $allowed
массиве
Комментарии:
1. Вы можете использовать этот HTML-минификатор, который я написал, с небольшой доработкой его можно было бы сделать для удаления определенных тегов. У него есть возможность не уменьшать определенные теги. Таким образом, вы могли бы изменить это на их удаление (вероятно) github.com/ArtisticPhoenix/MISC/blob/master/Lexers/… Он использует регулярное выражение в виде лексера / синтаксического анализатора.
2. @ArtisticPhoenix
ini_set('display_errors', 1);
не следует использовать в производственных средах, возможно, потребуется добавить комментарий к этому GIT.3. На самом деле это не должно было быть производственным кодом, на самом деле это было для другого ответа здесь. Вот почему это в MISC. и это говорит
//For debugging
4. Существуют ли какие-либо вложенные теги? например
<div><p>text</p><img />more text<p>text</p></div>
5. Да, содержимое взято из текстового редактора
Ответ №1:
Я бы посоветовал использовать DOMParser для лучшей читаемости, если вы смешиваете скрипты с html таким образом, позаботьтесь о производительности, если производительность имеет значение.
Ответ №2:
Эта функция должна делать то, что вы хотите. Учитывая DOMDocument
( $doc
) и узел ( $node
) для поиска, он рекурсивно выполняет итерацию по дочерним элементам этого узла, удаляя любые теги, которых нет в $allowed_tags
массиве, и для тех тегов, которые сохранены, удаляя любые атрибуты, которых нет в $allowed_attributes
массиве:
function remove_nodes_and_attributes($doc, $node, $allowed_tags, $allowed_attributes) {
$xpath = new DOMXPath($doc);
foreach ($xpath->query('./*', $node) as $child) {
if (!in_array($child->nodeName, $allowed_tags)) {
$node->removeChild($child);
continue;
}
$a = 0;
while ($a < $child->attributes->length) {
$attribute = $child->attributes->item($a)->name;
if (!in_array($attribute, $allowed_attributes)) {
$child->removeAttribute($attribute);
// don't increment the pointer as the list will shift with the removal of the attribute
}
else {
// allowed attribute, skip it
$a ;
}
}
// remove any children as necessary
remove_nodes_and_attributes($doc, $child, $allowed_tags, $allowed_attributes);
}
}
Вы бы использовали эту функцию следующим образом. Обратите внимание, что необходимо обернуть HTML в элемент верхнего уровня, который затем снова удаляется в конце с помощью substr
.
$doc = new DOMDocument();
$doc->loadHTML("<html>$text</html>", LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$html = $doc->getElementsByTagName('html')[0];
remove_nodes_and_attributes($doc, $html, $allowed_tags, $allowed_attributes);
echo substr($doc->saveHTML(), 6, -8);
Вывод (для ваших выборочных данных):
<img style="background:red">
<p>A function is triggered if an error occurs when loading the image. The function shows an alert box with a text. In this example we refer to an image that does not exist, therefore the onerror event occurs.</p>
Ответ №3:
Использование DOMDocument — это всегда лучший способ манипулировать HTML, он понимает структуру документа.
В этом решении я использую XPath для поиска любых узлов, которых нет в списке разрешенных, выражение XPath будет выглядеть примерно так…
//body//*[not(name() = "img" or name() = "p" or name() = "style")]
Выполняется поиск любого элемента в <body>
теге ( loadHTML
автоматически добавит этот тег для вас), имени которого нет в списке разрешенных тегов. XPath создается динамически из $allowed
списка, поэтому вы просто меняете список тегов, чтобы обновить его…
$allowed=array("img", "p", "style");
$text='<img src="image.gif" onerror="myFunction()" style="background:red" onclick="myFunction()">
<p>A function is triggered if an error occurs when loading the image. The function shows an alert box with a text.
In this example we refer to an image that does not exist, therefore the onerror event occurs.</p>
<script>
function myFunction() {
alert('The image could not be loaded.');
}
</script>';
$doc = new DOMDocument();
$doc->loadHTML($text);
$xp = new DOMXPath($doc);
$find = '//body//*[not(name() = "'.implode ('" or name() = "', $allowed ).
'")]';
echo "XPath = ".$find.PHP_EOL;
$toRemove = $xp->evaluate($find);
print_r($toRemove);
foreach ( $toRemove as $remove ) {
$remove->parentNode->removeChild($remove);
}
// recreate HTML
$outHTML = "";
foreach ( $doc->getElementsByTagName("body")[0]->childNodes as $tag ) {
$outHTML.= $doc->saveHTML($tag);
}
echo $outHTML;
Если вы также хотите удалить атрибуты, вы можете выполнить тот же процесс, используя @*
как часть выражения XPath…
$allowedAttribs = array();
$find = '//body//@*[not(name() = "'.implode ('" or name() = "', $allowedAttribs ).
'")]';
$toRemove = $xp->evaluate($find);
foreach ( $toRemove as $remove ) {
$remove->parentNode->removeAttribute($remove->nodeName);
}
Можно было бы объединить эти два, но это делает код менее разборчивым (ИМХО).
Комментарии:
1. Это не удаляет атрибуты (
src
,onclick
иonerror
) изimg
тега: 3v4l.org/1RR3A2. @Nick — Я добавил это. Было бы интересно, если бы они хотели получить определенные атрибуты от определенных типов элементов — или я не должен упоминать об этом :-/
3. Я думаю, что всегда безопаснее не задавать вопросы такого типа, вы просто можете получить ответ! 🙂