作为大部分(全部?)进行HTML净化的PHP库,如HTML Purifier都严重依赖于正则表达式,我认为尝试编写一个使用DOMDocument和相关类的HTML净化器将是一项值得尝试的实验。虽然我目前处于非常早期的阶段,但该项目已经显示出了一些潜力。
我的想法围绕着一个类,它使用DOMDocument来遍历提供的标记中的所有节点,将它们与白名单进行比较,并删除不在白名单上的任何内容。(第一次实现非常基础,仅根据节点类型删除节点,但我希望在未来更加复杂并分析节点的属性,链接地址是否指向不同域等等)。
我的问题是如何遍历DOM树?据我所知,DOM*对象具有childNodes属性,那么我需要递归整个树吗?此外,对DOMNodeLists的早期实验表明,您需要非常小心地删除顺序,否则可能会留下物品或触发异常。
如果有人有PHP中操作DOM树的经验,我会很感激您对此主题提供的任何反馈。
编辑:我已经为我的HTML清理类构建了以下方法。它递归遍历DOM树并检查找到的元素是否在白名单上。如果它们不在白名单上,则将其删除。
我遇到的问题是,如果您删除一个节点,则DOMNodeList中所有后续节点的索引都会更改。从底部向上简单地工作可以避免这个问题。目前它仍然是一种非常基本的方法,但我认为它显示出了潜力。它肯定比HTMLPurifier要快得多,尽管Purifier做了很多其他的东西。
我的想法围绕着一个类,它使用DOMDocument来遍历提供的标记中的所有节点,将它们与白名单进行比较,并删除不在白名单上的任何内容。(第一次实现非常基础,仅根据节点类型删除节点,但我希望在未来更加复杂并分析节点的属性,链接地址是否指向不同域等等)。
我的问题是如何遍历DOM树?据我所知,DOM*对象具有childNodes属性,那么我需要递归整个树吗?此外,对DOMNodeLists的早期实验表明,您需要非常小心地删除顺序,否则可能会留下物品或触发异常。
如果有人有PHP中操作DOM树的经验,我会很感激您对此主题提供的任何反馈。
编辑:我已经为我的HTML清理类构建了以下方法。它递归遍历DOM树并检查找到的元素是否在白名单上。如果它们不在白名单上,则将其删除。
我遇到的问题是,如果您删除一个节点,则DOMNodeList中所有后续节点的索引都会更改。从底部向上简单地工作可以避免这个问题。目前它仍然是一种非常基本的方法,但我认为它显示出了潜力。它肯定比HTMLPurifier要快得多,尽管Purifier做了很多其他的东西。
/**
* Recursivly remove elements from the DOM that aren't whitelisted
* @param DOMNode $elem
* @return array List of elements removed from the DOM
* @throws Exception If removal of a node failed than an exception is thrown
*/
private function cleanNodes (DOMNode $elem)
{
$removed = array ();
if (in_array ($elem -> nodeName, $this -> whiteList))
{
if ($elem -> hasChildNodes ())
{
/*
* Iterate over the element's children. The reason we go backwards is because
* going forwards will cause indexes to change when elements get removed
*/
$children = $elem -> childNodes;
$index = $children -> length;
while (--$index >= 0)
{
$removed = array_merge ($removed, $this -> cleanNodes ($children -> item ($index)));
}
}
}
else
{
// The element is not on the whitelist, so remove it
if ($elem -> parentNode -> removeChild ($elem))
{
$removed [] = $elem;
}
else
{
throw new Exception ('Failed to remove node from DOM');
}
}
return ($removed);
}