使用DOMDocument在div中的标头标记之间包装所有节点

时间:2019-12-08 10:13:43

标签: php dom

我有一组HTML文件要处理,其中相关部​​分未分组在容器中,并且实际上可以包含任何元素。当前,新部分仅由下一个标头标记划定:

<h1>Section 1</h1>
<p>Paragraph Text</p>
<p>Paragraph Text</p>

<h2>Section 2</h2>
<p>Paragraph Text</p>
<img src='an image' />
<p>Further Paragraph Text</p>

<h1>Section 3</h1>
<p>Paragraph Text</p>
<p>Paragraph Text</p>

如何使用DOM functions in PHP<div>的这些标头标记之间包装所有内容?即将上述HTML转换为:

<div>
    <h1>Section 1</h1>
    <p>Paragraph Text</p>
    <p>Paragraph Text</p>
</div>

<div>
    <h2>Section 2</h2>
    <p>Paragraph Text</p>
    <img src='an image' />
    <p>Further Paragraph Text</p>
</div>

<div>
    <h1>Section 3</h1>
    <p>Paragraph Text</p>
    <p>Paragraph Text</p>
</div>

到目前为止,我已经尝试过使用循环和XPATH选择器的各种组合来尝试收集标头之间的所有元素,以便可以像上面那样包装它们-但尚未成功完成。

1 个答案:

答案 0 :(得分:1)

我最初的想法是基于操纵输出缓冲区的,因为我未能正确阅读开头的段落,并且以下函数用作了ob_start的回调。

您可能会注意到在函数开始时使用$ tags,后面是相当复杂的$ query-$ tags稍后用于帮助填充找到的节点,并确保我们在找到下一个节点时停止$ tags数组-而不是编写模式并独立维护此数组,我认为这样会更灵活。

基本上,它的工作原理如下:从提供的$tags数组构造查询模式,然后使用该查询模式查询HTML DOM。如果存在匹配的节点,请遍历集合并将找到的节点(标头)添加到数组中。然后遍历找到的节点的同级并将它们添加到同一新数组中。在循环移动到集合中的下一个节点之前,请保存该数组,然后再重复此过程。处理完所有发现的节点后,就可以创建容器DIV元素,以确保再次填充所有子项。

<?php
    #https://stackoverflow.com/questions/59234379/using-domdocument-to-wrap-all-nodes-between-header-tags-in-div/59235431#59235431



    function wrapcallback( $buffer ){
        global $use_output_buffer;

        $delimiter='#';
        $tags=array('h1','h2','h3','h4','h5','h6');
        $query=implode('|', explode( $delimiter, sprintf( '//%s', implode( sprintf( '%s//', $delimiter ), $tags ) ) ) );
        $keepers=array();
        $parents=array();

        libxml_use_internal_errors( true );
        $dom=new DOMDocument;
        $dom->validateOnParse=false;
        $dom->recover=true;
        $dom->strictErrorChecking=false;
        $dom->preserveWhiteSpace=true;
        $dom->loadHTML( $buffer );
        $errors = libxml_get_errors();
        libxml_clear_errors();

        $xp=new DOMXPath( $dom );
        $col=$xp->query( $query );

        if( $col->length > 0 ){
            foreach( $col as $node ){

                $parents[]=$node->parentNode;
                $nodes=array( $node );

                while( $node = $node->nextSibling ){
                    if( in_array( $node->nodeName, $tags ) )break;
                    if( $node->nodeType==XML_ELEMENT_NODE  )$nodes[]=$node;
                }
                $keepers[]=$nodes;
            }
        }

        foreach( $keepers as $index => $obj ){
            $div=$dom->createElement('div');
            $parents[ $index ]->appendChild( $div );
            foreach( $obj as $child )$div->appendChild( $child );
        }

        $keepers = $parents = $xp = $div = null;
        echo $dom->saveHTML();
    };





    $html="
    <!DOCTYPE html>
    <html lang='en'>
        <head>
            <title>It's a Christmas Wrapper!</title>
            <style>
                body{
                    background:url( https://storage.needpix.com/rsynced_images/christmas-wallpaper-1480711266Vyi.jpg );
                    background-repeat:repeat;
                    color:white;
                }

            </style>
        </head>
        <body>

                <h1>Section 1</h1>
                <p>Paragraph Text</p>
                <p>Paragraph Text</p>

                <h2>Section 2</h2>
                <p>Paragraph Text</p>
                <img src='/images/laracroft.png' />
                <p>Further Paragraph Text</p>

                <h1>Section 3</h1>
                <p>Paragraph Text</p>
                <p>Paragraph Text</p>

        </body>
    </html>";

    wrapcallback( $html );

?>