如何从节点中删除PHP外部标签

时间:2019-03-04 19:29:08

标签: php domdocument

我有以下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>

如何解决问题?

2 个答案:

答案 0 :(得分:1)

您的原始代码非常接近,只缺少一个关键点。

原始代码

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 ($bodyDivs[0]->childNodes->length > 0) {
    $divChild = $bodyDivs[0]->childNodes->item(0);
    $dom->getElementsByTagName('body')->item(0)->appendChild($divChild);
}

可以用while循环替换。

->item(0)

在旁边:我使用上面的<int:logging-channel-adapter>表示法,因为这是比较传统的方法。

答案 1 :(得分:0)

好的,我已经找到了自己的解决方案,但也许有人会发表更优雅的话:

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)));