DOMDocument удаляет теги скриптов из источника HTML


Я использовал подход @Alex здесь для удаления тегов скриптов из HTML-документа с помощью встроенного DOMDocument. Проблема в том, что если у меня есть тег сценария с содержимым Javascript, а затем другой тег сценария, который ссылается на внешний исходный файл Javascript, не все теги сценария удаляются из HTML.

$result = '
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>
            hey
        </title>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
        <script>
            alert("hello");
        </script>
    </head>
    <body>hey</body>
</html>
';

$dom = new DOMDocument();
if($dom->loadHTML($result))
{
    $script_tags = $dom->getElementsByTagName('script');

    $length = $script_tags->length;

    for ($i = 0; $i < $length; $i++) {
        if(is_object($script_tags->item($i)->parentNode)) {
            $script_tags->item($i)->parentNode->removeChild($script_tags->item($i));
        }
    }

    echo $dom->saveHTML();
}

Приведенный выше код выводит:

<html>
    <head>
        <meta charset="utf-8">
        <title>hey</title>
        <script>
        alert("hello");
        </script>
    </head>
    <body>
        hey
    </body>
</html>

Как вы можете видеть из вывода, был удален только тег внешнего скрипта. Могу ли я что-нибудь сделать, чтобы обеспечить все теги скрипта удалены?

Author: Community, 2013-04-10

2 answers

Ваша ошибка на самом деле тривиальна. Объект DOMNode (и все его потомки - DOMElement, DOMNodeList и еще несколько других!) автоматически обновляется при изменении родительского элемента, особенно при изменении количества дочерних элементов. Это написано в нескольких строках в документе PHP, но в основном скрыто под ковром.

Если вы выполняете цикл с использованием ($k instanceof DOMNode)->length, а затем удаляете элементы из узлов, вы заметите, что свойство length на самом деле меняется! Мне пришлось написать свою собственную библиотеку, чтобы противодействуйте этому и нескольким другим причудам.

Решение:

if($dom->loadHTML($result))
{
    while (($r = $dom->getElementsByTagName("script")) && $r->length) {
            $r->item(0)->parentNode->removeChild($r->item(0));
    }
echo $dom->saveHTML();

На самом деле я не зацикливаюсь - просто открываю первый элемент по одному за раз. Результат: http://sebrenauld.co.uk/domremovescript.php

 21
Author: Sébastien Renauld, 2013-04-10 12:44:25

Чтобы избежать неожиданностей, связанных с живым списком узлов, который становится короче по мере удаления узлов, вы можете работать с копией в массив, используя iterator_to_array:

foreach(iterator_to_array($dom->getElementsByTagName($tag)) as $node) {
    $node->parentNode->removeChild($node);
};  
 5
Author: trincot, 2016-06-10 23:07:15