Php 递归解析XML文档

Php 递归解析XML文档,php,xml,recursion,xml-parsing,domdocument,Php,Xml,Recursion,Xml Parsing,Domdocument,我有包含文章信息的XML文档,它们有一种层次结构: <?xml version="1.0" encoding="UTF-8"?> <page> <elements> <element> <type>article</type> <id>1</id> <parentContainerID>page</parentContainerID> <parentContainer

我有包含文章信息的XML文档,它们有一种层次结构:

<?xml version="1.0" encoding="UTF-8"?>

<page>
<elements>

<element>
<type>article</type>
<id>1</id>
<parentContainerID>page</parentContainerID>
<parentContainerType>page</parentContainerType>
</element>

<element>
<type>article</type>
<id>2</id>
<parentContainerID>1</parentContainerID>
<parentContainerType>article</parentContainerType>
</element>

<element>
<type>photo</type>
<id>3</id>
<parentContainerID>2</parentContainerID>
<parentContainerType>article</parentContainerType>
</element>

<... more elements ..>

</elements>
</page>

文章
1.
页
页
文章
2.
1.
文章
照片
3.
2.
文章

元素具有节点parentContainerID和节点parentContainerType。如果parentContainerType==page,则这是主元素。parentContainerID显示元素的主元素。因此它应该是这样的:1在XML中没有嵌套/递归。
节点是同级节点。为了构建父子关系,我建议在XML上循环并构建两个数组。一个用于关系,一个用于引用元素

$xml = file_get_contents('php://stdin');

$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);

$relations = [];
$elements = [];
foreach ($xpath->evaluate('//element') as $element) {
  $id = (int)$xpath->evaluate('string(id)', $element);
  $parentId = (int)$xpath->evaluate('string(parentContainerID)', $element);
  $relations[$parentId][] = $id;
  $elements[$id] = $element;
}

var_dump($relations);
输出:

array(3) {
  [0]=>
  array(1) {
    [0]=>
    int(1)
  }
  [1]=>
  array(1) {
    [0]=>
    int(2)
  }
  [2]=>
  array(1) {
    [0]=>
    int(3)
  }
}
1: article
 2: article
  3: photo
关系数组现在包含任何父元素的子ID,没有父元素的元素位于索引0中。这允许您使用递归函数以树的形式访问元素

function traverse(
  int $parentId, callable $callback, array $elements, array $relations, $level = -1
) {
  if ($elements[$parentId]) {
     $callback($elements[$parentId], $parentId, $level);
  }
  if (isset($relations[$parentId]) && is_array($relations[$parentId])) {
    foreach ($relations[$parentId] as $childId) {
      traverse($childId, $callback, $elements, $relations, ++$level);
    }
  }
}
这将对每个节点执行回调。正确的实现方法是使用
递归迭代器
,但函数应该用于示例

traverse(
  0,
  function(DOMNode $element, int $id, int $level) use ($xpath) {
    echo str_repeat(' ', $level);
    echo $id, ": ", $xpath->evaluate('string(type)', $element), "\n";
  },
  $elements,
  $relations
);
输出:

array(3) {
  [0]=>
  array(1) {
    [0]=>
    int(1)
  }
  [1]=>
  array(1) {
    [0]=>
    int(2)
  }
  [2]=>
  array(1) {
    [0]=>
    int(3)
  }
}
1: article
 2: article
  3: photo

请注意,
$xpath
对象作为回调的上下文提供。由于
$elements
数组包含原始节点,因此可以使用Xpath表达式从与当前元素节点相关的DOM中获取详细数据。

“现在我需要构建一个新的页面(html),该页面包含如下内容:ID 1的内容、ID 2的内容、ID 3的内容”-只是顺序的或嵌套的,根据XML定义的父/子关系?如果不是嵌套的,为什么要在这里使用递归?XML中的元素也不是嵌套的,它们都在同一级别上。我可能会先把这些东西读入数组,然后对它们进行相应的排序。。。然后根据父/子关系对其进行循环以生成所需的output.CBroe。