Php DOMDocument从HTML源中删除脚本标记

Php DOMDocument从HTML源中删除脚本标记,php,html-parsing,xss,domdocument,script-tag,Php,Html Parsing,Xss,Domdocument,Script Tag,我曾经使用内置的DOMDocument从HTML文档中删除脚本标记。问题是,如果我有一个包含Javascript内容的脚本标记,然后又有一个链接到外部Javascript源文件的脚本标记,那么并非所有的脚本标记都会从HTML中删除 $result = ' <!doctype html> <html> <head> <meta charset="utf-8"> <title>

我曾经使用内置的DOMDocument从HTML文档中删除脚本标记。问题是,如果我有一个包含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();
}
$result=
嘿
警惕(“你好”);
嘿
';
$dom=新的DOMDocument();
if($dom->loadHTML($result))
{
$script_tags=$dom->getElementsByTagName('script');
$length=$script\u标签->长度;
对于($i=0;$i<$length;$i++){
如果(是对象($script\u tags->item($i)->parentNode)){
$script\u tags->item($i)->parentNode->removeChild($script\u tags->item($i));
}
}
echo$dom->saveHTML();
}
上述代码输出:

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


嘿
警惕(“你好”);
嘿

从输出中可以看到,只删除了外部脚本标记。我能做些什么来确保删除所有脚本标记吗?

您的错误实际上很小。
domnodel
对象(及其所有子对象-
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();

我不是在循环——只是一次一个地弹出第一个元素。结果是:

为了避免出现活动节点列表的意外情况(删除节点时会变短),可以使用
迭代器\u To \u array
将副本复制到数组中:

foreach(iterator_to_array($dom->getElementsByTagName($tag)) as $node) {
    $node->parentNode->removeChild($node);
};  

另一种解决方案是以相反的顺序循环元素。请注意,使用loadHTML()时,这会中断DOMDocument解析,因为Javascript字符串中有HTML标记:
var str='this NOT get remove';
+1比公认的答案简单得多-在遍历文档进行更改时,在节点列表上使用
迭代器\u to\u array()
基本上是必须的。哇!你刚刚救了我一个巨大的头痛。非常感谢!