PHP深入挖掘DOMNodes

时间:2016-01-11 21:56:56

标签: php arrays sorting

我试图通过nodePath对php DOMDocument元素进行排序,以便它们最先嵌入最深层次,然后按顺序存在于同一父文档中的文档中。

例如,在以下HTML中,order属性是顺序 我想要对元素进行排序。

<div order='5'>
   <div order='3'>
      <div order='1'></div>
      <div order='2'></div>
   </div>
   <div order='4'></div>
</div>

我非常接近,但并非一直都在那里。

//THIS gives me order 2,1,4,3,5 which is deepest nested but not linear
 within the same parent

function dorder($a,$b){
    $bp=count(explode("/",$b->getNodePath()) );
    $ap=count( explode("/",$a->getNodePath()) );
        if($ap==$bp){
            return 0;
        }  
        else if($ap<$bp){
            return 1;
          }

         else{
            return -1;
         }  
  }

有时排序会在一个父级中以正确的顺序获得一些元素, 但在另一位父母身上错了......

//THIS gives me 1,2,4,3,5 which is right except for 4 and 3 are swapped.
function dorder($a,$b){

$bp=count(explode("/",$b->getNodePath()) );
$ap=count( explode("/",$a->getNodePath()) );
        if($ap==$bp){
            return 1;
        }  
        else if($ap<$bp){
            return 1;
         }

         else{
            return -1;
         }
}

注意 在实际的XML中没有&#39; order&#39;属性来工作。它仅用于测试目的。

2 个答案:

答案 0 :(得分:1)

这是你可以去的方式。而不是usort,你可以直接深入dom堆栈。您将按顺序可靠地命中元素:

$dom = <<<DOM
<div order='5'>
  <div order='3'>
    <div order='1'></div>
    <div order='2'></div>
  </div>
  <div order='4'></div>
</div>
DOM;

echo "<pre>" . htmlentities($dom) . "</pre>";

$domDocument = new DOMDocument;
$domDocument->loadXML($dom);
$domXPath = new DOMXPath($domDocument);
$domNodes = $domXPath->query('//div');
$nodesArray = array();
orderNodes($domNodes, $nodesArray);

foreach ($nodesArray as $n) {
  echo "order: $n<br>";
}

function orderNodes($domNodes, &$nodesArray) {
  foreach ($domNodes as $divDOM) {
    if ($divDOM->hasChildNodes()) {
      orderNodes($divDOM->childNodes, $nodesArray);
    } 
    if ($divDOM instanceof DOMElement && ! isset($divDOM->visited)) {
      $nodesArray[] = $divDOM->getAttribute('order');
      $divDOM->visited = true;
    }
  }
}

使用递归深入了解DOMNodes,深度优先。我在那里塞了一个黑客,设置了一个任意属性:$divDOM->visited = true;。这有点像过滤器,防止在递归退出时写入重复元素。

答案 1 :(得分:1)

@eggmatters说服我放弃对此的回应,并摒弃返回节点的自然顺序。我更改了函数,因此我没有为每个节点添加'visited'属性,我可以使用它来比较文档中的任何两个或更多节点,而无需对整个文档进行排序。

function depthSort($nodesArr){
    $sorted=array();
    foreach($nodesArr as $node){
       $i=0;
         $adepth=count(explode("/",$node->getNodePath() ) );
         while($i<count($sorted)){
           $bdepth=count(explode("/",$sorted[$i]->getNodePath()));
           if($adepth>$bdepth){
            array_splice($sorted,$i,0,array($node));
            break;
        }
        $i++;
    }   
    //order must go on the end cause it didn't go anywhere else
    if($i==count($sorted)){
        array_push($sorted, $node); 
     }                                               

}
return $sorted;
}