Сохранить несколько HTML тел как одно, используя DOMDocument - PullRequest
1 голос
/ 11 февраля 2020

У меня есть строка, содержащая несколько <html><body><div>Content</div></body></html> тегов. Я хочу получить все содержимое и объединить их в одну действительную структуру. Например:

<html><body><div>Content</div></body></html>
<html><body><div>Content</div></body></html>
<html><body><div>Content</div></body></html>

Должно быть:

<html>
    <body>
        <div>Content</div>
        <div>Content</div>
        <div>Content</div>
    </body>
</html>

Мой текущий код выглядит следующим образом:

    libxml_use_internal_errors(true);
    $newDom = new DOMDocument();

    $newBody = "";

    $newDom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));

    $bodyTags = $newDom->getElementsByTagName("body");

    foreach($bodyTags as $body) {
        $newBody .= $newDom->saveHTML($body);
    }

$newBody теперь содержит все теги тела:

<body><div>Content</div></body>
<body><div>Content</div></body>
<body><div>Content</div></body>

Как сохранить только HTML содержимое каждого тега тела в $newBody?

Редактировать:

На основе @ Ответ НайджелРена: Это мое решение:

    libxml_use_internal_errors(true);
    $newDom = new DOMDocument();

    $newBody = '';
    $newDom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));

    $bodyTags = $newDom->getElementsByTagName("body");

    foreach($bodyTags as $body) {
        foreach ($body->childNodes as $node)   {
            $newBody .= $newDom->saveHTML($node);
        }
    }

    $newDom = new DOMDocument();
    $newDom->loadHTML(mb_convert_encoding($newBody, 'HTML-ENTITIES', 'UTF-8'));
    $newBody = $newDom->saveHTML();

Ответы [ 3 ]

1 голос
/ 11 февраля 2020

Это неудобно, так как при использовании loadHTML() он попытается исправить HTML в исходном документе. Это создает структуру, отличную от той, которая может показаться вам.

НО, если у вас есть базовый c контур документа, следующее скопирует содержимое тегов <body> в новый документ (комментарии в коде) ...

$html = '<html><body><div>Content1</div></body></html>
<html><body><div>Content2</div></body></html>
<html><body><div>Content3</div></body></html>';

libxml_use_internal_errors(true);
$newDom = new DOMDocument();

// New document with final code
$newBody = new DOMDocument();

$newDom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));

// Set up basic template for new doucument
$newBody->loadHTML("<html><body /></html>");
// Find where to add any new content
$addBody = $newBody->getElementsByTagName("body")[0];
// Find the existing content to add
$bodyTags = $newDom->getElementsByTagName("body");
foreach($bodyTags as $body) {
    // Add all of the contents of the <body> tag into the new document
    foreach ( $body->childNodes as $node )   {
        // Import the node to copy to the new document and add it in
        $addBody->appendChild($newBody->importNode($node, true));
    }
}
echo $newBody->saveHTML();

, который дает ...

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><div>Content1</div><div>Content2</div><div>Content3</div></body></html>

Ограничения заключаются в том, что любой контент вне тегов <body> и любых атрибутов тег <body> не сохраняется.

1 голос
/ 11 февраля 2020

Идея о том, что вы хотите загрузить несколько html документов в одно дерево DOM, обязательно означает, что вы неправильно сформировали X / HTML. Работать с этим будет непросто, потому что парсер DOM сделает некоторые предположения о том, что вы имели в виду, что не обязательно будет интуитивно понятным. HTML - это неразборчивый язык, поэтому для этого потребуется некоторое маневрирование.

Вот суть этого, хотя. Вы берете каждый элемент body, рекурсивно просматриваете его список узлов и воссоздаете каждый элемент в новый документ.

Вот как я бы это сделал:

class DOMExtended extends DOMDocument {
    public function walk(DOMNode $node, $skipParent = false) {
        if (!$skipParent) {
            yield $node;
        }
        if ($node->hasChildNodes()) {
            foreach ($node->childNodes as $n) {
                yield from $this->walk($n);
            }
        }
    }
}

$html = <<<'HTML'
    <html><body><div>Content 1</div></body></html>
    <html><body><div>Content 2</div></body></html>
    <html><body><div>Content 3</div></body></html>
HTML;

libxml_use_internal_errors(true);

// We'll load the html with multiple body tags here
$oldDom = new DOMExtended;

// We'll recreate the new html here
$newDom = new DOMExtended;
$main = $newDom->childNodes->item(1);
$htmlNode = new DOMElement('html');
$newDom->appendChild($htmlNode);


$oldDom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

// extract all the body tags from the old dom
$bodyTags = $oldDom->getElementsByTagName('body');

foreach ($bodyTags as $bodyTag) {
    foreach ($oldDom->walk($bodyTag, true) as $childNode) {
        // recreate the child nodes in the newDom
        $name = $childNode->nodeName;
        if ($name === '#text') { // prevent textnodes
            continue;
        }
        $content = $childNode->nodeValue;
        $newNode = new DOMElement($name, $content);
        // append that node into the newDom
        $htmlNode->appendChild($newNode);
    }
}

// Here's the result
echo $newDom->saveHTML();

Конечный результат:

<html>
    <div>Content 1</div>
    <div>Content 2</div>
    <div>Content 3</div>
</html>

Для рекурсивного обхода части дерева я добавил небольшого помощника с DOMExtended, который просто выполняет рекурсивный обход дерева через генератор.

0 голосов
/ 11 февраля 2020

Вы можете сделать это, поместив html коды внутри php кодов. Вы можете написать свой код так:

<?php
    echo '<html><body><div>Content</div></body></html>';
    *PHP code to be executed...*
?>
...