Say you have the following array:
$nodes = array( "parent node", "parent node", array( "child node", "child node", array( "grand child node", "grand child node")));
How would you go about transforming it to an XML string so that it looks like:
parent nodeparent nodechild nodechild nodegrand child nodegrand child node
One way to do it would be through a recursive method like:
function traverse($nodes) { echo ""; foreach($nodes as $node) { if(is_array($node)) { traverse($node); } else { echo "$node"; } } echo ""; } traverse($nodes);
I'm looking for an approach that uses iteration, though.
'; $arr = $nodes; while(count($arr) > 0) { $n = array_shift($arr); if(is_array($n)) { array_unshift($arr, null); $arr = array_merge($n, $arr); $s .= ''; } elseif(is_null($n)) $s .= ''; else $s .= ''.$n.''; } $s .= ''; echo $s; ?>
You could use an Iterator to iterate over the array and then produce your desired output:
class TranformArrayIterator extends RecursiveIteratorIterator { protected function indent() { echo str_repeat("\t", $this->getDepth()); return $this; } public function beginIteration() { echo '', PHP_EOL; } public function endIteration() { echo '', PHP_EOL; } public function beginChildren() { $this->indent()->beginIteration(); } public function endChildren() { $this->indent()->endIteration(); } public function current() { return sprintf('%s%s%s', str_repeat("\t", $this->getDepth() +1), parent::current(), PHP_EOL); } }
and then assemble it like this:
$iterator = new TranformArrayIterator(new RecursiveArrayIterator($nodes)); foreach($iterator as $val) { echo $val; }
outputs
parent nodeparent nodechild nodechild nodegrand child nodegrand child node
To blank out $key
when using $key => $val
, add this to TraverseArrayIterator
public function key() { return ''; }
Since your aim seems to be to produce XML, you could also pass an XMLWriter as a collaborator to the Iterator. This allows for more control over the generated XML and also makes sure the output is valid 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()); } }
You'd then use it like this:
$xmlWriter = new XmlWriter; $xmlWriter->openUri('php://output'); $xmlWriter->setIndent(true); $xmlWriter->setIndentString("\t"); $iterator = new TranformArrayIterator( $xmlWriter, new RecursiveArrayIterator($nodes) );
and foreach
'ing over it will produce the same output then (but adding the XML prolog)