如何从节点中删除 PHP 外部标签
How to remove in PHP outer tags from a node
我有以下 html 代码:
$pageHTML = '<html>
<head></head>
<body>
<div class="some class">
<header>Header</header>
<section>Section</section>
<footer>Footer</footer>
</div>
</body>
</html>';
我需要删除 <div>
的外部标签,将其所有内部 HTML 保留在 <body>
内
如果我尝试
$dom = new DOMDocument;
libxml_use_internal_errors(true);
$dom->loadHTML($pageHTML);
libxml_use_internal_errors(false);
$bodyDivs = [];
foreach($dom->getElementsByTagName('body')[0]->childNodes as $bodyChild) {
if($bodyChild->nodeName == 'div') {
$bodyDivs[] = $bodyChild;
}
}
if(count($bodyDivs) == 1) {
foreach($bodyDivs[0]->childNodes as $divChild) {
$dom->getElementsByTagName('body')[0]->appendChild($divChild);
}
$dom->getElementsByTagName('body')[0]->removeChild($bodyDivs[0]);
}
正在删除 div,但在删除
之前没有将其子项附加到 <body>
如果我尝试像
这样的反向循环
$k = count($bodyDivs[0]->childNodes);
for($n = $k-1; $n >= 0; $n--) {
$dom->getElementsByTagName('body')[0]->appendChild($bodyDivs[0]->childNodes[$n]);
}
$dom->getElementsByTagName('body')[0]->removeChild($bodyDivs[0]);
正在将孩子添加到正文中,但顺序相反
所以我得到
<body>
<footer>Footer</footer>
<section>Section</section>
<header>Header</header>
</body>
但我需要
<body>
<header>Header</header>
<section>Section</section>
<footer>Footer</footer>
</body>
如何解决问题?
好的,我找到了自己的解决方案,但也许有人会 post 更优雅:
if(count($bodyDivs) == 1) {
$count = count($bodyDivs[0]->childNodes);
$arr = [];
for($n = $count-1; $n >= 0; $n--) {
$arr[] = $bodyDivs[0]->childNodes[$n];
}
for($n = $count-1; $n >= 0; $n--) {
$dom->getElementsByTagName('body')[0]->appendChild($arr[$n]);
}
$dom->getElementsByTagName('body')[0]->removeChild($bodyDivs[0]);
}
echo str_replace("\n\r", "", $dom->saveHTML((new \DOMXPath($dom))->query('/')->item(0)));
你的原代码很接近,只是少了一个关键点。
原码
foreach($bodyDivs[0]->childNodes as $divChild) {
$dom->getElementsByTagName('body')[0]->appendChild($divChild);
}
尝试 foreach
一个节点列表,同时从同一个列表中删除节点(在您的情况下,将它们移动到 <body>
),并不像您预期的那样运行。
用于演示目的的简化、完整示例:
<?php
$doc = new DOMDocument;
$doc->loadXML('<example><a/><b/><c/><d/><e/></example>');
$parent = $doc->documentElement;
foreach ($parent->childNodes as $child) {
$parent->removeChild($child);
}
echo $doc->saveXML();
输出如下:
<?xml version="1.0"?>
<example><b/><c/><d/><e/></example>
完全明智,对吧?!别怕,我们可以做得更好。
怎么办?
一种确实按预期运行的常见方法是遍历列表,直到它为空。
<?php
$doc = new DOMDocument;
$doc->loadXML('<example><a/><b/><c/><d/><e/></example>');
$parent = $doc->documentElement;
while ($parent->childNodes->length > 0) {
$child = $parent->childNodes->item(0);
$parent->removeChild($child);
}
echo $doc->saveXML();
应用于您的代码
以上都表示你原来的foreach
:
foreach($bodyDivs[0]->childNodes as $divChild) {
$dom->getElementsByTagName('body')[0]->appendChild($divChild);
}
可以用while循环代替。
while ($bodyDivs[0]->childNodes->length > 0) {
$divChild = $bodyDivs[0]->childNodes->item(0);
$dom->getElementsByTagName('body')->item(0)->appendChild($divChild);
}
旁白:我在上面使用了 ->item(0)
符号,因为它更传统。
我有以下 html 代码:
$pageHTML = '<html>
<head></head>
<body>
<div class="some class">
<header>Header</header>
<section>Section</section>
<footer>Footer</footer>
</div>
</body>
</html>';
我需要删除 <div>
的外部标签,将其所有内部 HTML 保留在 <body>
如果我尝试
$dom = new DOMDocument;
libxml_use_internal_errors(true);
$dom->loadHTML($pageHTML);
libxml_use_internal_errors(false);
$bodyDivs = [];
foreach($dom->getElementsByTagName('body')[0]->childNodes as $bodyChild) {
if($bodyChild->nodeName == 'div') {
$bodyDivs[] = $bodyChild;
}
}
if(count($bodyDivs) == 1) {
foreach($bodyDivs[0]->childNodes as $divChild) {
$dom->getElementsByTagName('body')[0]->appendChild($divChild);
}
$dom->getElementsByTagName('body')[0]->removeChild($bodyDivs[0]);
}
正在删除 div,但在删除
之前没有将其子项附加到<body>
如果我尝试像
这样的反向循环$k = count($bodyDivs[0]->childNodes);
for($n = $k-1; $n >= 0; $n--) {
$dom->getElementsByTagName('body')[0]->appendChild($bodyDivs[0]->childNodes[$n]);
}
$dom->getElementsByTagName('body')[0]->removeChild($bodyDivs[0]);
正在将孩子添加到正文中,但顺序相反
所以我得到
<body>
<footer>Footer</footer>
<section>Section</section>
<header>Header</header>
</body>
但我需要
<body>
<header>Header</header>
<section>Section</section>
<footer>Footer</footer>
</body>
如何解决问题?
好的,我找到了自己的解决方案,但也许有人会 post 更优雅:
if(count($bodyDivs) == 1) {
$count = count($bodyDivs[0]->childNodes);
$arr = [];
for($n = $count-1; $n >= 0; $n--) {
$arr[] = $bodyDivs[0]->childNodes[$n];
}
for($n = $count-1; $n >= 0; $n--) {
$dom->getElementsByTagName('body')[0]->appendChild($arr[$n]);
}
$dom->getElementsByTagName('body')[0]->removeChild($bodyDivs[0]);
}
echo str_replace("\n\r", "", $dom->saveHTML((new \DOMXPath($dom))->query('/')->item(0)));
你的原代码很接近,只是少了一个关键点。
原码
foreach($bodyDivs[0]->childNodes as $divChild) {
$dom->getElementsByTagName('body')[0]->appendChild($divChild);
}
尝试 foreach
一个节点列表,同时从同一个列表中删除节点(在您的情况下,将它们移动到 <body>
),并不像您预期的那样运行。
用于演示目的的简化、完整示例:
<?php
$doc = new DOMDocument;
$doc->loadXML('<example><a/><b/><c/><d/><e/></example>');
$parent = $doc->documentElement;
foreach ($parent->childNodes as $child) {
$parent->removeChild($child);
}
echo $doc->saveXML();
输出如下:
<?xml version="1.0"?>
<example><b/><c/><d/><e/></example>
完全明智,对吧?!别怕,我们可以做得更好。
怎么办?
一种确实按预期运行的常见方法是遍历列表,直到它为空。
<?php
$doc = new DOMDocument;
$doc->loadXML('<example><a/><b/><c/><d/><e/></example>');
$parent = $doc->documentElement;
while ($parent->childNodes->length > 0) {
$child = $parent->childNodes->item(0);
$parent->removeChild($child);
}
echo $doc->saveXML();
应用于您的代码
以上都表示你原来的foreach
:
foreach($bodyDivs[0]->childNodes as $divChild) {
$dom->getElementsByTagName('body')[0]->appendChild($divChild);
}
可以用while循环代替。
while ($bodyDivs[0]->childNodes->length > 0) {
$divChild = $bodyDivs[0]->childNodes->item(0);
$dom->getElementsByTagName('body')->item(0)->appendChild($divChild);
}
旁白:我在上面使用了 ->item(0)
符号,因为它更传统。