phpRecently I start working on an export module to invoice applications. One of them uses a very simple xml structure (simple nodes without attributes etc.) and therefore I wanted to create this xml also in a very simple way: from an array.

I decided the use DOM, and not e.g. SimpleXML, because I need to validate my xml with a schema.

After a quick search on the internet I found several snippets that could do the trick until I had to set the same element tag name on the same level:

<nodes>
	<node>text</node>
	<node>text</node>
</nodes>

The snippets I found use the index of an array to create the name of the element and the value for the text part. Only everyone knows that you cannot set 2 keys with the same name in an array.

So what could we do to improve this ?

I changed a snippet so it can generate the example above. The array should now look like this:

array (
	“nodes” => array (
		“node” => array (
			0 => “text”
			1 => “text”
		)
	)
)

Since you cannot set integers as element tags, it checks if the index is an integer and if it is it loops the array to recreate the same element tag name with the new value.

I added the function in a class which extends the DOMDocument class (XmlDomConstruct):

<?php
/**
 * Extends the DOMDocument to implement personal (utility) methods.
 *
 * @author Toni Van de Voorde
 */
class XmlDomConstruct extends DOMDocument {

	/**
	 * Constructs elements and texts from an array or string.
	 * The array can contain an element's name in the index part
	 * and an element's text in the value part.
	 * 
	 * It can also creates an xml with the same element tagName on the same 
	 * level.
	 * 
	 * ex:
	 * <nodes>
	 *   <node>text</node>
	 *   <node>
	 *     <field>hello</field>
	 *     <field>world</field>
	 *   </node>
	 * </nodes>
	 * 
	 * Array should then look like:
	 * 
	 * Array (
	 *   "nodes" => Array (
	 *     "node" => Array (
	 *       0 => "text"
	 *       1 => Array (
	 *         "field" => Array (
	 *           0 => "hello"
	 *           1 => "world"
	 *         )
	 *       )
	 *     )
	 *   )
	 * )
	 *
	 * @param mixed $mixed An array or string.
	 * 
	 * @param DOMElement[optional] $domElement Then element
	 * from where the array will be construct to.
	 * 
	 */
	public function fromMixed($mixed, DOMElement $domElement = null) {

		$domElement = is_null($domElement) ? $this : $domElement;

		if (is_array($mixed)) {
			foreach( $mixed as $index => $mixedElement ) {

				if ( is_int($index) ) {
					if ( $index == 0 ) {
						$node = $domElement;
					} else {
						$node = $this->createElement($domElement->tagName);
						$domElement->parentNode->appendChild($node);
					}
				} 
				
				else {
					$node = $this->createElement($index);
					$domElement->appendChild($node);
				}
				
				$this->fromMixed($mixedElement, $node);
				
			}
		} else {
			$domElement->appendChild($this->createTextNode($mixed));
		}
		
	}
	
}
?>

And here is a very simple example on how you can use this class:

$array = array(
  "nodes" => array(
    "node" => array(
      0 => "text",
      1 => "text"
)));

$this->dom = new XmlDomConstruct('1.0', 'utf-8');
$this->dom->fromMixed($array);

echo $this->dom->saveXML();

Isn’t that wonderful? You don’t even have to know how to construct xmls with the DOMDocument object. Of course this will only work for simple xmls.

Maybe, if I have the time, I’ll try to add the possibility to insert attributes to elements.