Location: PHPKode > scripts > crXml > crXml.php
<?php
/*

Copyright (c) 2011 Sandeep.C.R, <hide@address.com>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

// List of Recent modifications

// Fixed misc bugs
// changed the below format ..->{'prefix:localName'}($namespaceURI) to  ..->{'namespaceURI|localName|prefix'}
// Added format ..->{'prefix:localName'}($namespaceURI) to add nodes of type <prf:item xmlns:prf = "http://yahoo.com".....
// Added 'addNameSpaceDefNode' function to add nodes of type <prf:item xmlns:prf = "http://yahoo.com".....
// Added remove function to remove a node
// Added emptyNode function to delete all child nodes
// Fixed minor bug in fullNode.
// Made attributes to copy while using fullNode function
// Fixed minor bug in offsetunset
// Fixed a minor bug in offsetSet
// Added fullnode function to use in xml editing.
// Added support for namespaced attributes.
// Thu Feb 3 4 09:59:19 IST 2011
// XML Parser/Generator Class 

// Author: Sandeep.C.R 
// hide@address.com

class crXml implements arrayAccess, iterator
{
	protected $node;
	protected $parent;

	protected $root;
	protected $offset;
	protected $nodeName;

	protected $current;

	public function __construct(crXml $parent=null,crXml $root=null,$nodeName='',$offset=0)
	{
		$this->root=$root;
		if(is_null($parent)) {
			$this->node=new DOMDocument();
			$this->root=$this;
			$this->parent=null;
		} else {
			$this->node=null;
			$this->parent=$parent;
		}
		$this->offset=$offset;
		$this->nodeName=$nodeName;
	}
	public function __call($name,$args) 
	{
		if(strpos($name,":")!==false) {
			list($prefix,$localName)=explode(':',$name);
			if(isset($args[0])) $nameSpaceURI = $args[0];else throw  new Exception("No URI given for namespace prefix $prefix");
			return $this->addNameSpaceDefNode($prefix,$localName,$nameSpaceURI);
		}
	}
	public function __unset($name)
	{
		if($node=$this->_getNode()) {
			if($childNodes=$this->_getChildrenForNode($node,$name)) {
				if($childNode=$childNodes->item($this->offset)) {
					$node->removeChild($childNode);
				}
			}
		}
	}
	public function __set($name,$value)
	{
		if(is_null($this->node)) {
			$this->node=$this->connectToParent();
		}
		if($this->offset==0) {
			$childNodes=$this->_getChildrenForNode($this->_getNode(),$name);
			if($node=$childNodes->item(0)) {
				$this->_assignValue($node,$value);
			} else {
				$childNode=$this->_getNewElement($name);
				$this->node->appendChild($childNode);
				$this->_assignValue($childNode,$value);
			}
			return;
		} else {
			if($this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->length-1 < $this->offset) {
				while($this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->length < $this->offset) {
					$element=$this->_getNewElement($this->nodeName);
					$this->parent->_getNode()->appendChild($element);
				}
				$element=$this->_getNewElement($this->nodeName);
				$lastNode=$this->parent->_getNode()->appendChild($element);
				$element=$this->_getNewElement($name);
				$lastNode->appendChild($element);
				$this->_assignValue($element,$value);
			} else {
				$targetNode=$this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->item($this->offset);
				if($node=$this->_getChildrenForNode($targetNode,$name)->item(0)) {
					$this->_assignValue($node,$value);
				} else {
					$element=$this->_getNewElement($name);
					$targetNode->appendChild($element);
					$this->_assignValue($element,$value);
				}
			}

		}
	}
	public function __get($name)
	{
		if($this->offset>0)
		{
			$crXml=new crXml($this,$this->root,$name);
			if($this->parent->_getNode()) {
				if($node=$this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->item($this->offset)) {
					$childNode=$this->_getChildrenForNode($node,$name)->item(0);
					$crXml->node=$childNode;
				}
			}
			return $crXml;
		}
		else {
			$crXml=new crXml($this,$this->root,$name);
			if($this->_getNode() && $node=$this->_getChildrenForNode($this->_getNode(),$name)->item(0)) {
				$crXml->node=$node;
			}
			return $crXml;
		}
	}
	public function connectToParent()
	{
		if(is_null($this->parent) || !is_null($this->_getNode())) {
			if($this->offset>0) {
				if($this->parent) {
					return $this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->item($this->offset);
				}
			} else {
				return $this->node;
			}
		} else {
			$parentNode=$this->parent->connectToParent();
			if($this->_getChildrenForNode($parentNode,$this->nodeName)->length-1 < $this->offset) {
				while($this->_getChildrenForNode($parentNode,$this->nodeName)->length < $this->offset) {
					$element=$this->_getNewElement($this->nodeName);
					$parentNode->appendChild($element);
				}
			}
			$element=$this->_getNewElement($this->nodeName);
			$this->node=$parentNode->appendChild($element);
			return $this->node;
		}

	}
	// arrayaccess interface
	public function offsetExists($offset)
	{
		if(is_string($offset)) {
			if($node=$this->_getNode()->attributes->getNamedItem($offset)) {
				return true;
			} else {
				return false;
			}
		} else {
			if($node=$this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->item($offset)) {
				return true;
			} else {
				return false;
			}
		}
	}
	public function offsetGet($offset)
	{
		if(is_string($offset)) {
			if(strpos($offset,":")!==false) {
				list($prefix,$localName)=explode(':',$offset);
				$nameSpaceURI=$this->root->_getNode()->lookupNamespaceURI($prefix);
				if($node=$this->_getNode()->attributes->getNamedItemNS ($nameSpaceURI , $localName )) {
					return $node->nodeValue;
				}
			} else {
				if($node=$this->_getNode()->attributes->getNamedItem($offset)) {
					return $node->nodeValue;
				} else {
					return false;
				}
			}

		} else {
			$node=clone $this;
			$node->offset=$offset;
			return $node;
		}
	}
	public function offsetSet($offset,$value)
	{
		if(!is_object($value)) $value=str_replace('&','&amp;',$value);
		if(is_null($this->_getNode())) {
			$this->node=$this->connectToParent();
		}
		if(is_string($offset)) {
			$this->_getNode()->setAttribute($offset,$value);
		} else {
			if($this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->length-1 < $offset) {
				while($this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->length < $offset) {
					$element=$this->_getNewElement($this->nodeName);
					$this->parent->_getNode()->appendChild($element);
				}					  
				$element=$this->_getNewElement($this->nodeName);
				$lastNode=$this->parent->_getNode()->appendChild($element);
				$this->_assignValue($element,$value);
			} else {
				$node=$this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->item($offset);
				$this->_assignValue($node,$value);
			}
		}
	}
	public function offsetUnset($offset)
	{
		$deadNode=new crXml();
		if(is_string($offset)) {
			$node=$this->_getNode();
			$node->removeAttribute($offset);
		} else {
			$deadNode->node=$this->parent->_getNode();
			$deadNode->root=$this->root;
			if($node=$deadNode->_getNode()) {
				if($childNodes=$deadNode->_getChildrenForNode($node,$this->nodeName)) {
					if($childNode=$childNodes->item($offset)) {
						$node->removeChild($childNode);
					}
				}
			}
		}
	}
	function __toString()
	{
		if($node=$this->_getNode()) {
			if($cDataNode=$node->childNodes->item(1)) {
				if($cDataNode->nodeName=='#cdata-section') {
					return $cDataNode->nodeValue;
				} else {
					return '';
				}
			}
			if($childNode=$node->childNodes->item(0)) {
				if($childNode->nodeType==3 || $childNode->nodeType==4 ) {
					return $childNode->nodeValue;
				} else {
					return '';
				}
			}else {
				return '';
			}
		} else {
			return '';
		}
	}
	function loadXML($xmlString)
	{
		$this->node->loadXML($xmlString);
		$this->parent=null;
	}

	function xml()
	{
		$node=$this->_getNode();
		return $this->root->_getNode()->saveXML($node);
	}
	function _getNode()
	{
		if($this->offset==0) {
			return $this->node;
		} else {
			if($this->parent) if($this->parent->_getNode()) {
				return  $this->_getChildrenForNode($this->parent->_getNode(),$this->nodeName)->item($this->offset);
			}
			
		}
		return null;
	}
	function count($name='')
	{
		if(!is_null($this->node)) {
			if($name) {
				return $this->_getChildrenForNode($this->_getNode(),$name)->length;
			}
			else {
				return $this->_getNode()->childNodes->length;
			}
		} else  return 0;
	}
	function addNameSpaceDefNode($prefix,$nodeName,$nameSpaceURI)
	{
		if(is_null($this->node)) {
			$this->node=$this->connectToParent();
		}
		$element=$this->root->_getNode()->createElementNS($nameSpaceURI,"$prefix:$nodeName",'');
		$this->node->appendChild($element);
		$fullName = "$nameSpaceURI|$nodeName";
		return $this->$fullName;
	}
	function addNameSpace($nameSpaces)
	{
		if(is_null($this->node)) {
			$this->node=$this->connectToParent();
		}
		foreach($nameSpaces as $prefix=>$nameSpaceURI) {
			$this->_getNode()->setAttributeNS('http://www.w3.org/2000/xmlns/' ,"xmlns:$prefix" , $nameSpaceURI);
		}
		return $this;
	}
	function _getNewElement($name,$value=null)
	{
		if(strpos($name,"|")!==false) {
			list($nameSpaceURI,$localName,$prefix)=explode('|',$name);
			if(isset($prefix)) {
				$element=$this->root->_getNode()->createElementNS($nameSpaceURI,"$prefix:$localName",'');
			} else {
				$element=$this->root->_getNode()->createElementNS($nameSpaceURI,"$localName",'');
			}
			if($value) $element->nodeValue=$value;
			return $element;
		} else if(strpos($name,":")!==false) {
			list($prefix,$localName)=explode(':',$name);
			$nameSpaceURI=$this->root->_getNode()->lookupNamespaceURI($prefix);
			$element=$this->root->_getNode()->createElementNS($nameSpaceURI,"$prefix:$localName",'');
			if($value) $element->nodeValue=$value;
			return $element;
		} else {
			return new DOMElement($name,$value);
		}
	}
	function _getChildrenForNode($node,$name)
	{

		if(strpos($name,"|")!==false) {
			list($nameSpaceURI,$localName)=explode('|',$name);
			$domXpath=new DOMXpath($this->root->node);
			$domXpath->registerNamespace('atom',$nameSpaceURI);
			$childNodes=$domXpath->query("atom:$localName",$node);
		} else if(strpos($name,":")!==false) {
			list($prefix,$localName)=explode(':',$name);
			$domXpath=new DOMXpath($this->root->node);
			if($nameSpaceURI = $this->root->node->lookupNamespaceURI($prefix)) {
				$domXpath->registerNamespace($prefix,$nameSpaceURI);
			}
			else {
				$found = false;
				if($node->childNodes->length) 	foreach(range(0,$node->childNodes->length-1) as $v) {
					$childNode = $node->childNodes->item($v);
					$childNodeName = $node->childNodes->item($v)->nodeName;
					if($childNodeName == $name ) {
						$childNodeNamespaceURI = $childNode->namespaceURI;
						$childNodePrefix = $childNode->prefix;
						$domXpath->registerNamespace($childNodePrefix,$childNodeNamespaceURI);
						$found = true;
					}
				}
				if(!$found) throw new Exception("undefined namespace prefix $prefix");
			}
			$childNodes=$domXpath->query($name,$node);
		} elseif($node->isDefaultNamespace($node->namespaceURI)) {
			$domXpath=new DOMXpath($this->root->node);
			$parentNamespaceURI = $node->namespaceURI;
			$domXpath->registerNamespace('atom',$parentNamespaceURI);
			$childNodes=$domXpath->query("atom:$name",$node);
		} else{
			$domXpath=new DOMXpath($this->root->node);
			$childNodes=$domXpath->query($name,$node);
		}
		if($childNodes->length == 0) {
			if($node->childNodes->length) 	foreach(range(0,$node->childNodes->length-1) as $v) {
				$childNodeName = $node->childNodes->item($v)->nodeName;
				if(strpos($childNodeName,":")!==false) 	list($prefix,$localname)=explode(':',$childNodeName);else {
				$prefix = '';
				$localname = $childNodeName;
				}
				if(strpos($name,":")!==false) 	list($tprefix,$tlocalname)=explode(':',$name);else {
				$tlocalname = $name;
				$tprefix = '';
				}

				if($localname==$tlocalname) {
					$foundprefix = $this->root->node->lookupPrefix($node->childNodes->item($v)->namespaceURI);
					throw new Exception("<br/>No child '".$name."' with default or specified ($tprefix) name space found for node {$node->nodeName}, but <b>found a node</b>, '".$localname."' within namespace '".$node->childNodes->item($v)->namespaceURI."(prefix:$foundprefix)' for same node.Please use format <br/>\n<br/>\n ..->{$node->nodeName}->".((strlen($foundprefix)>0)?"{'{$foundprefix}:{$tlocalname}'} <br/><br/>to access the above node<br/>\n<br/>\n":"$tlocalname <br/><br/>to access the above node<br/>\n<br/>\n"));
				}
			}
		}
		return $childNodes;
	}

	public function _assignValue($node,$value)
	{
		if(!is_object($value)) $value = str_replace('&','&amp;',$value);
		if(is_object($value)) {
			if(get_class($value) == 'stdClass') { 
				$node->nodeValue='';
				$cDataSection=$this->root->node->createCDATASection($value->scalar);
				$node->appendChild($cDataSection);
			}
			elseif (get_class($value) == 'crXml') {
				$fragmentNode = new DOMDocument();
				$currentChildNode = 0;
				while($currentChildNodeElement = $value->_getNode()->childNodes->item($currentChildNode)) {
				$node->appendChild($this->root->node->importNode($value->_getNode()->childNodes->item($currentChildNode)->cloneNode(true),true));
				$currentChildNode++;
				}
			}
		} else {
			$node->nodeValue=$value;
		}
	}
	function remove()
	{
		if($this->parent) {
			$this->parent->_getNode()->removeChild($this->_getNode());
		}
	}
	function emptyNode()
	{
		$node = $this->_getNode();
		while ($node->childNodes->length) $node->removeChild($node->firstChild);
	}
	function fullNode()
	{
		$temp = new crXml();
		$nodeName = $this->_getNode()->nodeName;
		if(strpos($nodeName,"|")!==false) {
			list($namespaceURI,$localName)=explode('|',$nodeName);
			$nodeName = $nameSpaceURI. "|" . $localName;
			$temp->$nodeName = $this;
		} else if(strpos($nodeName,":")!==false) {
			list($prefix,$localName)=explode(':',$nodeName);
			$nameSpaceURI=$this->_getNode()->namespaceURI;
			$nodeName = $prefix. ":" . $localName;
			$temp->addNameSpaceDefNode($prefix,$localName,$nameSpaceURI);
			$temp->$nodeName = $this;
		} else{
			$temp->$nodeName = $this;
		}
		for($i=0;($attributeNode = $this->_getNode()->attributes->item($i));$i++) {
			$temp->$nodeName->offsetSet($attributeNode->nodeName, $attributeNode->nodeValue);
		}
		return $temp;
	}
	//Iterator Interface
	function current()
	{
		$childNodes=$this->_getChildrenForNode($this->_getNode(),'*');
		if($node=$childNodes->item($this->current)) {
			$return=clone $this;
			$return->node=$node;
			$return->offset=0;
			return $return;
		}
	}
	function key()
	{
		$childNodes=$this->_getChildrenForNode($this->_getNode(),'*');
		if($node=$childNodes->item($this->current)) {
			return $node->nodeName;
		} else {
			return false;
		}
	}
	function next()
	{
		$this->current++;
	}
	function rewind()
	{
		$this->current=0;
	}
	function valid()
	{
		$childNodes=$this->_getChildrenForNode($this->_getNode(),'*');
		if($node=$childNodes->item($this->current)) {
			return true;
		} else {
			return false;
		}
	}


	function dump($node=null,$level = 0)
	{
	if(!$node) $node = $this->root->node;
	foreach($node->childNodes as $k=>$v) {
		echo str_repeat("&nbsp ",$level);
		echo $v->nodeName;
		if($v->nodeType == 3 || $v->nodeType == 4 ) echo "=>".$v->nodeValue;
		echo "\n<br/>\n";
		if(isset($v->childNodes)) if($v->childNodes->length > 0) {
			$this->dump($v,$level+1);
			}
		}
	}


}

Return current item: crXml