避免使用 DOMDocument::saveHTML 自动关闭打开的 HTML 元素
Avoiding automatic closure of open HTML elements with DOMDocument::saveHTML
我打算将自定义数据属性添加到 div
和 ul
容器,如果该容器使用特定的 class。我从 HTML 片段开始,这些片段包含有效的 HTML(完整的 div
及其内容和结尾的 </div>
)或仅包含开头的 div
(没有其内容和结尾 </div>
).
以下是我可能开始的内容示例:
<div id='gallery-7222-1' class='gallery galleryid-7222 gallery-columns-3 gallery-size-thumbnail'>
这是我到目前为止尝试过的方法:
// grab all containers from the HTML.
$dom_doc = new DOMDocument();
/*
* $html here can be the example I posted above.
* LIBXML_HTML_NOIMPLIED and LIBXML_HTML_NODEFDTD are used
* to avoid adding a doctype and wrapping the whole output in HTML tags.
*/
$dom_doc->loadHTML( $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
// Let's look for lists and divs.
$ul_tags = $dom_doc->getElementsByTagName( 'ul' );
$div_tags = $dom_doc->getElementsByTagName( 'div' );
/*
* Loop through each ul, and add the data.
* I do more things in there, like checking for what class the ul has,
* but I've simplified things here.
*/
foreach ( $ul_tags as $ul_tag ) {
$ul_tag->setAttribute( 'data-foo', 'bar' );
}
/*
* Loop through each div, and add the data.
* I do more things in there, like checking for what class the div has,
* but I've simplified things here.
*/
foreach ( $div_tags as $div_tag ) {
$div_tag->setAttribute( 'data-foo', 'bar' );
}
// Save our updated HTML.
$html = $dom_doc->saveHTML();
返回的 HTML 包括新的数据属性,但也包括我在这里并不真正想要的结束 </div>
。你可以在这里看到它:
https://ideone.com/sVfAOn
我首先想到用 substr
删除结束 </div>
,但我做不到:
- 在某些情况下,我原来的 HTML 实际上包含一个我想保留的结束 div 标签。
- 有时我可能正在编辑包含 ul
的字符串。
我该如何阻止 saveHTML()
在这里变得那么聪明,并试图为我修复我的 HTML?
谢谢!
不,您无法说服 HTML 解析器不解析 HTML。最好的解决方案是首先重新考虑您获取数据的方法,并确保您不会获取碎片。
否则,您可以先尝试将其视为 XML,看看它是否损坏:
<?php
libxml_use_internal_errors(true);
$dom_doc = new DOMDocument();
$remove = "";
// try loading it as xml
if (!$dom_doc->loadXml($html)) {
// it failed, get the error message
$err = libxml_get_last_error()->message ?? "";
// is it because of an unclosed element?
// find out which element it's breaking on
if (preg_match("/end of data in tag (.*?) /", $err, $matches)) {
$remove = "</$matches[1]>";
}
}
$dom_doc->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
// do stuff
$html = str_replace($remove, "", $dom_doc->saveHTML());
当然,这假定您正在处理 HTML,否则它是干净的并且不会触发一堆其他错误。如果是这种情况,您需要检查 libxml_get_errors()
是否存在未闭合的元素错误。
我打算将自定义数据属性添加到 div
和 ul
容器,如果该容器使用特定的 class。我从 HTML 片段开始,这些片段包含有效的 HTML(完整的 div
及其内容和结尾的 </div>
)或仅包含开头的 div
(没有其内容和结尾 </div>
).
以下是我可能开始的内容示例:
<div id='gallery-7222-1' class='gallery galleryid-7222 gallery-columns-3 gallery-size-thumbnail'>
这是我到目前为止尝试过的方法:
// grab all containers from the HTML.
$dom_doc = new DOMDocument();
/*
* $html here can be the example I posted above.
* LIBXML_HTML_NOIMPLIED and LIBXML_HTML_NODEFDTD are used
* to avoid adding a doctype and wrapping the whole output in HTML tags.
*/
$dom_doc->loadHTML( $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
// Let's look for lists and divs.
$ul_tags = $dom_doc->getElementsByTagName( 'ul' );
$div_tags = $dom_doc->getElementsByTagName( 'div' );
/*
* Loop through each ul, and add the data.
* I do more things in there, like checking for what class the ul has,
* but I've simplified things here.
*/
foreach ( $ul_tags as $ul_tag ) {
$ul_tag->setAttribute( 'data-foo', 'bar' );
}
/*
* Loop through each div, and add the data.
* I do more things in there, like checking for what class the div has,
* but I've simplified things here.
*/
foreach ( $div_tags as $div_tag ) {
$div_tag->setAttribute( 'data-foo', 'bar' );
}
// Save our updated HTML.
$html = $dom_doc->saveHTML();
返回的 HTML 包括新的数据属性,但也包括我在这里并不真正想要的结束 </div>
。你可以在这里看到它:
https://ideone.com/sVfAOn
我首先想到用 substr
删除结束 </div>
,但我做不到:
- 在某些情况下,我原来的 HTML 实际上包含一个我想保留的结束 div 标签。
- 有时我可能正在编辑包含 ul
的字符串。
我该如何阻止 saveHTML()
在这里变得那么聪明,并试图为我修复我的 HTML?
谢谢!
不,您无法说服 HTML 解析器不解析 HTML。最好的解决方案是首先重新考虑您获取数据的方法,并确保您不会获取碎片。
否则,您可以先尝试将其视为 XML,看看它是否损坏:
<?php
libxml_use_internal_errors(true);
$dom_doc = new DOMDocument();
$remove = "";
// try loading it as xml
if (!$dom_doc->loadXml($html)) {
// it failed, get the error message
$err = libxml_get_last_error()->message ?? "";
// is it because of an unclosed element?
// find out which element it's breaking on
if (preg_match("/end of data in tag (.*?) /", $err, $matches)) {
$remove = "</$matches[1]>";
}
}
$dom_doc->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
// do stuff
$html = str_replace($remove, "", $dom_doc->saveHTML());
当然,这假定您正在处理 HTML,否则它是干净的并且不会触发一堆其他错误。如果是这种情况,您需要检查 libxml_get_errors()
是否存在未闭合的元素错误。