<?php
 
  /**------------------------------------------------------------------------------
 
   * File:         extendeddom.phplibrary
 
   * Description:  True DOMDocument and DOMElement abstraction
 
   * Version:      1.0
 
   * Author:       Richard Keizer
 
   * Email:        ra dot keizer at gmail dot com
 
   *------------------------------------------------------------------------------
 
   * COPYRIGHT (c) 2011 Richard Keizer
 
   *
 
   * The source code included in this package is free software; you can
 
   * redistribute it and/or modify it under the terms of the GNU General Public
 
   * License as published by the Free Software Foundation. This license can be
 
   * read at:
 
   *
 
   * http://www.opensource.org/licenses/gpl-license.php
 
   *
 
   * This program is distributed in the hope that it will be useful, but WITHOUT 
 
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 
   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 
 
   *------------------------------------------------------------------------------
 
   *
 
   *
 
   */
 
 
  class extDOMDocument extends DOMDocument {
 
    
 
    public function __construct($version='1.0', $encoding=null) {
 
      parent::__construct($version, $encoding);
 
      $this->formatOutput = true;
 
    }
 
    
 
    public function createElement($classname, $name=null, $owner=null, $value=null) {
 
      if (is_null($owner)) $owner = $this;
 
      
 
      return (!is_null($name) && $this->getElementByName($name, $owner)) ? $this->getElementByName($name, $owner) : new $classname($owner, $name, $value);
 
    }
 
    
 
    public function createElementNS($namespaceURI, $classname, DOMNode $owner, $value=null) {
 
      if (is_null($owner)) $owner = $this;
 
      return $this->getElementByName($name, $owner) ? $this->getElementByName($name, $owner) : new $classname($owner, $name, $value, $namespaceURI);      
 
    }
 
    
 
    public function getElementByName($name, $node=null) {
 
      if (is_null($node)) $node = $this;
 
      foreach($node->childNodes as $child) if ($child->hasAttribute('name') && $child->getAttribute('name') == $name) return $child;
 
    }
 
    
 
    
 
    public function marshal() {
 
      return $this->saveXML();
 
    }
 
    
 
    public function unmarshal($xml) {
 
      return $this->loadXML($xml);
 
    }
 
    
 
    public function loadXML($xml) {
 
      $tmpdom = new DOMDocument('1.0');
 
      $tmpdom->preserveWhiteSpace = false;
 
      $tmpdom->loadXML($xml);
 
      
 
      $stack = array($tmpdom, $this);                 //push source (=tmpdom's root) and target (=insertionpoint)
 
      while (!empty($stack)) {
 
        
 
        $source = array_shift($stack);
 
        $target = array_shift($stack);
 
        
 
        foreach($source->childNodes as $child) {
 
          
 
          switch ($child->nodeType) {
 
            case XML_ELEMENT_NODE: {
 
              
 
              $new = $child->namespaceURI == '' ? $this->createElement($child->nodeName, $child->getAttribute('name'), $target) : $this->createElementNS($uri, $child->nodeName, $child->getAttribute('name'), $target);
 
              
 
              foreach ($child->attributes as $attr) $new->setAttribute($attr->nodeName, $attr->nodeValue);        //$new->appendChild($this->importNode($attr, true));
 
              
 
              array_push($stack, $child);
 
              array_push($stack, $new);
 
              break;
 
            }
 
              
 
            case XML_TEXT_NODE: {
 
              $target->appendChild($this->createTextNode($child->nodeValue));   //$target->nodeValue = $child->nodeValue;
 
              break;
 
            }
 
              
 
            case XML_PI_NODE: {
 
              $target->appendChild($this->createProcessingInstruction($child->nodeName, $child->nodeValue));
 
              break;
 
            }
 
              
 
            case XML_COMMENT_NODE: {
 
              //$target->appendChild($this->createComment($child->nodeValue));
 
              break;              
 
            }
 
              
 
            default: {
 
              throw new Exception("nodeType '{$child->nodeType}' not implemented!");
 
            }
 
          }
 
        }
 
      }
 
    }          
 
  }
 
 
  
 
  
 
  //id is global unique
 
  //name is local unique
 
 
 
  class extDOMElement extends DOMElement {
 
    private $this;
 
    
 
    public function __construct($owner, $name=null, $value=null, $namespaceURI=null) {
 
      parent::__construct(get_called_class(), $value, $namespaceURI);
 
      $owner->appendChild($this);                    //append to owner
 
      $this->setAttribute('id', uniqid('u'));        //assign unique identifier
 
      $this->setIdAttribute('id', true);
 
      if ($name) $this->setAttribute('name', $name);
 
      $this->this = $this;                          //IMPORTANT: if we don't store this extra reference the element would degrade to a DOMElement for some reason...
 
    }
 
    
 
    public function createElement($classname, $name=null, $value=null) {
 
      return $this->ownerDocument->createElement($classname, $name, $this, $value);
 
    }
 
    
 
    public function getId() {
 
      return $this->getAttribute('id');
 
    }
 
    
 
    public function getElementByName($name) {
 
      return $this->ownerDocument->getElementByName($name, $this);
 
    }
 
  }
 
  
 
 
 |