PHP DOMXPath problem


PHP DOMXPath problem

我试图用html标记解析文本块,但遇到了一些问题。

<?php
    libxml_use_internal_errors(true);
    $html = '
<html>
<body>
    <div>
        Message <b>bold</b>, <s>strike</s>
    </div>
    <div>
        <span class="how">
            <a href="link" title="text">Link</a>, <b> BOLD </b>
        </span>
    </div>
</body>
</html>
    ';
    $dom = new DOMDocument();
    $dom->preserveWhiteSpace = false;
    $dom->strictErrorChecking = false;
    $dom->recover = true;
    $dom->loadHTML($html);        
    function getMessages($element, $xpath)
    {
        $messages = array();
        $children = $element->childNodes;        
        foreach ($children as $child) 
        { 
            if(strtolower($child->nodeName) == 'div')
            {
                // my functions
            }
            else
            if ($child->nodeType == XML_TEXT_NODE)
            {
                $text = trim(DOMinnerHTML($element));
                if($text)
                {
                    $messages[] = array('type' => 'text', 'text' => $text);
                }
            }
        }
        return $messages;
    }
    function DOMinnerHTML($element) 
    {
        $innerHTML = null; 
        $children = $element->childNodes;
        foreach ($children as $child) 
        {
            $tmp_dom = new DOMDocument(); 
            $tmp_dom->appendChild($tmp_dom->importNode($child, true)); 
            $innerHTML .= trim($tmp_dom->saveHTML()); 
        } 
        return $innerHTML; 
    } 
    $xpath = new DOMXPath($dom);
    $messagesXpath = $xpath->query("//div");
    $messages = array();
    $i = 0;
    foreach($messagesXpath as $message)
    {
        $messages[] = getMessages($message, $xpath);
        $i++;
        if ($i == 2)
        break;
    }
    var_dump($messages);  

此代码返回以下数组:

array(2) {
  [0]=>
  array(3) {
    [0]=>
    array(2) {
      ["type"]=>
      string(4) "text"
      ["text"]=>
      string(32) "Message<b>bold</b>,<s>strike</s>"
    }
    [1]=>
    array(2) {
      ["type"]=>
      string(4) "text"
      ["text"]=>
      string(32) "Message<b>bold</b>,<s>strike</s>"
    }
    [2]=>
    array(2) {
      ["type"]=>
      string(4) "text"
      ["text"]=>
      string(32) "Message<b>bold</b>,<s>strike</s>"
    }
  }
  [1]=>
  array(2) {
    [0]=>
    array(2) {
      ["type"]=>
      string(4) "text"
      ["text"]=>
      string(100) "<span class="how">
            <a href="link" title="text">Link</a>, <b> BOLD </b>
        </span>"
    }
    [1]=>
    array(2) {
      ["type"]=>
      string(4) "text"
      ["text"]=>
      string(100) "<span class="how">
            <a href="link" title="text">Link</a>, <b> BOLD </b>
        </span>"
    }
  }
}

我想让$messages['text']带有html标记(没关系),但由于某种原因,数组重复了!!!!

我认为这是这个区块的问题

if ($child->nodeType == XML_TEXT_NODE)
{
    $text = trim(DOMinnerHTML($element));
    if($text)
    {
          $messages[] = array('type' => 'text', 'text' => $text);
    }
}

我认为您误解了哪些元素是存在迭代的,因为您选择了所有的<div>,然后将每个元素传递给getMessages。但是,在getMessages中,然后在每个<div>XML_TEXT_NODE子节点上迭代,这就是重复的来源。

让我们看看HTML:

<div>
    Message <b>bold</b>, <s>strike</s>
</div>

DOM元素和文本节点在逻辑上不同,并且具有不同的类型——XML_ELEMENT_NODE和XML_text_NODE(完整列表请参见此处),因此<div>实际上包含5个子级(text、ELEMENT、text、ELEMENT、text)。您正确地识别了有问题的if条件,但是简单地将类型更改为*XML_ELEMENT_NODE*并不能完全解决问题。仍然存在多个子节点,其中每个<div>的类型为XML_ELEMENT_NODE。

为了完全解决这个问题,我更改了传递给getMessages函数的元素,这样函数就可以在正确的级别上迭代并消除重复。我还删除了一些复杂性,通过重命名一些变量来提高可读性。

这是我的完整解决方案:

<?php
    libxml_use_internal_errors(true);
    $html = <<<HTML
<html>
<body>
    <div>
        Message <b>bold</b>, <s>strike</s>
    </div>
    <div>
        <span class="how">
            <a href="link" title="text">Link</a>, <b> BOLD </b>
        </span>
    </div>
</body>
</html>
HTML;
    $dom = new DOMDocument();
    $dom->preserveWhiteSpace = false;
    $dom->strictErrorChecking = false;
    $dom->recover = true;
    $dom->loadHTML($html);
    function getMessages($allDivs) {
        $messages = array();
        foreach ($allDivs as $div)  {
            if ($div->nodeType == XML_ELEMENT_NODE) {
                $messages[] = trim(DOMinnerHTML($div));
            }
        }
        return $messages;
    }
    function DOMinnerHTML($element) {
        $innerHTML = null;
        $children = $element->childNodes;
        foreach ($children as $child) {
            $tmp_dom = new DOMDocument();
            $tmp_dom->appendChild($tmp_dom->importNode($child, true));
            $innerHTML .= trim($tmp_dom->saveHTML());
        }
        return $innerHTML;
    }
    $xpath = new DOMXPath($dom);
    $messagesXpath = $xpath->query("//div");
    $messages[] = getMessages($messagesXpath);
    print_r($messages);
?>