Многомерная итерация массива

Скажем, у вас есть следующий массив:

$nodes = array( "parent node", "parent node", array( "child node", "child node", array( "grand child node", "grand child node"))); 

Как бы вы решили преобразовать его в строку XML, чтобы он выглядел так:

 <node> <node>parent node</node> <node>parent node</node> <node> <node>child node</node> <node>child node</node> <node> <node>grand child node</node> <node>grand child node</node> </node> </node> </node> 

Один из способов сделать это – рекурсивный метод, например:

 function traverse($nodes) { echo "<node>"; foreach($nodes as $node) { if(is_array($node)) { traverse($node); } else { echo "<node>$node</node>"; } } echo "</node>"; } traverse($nodes); 

Тем не менее, я ищу подход, который использует итерацию.

 <?php $nodes = array( "parent node", "parent node", array( "child node", "child node", array( "grand child node", "grand child node" ) ) ); $s = '<node>'; $arr = $nodes; while(count($arr) > 0) { $n = array_shift($arr); if(is_array($n)) { array_unshift($arr, null); $arr = array_merge($n, $arr); $s .= '<node>'; } elseif(is_null($n)) $s .= '</node>'; else $s .= '<node>'.$n.'</node>'; } $s .= '</node>'; echo $s; ?> 

Вы можете использовать Iterator для итерации по массиву, а затем для получения нужного результата:

 class TranformArrayIterator extends RecursiveIteratorIterator { protected function indent() { echo str_repeat("\t", $this->getDepth()); return $this; } public function beginIteration() { echo '<nodes>', PHP_EOL; } public function endIteration() { echo '</nodes>', PHP_EOL; } public function beginChildren() { $this->indent()->beginIteration(); } public function endChildren() { $this->indent()->endIteration(); } public function current() { return sprintf('%s<node>%s</node>%s', str_repeat("\t", $this->getDepth() +1), parent::current(), PHP_EOL); } } 

а затем соберите его вот так:

 $iterator = new TranformArrayIterator(new RecursiveArrayIterator($nodes)); foreach($iterator as $val) { echo $val; } 

выходы

 <nodes> <node>parent node</node> <node>parent node</node> <nodes> <node>child node</node> <node>child node</node> <nodes> <node>grand child node</node> <node>grand child node</node> </nodes> </nodes> </nodes> 

Чтобы исключить $key при использовании $key => $val , добавьте это в TraverseArrayIterator

 public function key() { return ''; } 

Поскольку ваша цель состоит в создании XML, вы также можете передать XMLWriter в качестве соавтора в Iterator. Это позволяет больше контролировать сгенерированный XML, а также гарантирует корректность вывода XML:

 class TranformArrayIterator extends RecursiveIteratorIterator { private $xmlWriter; public function __construct( XmlWriter $xmlWriter, Traversable $iterator, $mode = RecursiveIteratorIterator::LEAVES_ONLY , $flags = 0) { $this->xmlWriter = $xmlWriter; parent::__construct($iterator, $mode, $flags); } public function beginIteration() { $this->xmlWriter->startDocument('1.0', 'utf-8'); $this->beginChildren(); } public function endIteration() { $this->xmlWriter->endDocument(); } public function beginChildren() { $this->xmlWriter->startElement('nodes'); } public function endChildren() { $this->xmlWriter->endElement(); } public function current() { $this->xmlWriter->writeElement('node', parent::current()); } } 

Затем вы будете использовать его следующим образом:

 $xmlWriter = new XmlWriter; $xmlWriter->openUri('php://output'); $xmlWriter->setIndent(true); $xmlWriter->setIndentString("\t"); $iterator = new TranformArrayIterator( $xmlWriter, new RecursiveArrayIterator($nodes) ); 

и foreach 'ing над ним выдаст тот же результат (но добавив пролог XML)