Как убрать в PHP внешние теги с узла - PullRequest
0 голосов
/ 04 марта 2019

У меня есть следующий 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]);

childs добавляются в тело, но в обратном порядке

Так я получаю

<body>
<footer>Footer</footer>
<section>Section</section>
<header>Header</header>
</body>

но мне нужно

<body>
<header>Header</header>
<section>Section</section>
<footer>Footer</footer>
</body>

Как решить проблему?

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Ваш исходный код очень близок, просто отсутствует одна ключевая точка.

Исходный код

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) выше, так как это более условно.

0 голосов
/ 04 марта 2019

Хорошо, я нашел свое собственное решение, но, возможно, кто-то опубликует более элегантный:

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