Login   Register  
PHP Classes
elePHPant
Icontem

File: DomDocument.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Korvus  >  DOM Document  >  DomDocument.php  >  Download  
File: DomDocument.php
Role: Class source
Content type: text/plain
Description: Main class
Class: DOM Document
Edit XML documents with Expat DOM implementation
Author: By
Last change: - Class DomDocument renamed to 'Document'
- Class DomElement renamed to 'DocumentElement'
- Fixed some pointer bugs on methods such as 'appendChild', 'appendSibling' ...
- Document :: clone method renamed to Document :: copy
- Added new parameter at Document :: toString, which allows to output indented xml code
- Added DocumentElement :: removeAttribute method
- Added new parameter on DocumentElement :: appendChild & DocumentElement :: appendSibling methods, which allows to define where to place the new xml node ( DocumentElement object )
Date: 9 years ago
Size: 12,536 bytes
 

Contents

Class file image Download
<?

/******************************************

Fichero:		/php/classes/Document.php
Fecha:			22-04-2004
Autor:			Korvus

Copyright (c) Vivace Multimedia
http://www.vivacemultimedia.com

******************************************/

define( "DOM_INSERT_FIRST", 0 );
define( "DOM_INSERT_LAST", 1 );
define( "DOM_INSERT_AFTER", 1 );
define( "DOM_INSERT_BEFORE", 0 );

class Document
{

	// ==================== Atributos ==========================

	var $_path;
	
	var $encoding			=			"ISO-8859-1";
	var $version			=			"1.0";
	
	var $firstChild;
	var $_elements			=			array();
	
	// ==================== Metodos SET ========================
	
	function Document( $sFile = NULL )
	{
	 if( is_null( $sFile ) || !file_exists( $sFile ) )
	  return( false );
	 
	 $this->_path = $sFile;
	 $this->parseFromString( file_get_contents( $this->getPath() ) );
	}	

	function parseFromString( $sString )
	{
	 $sSource = preg_replace( "/>\s+</i", "><", $sString );
	 $oLevels = array();
	 
	 $oParser = xml_parser_create( $this->encoding );
	 xml_parser_set_option( $oParser, XML_OPTION_CASE_FOLDING, 0 );
	 xml_parse_into_struct( $oParser, $sSource, $oElements );
	 xml_parser_free( $oParser );
	 
	 # Situa los nodos en orden jerarquico con arrays
	 # No conserva los tags de fin de elemento ( </fin> ) en el arbol de elementos
	 
	 for( $i = 0; $i < count( $oElements ); $i++ )
	 {
	  # Si el elemento es del tipo cdata, subimos 1 nivel para que sea asignado al padre
	  # correcto, y dejamos el tag a 'NULL' para que la funcion 'createElement' cree un 'cdata'
	  
	  if( $oElements[ $i ][ "type" ] == "cdata" )
	  {
	   $oElements[ $i ][ "level" ]++;
	   $oElements[ $i ][ "tag" ] = NULL;
	  }
	  
	  if( $oElements[ $i ][ "type" ] != "close" )
	  {
	   $nIndex = array_push( &$this->_elements, $this->createElement( $oElements[ $i ][ "tag" ], $oElements[ $i ][ "value" ] ) ) - 1;
	   
	   # Propiedades del nuevo elemento XML
	   # valor, atributos, nodo padre, posicion dentro de los nodos hijos, documento raiz ...
	   
	   $this->_elements[ $nIndex ]->_root = &$this;
	   $this->_elements[ $nIndex ]->attributes = ( array ) $oElements[ $i ][ "attributes" ]; 
	   
	   $oLevels[ $oElements[ $i ][ "level" ] ] = &$this->_elements[ $nIndex ];
	   
	   if( is_object( $oLevels[ $oElements[ $i ][ "level" ] - 1 ] ) )
		$oLevels[ $oElements[ $i ][ "level" ] - 1 ]->appendChild( &$this->_elements[ $nIndex ] );
	  }
	  else
	   array_slice( $oElements, $i, 1 );
	 } 
	 
	 $this->firstChild = &$this->_elements[ 0 ];
	 $this->_elements[ 0 ]->parentNode = &$this;	
	}
	
	// ==================== Metodos GET ========================
	
	function getPath()
	{ return( $this->_path ); }
	
	# Devuelve un array con aquellos elementos cuyo tag coincida con
	# alguno de los elementos del array $oTagName
	
	function getElementsByTagName( $sTagName )
	{
	 for( $i = 0; $i < count( $this->_elements ); $i++ )
	 {
	  if( $sTagName == "*" || eregi( "^($sTagName)$", $this->_elements[ $i ]->nodeName ) )
	   $oMatches[] = &$this->_elements[ $i ];
	 }
	 
	 return( $oMatches );
	}
	
	# Devuelve el primer elemento cuyo identificativo sea igual a $sID
	
	function &getElementById( $sID )
	{ 
	 for( $i = 0; $i < count( $this->_elements ); $i++ )
	 {
	  if( strcmp( $this->_elements[ $i ]->getAttribute( "id" ), $sID ) == 0 )
	   return( $this->_elements[ $i ] );
	 }
	}
	
	# Devuelve un array de elementos cuyos atributos '$sName' tengan 
	# un valor identico al indicado en el segundo parametro '$sValue'
	
	function getElementsByAttribute( $sName, $sValue )
	{ 
	 for( $i = 0; $i < count( $this->_elements ); $i++ )
	 {
	  if( $this->_elements[ $i ]->getAttribute( $sName ) == $sValue )
	   $oMatches[] = &$this->_elements[ $i ];
	 }
	 
	 return( $oMatches );
	}	
	
	// ==================== Otros metodos ======================
	
	function isValid()
	{ return( is_object( $this->firstChild ) ); }
	
	# Crea un nuevo elemento con el tag, valor y atributos especificados
	
	function &createElement( $sTagName = NULL, $sValue = NULL, $oAttributes = array() )
	{ 
	 $oElement = new DocumentElement( $sTagName, $sValue );
	 $oElement->attributes = $oAttributes;
	 $oElement->_root = &$this;
	 
	 return( $oElement ); 
	}
	
	function createCdata( $sString )
	{ return( $this->createElement( NULL, $sString ) ); }
		
	function toString( $bFormatted = false, $bIncludeHeader = true )
	{ 
	 if( $bIncludeHeader == true )
	  $sReturn = "<?xml version=\"" . $this->version . "\" encoding=\"" . $this->encoding . "\" ?>\r\n\r\n";
	  
	 return( $sReturn . $this->firstChild->toString( true, false, $bFormatted ? 0 : -1 ) ); 
	}

}


class DocumentElement
{

	// ==================== Atributos ==========================

	var $nodeName			=		NULL;
	var $nodeValue			=		NULL;
	
	var $attributes			=		array();	
	
	var $parentNode			=		NULL;
	var $firstChild			=		NULL;
	var $lastChild			=		NULL;
	var $nextSibling		=		NULL;
	var $previousSibling	=		NULL;
		
	// ==================== Metodos SET ========================

	function DocumentElement( $sTagName, $sValue = NULL )
	{ 
	 $this->nodeName = $sTagName; 
	 $this->nodeValue = $sValue;
	}
	
	function setAttribute( $sKey, $sValue, $bOverWrite = true )
	{ 
	 if( $bOverWrite == true || !isset( $this->attributes[ $sKey ] ) )
	  $this->attributes[ $sKey ] = $sValue;
	}
	
	function removeAttribute( $sKey )
	{ unset( $this->attributes[ $sKey ] ); }
	
	// ==================== Metodos GET ========================

	function getContent()
	{
	 if( !is_null( $this->nodeValue ) || !$this->hasChildNodes() )
	  return( $this->nodeValue );	
	 else
	  return( $this );
	}
	
	function getAttribute( $sName )
	{ return( $this->attributes[ $sName ] ); }
	
	# Busca todos los elementos a partir de 'this' cuyo tag sea igual al patron
	# especificado en 'sTagName'
	# Esta funcion realiza una busqueda recursiva a la que podemos definir el numero
	# maximo de niveles a buscar ( predeterminado -1: todos los niveles )
	
	function getElementsByTagName( $sTagName, $nMaxLevels = -1, $nLevel = 0 )
	{ 
	 if( $nMaxLevels == -1 || $nLevel < $nMaxLevels )
	 {
	  $oMatches = array();
	  $oChild 	= &$this->firstChild;
	  
	  while( $oChild !== NULL )
	  {
	   if( $sTagName == "*" || eregi( "(" . $sTagName . ")", $oChild->nodeName ) )
		array_push( $oMatches, &$oChild );
		
	   $oMatches = array_merge( &$oMatches, $oChild->getElementsByTagName( $sTagName, $nMaxLevels, $nLevel + 1 ) );	   
	   $oChild	 = &$oChild->nextSibling;
	  }
	  
	  return( $oMatches );
	 }
	}
	
	function hasChildNodes()
	{ return( $this->firstChild !== NULL ); }
	
	function hasAttribute( $sName )
	{ return( isset( $this->attributes[ $sName ] ) ); }

	// ==================== Otros metodos ======================
	
	# Agrega un nuevo elemento hijo
	# Si es la primera vez que se le agrega un hijo, setearemos 
	# los punteros 'firstChild' y 'lastChild'
	
	function &appendChild( &$oNewChild, $nPosition = DOM_INSERT_LAST )
	{ 
	 $oNewChild->unlink();
	 $oNewChild->parentNode = &$this;
	
	 if( $this->firstChild === NULL )
	 {
	  $this->firstChild = &$oNewChild;
	  $this->lastChild  = &$oNewChild;
	 }
	 else
	 {
	  switch( $nPosition )
	  {
	   case DOM_INSERT_FIRST:
	   
	    $oNewChild->nextSibling = &$this->firstChild;
		$this->firstChild->previousSibling = &$oNewChild;
		$this->firstChild = &$oNewChild;
	   
	   break;
	  
	   case DOM_INSERT_LAST: 
	  
	  	$oNewChild->previousSibling = &$this->lastChild;
	  	$this->lastChild->nextSibling = &$oNewChild;
  	  	$this->lastChild = &$oNewChild;
	
	   break;
	  }
	 }
	 
	 return( $oNewChild );
	}
	
	# Agrega un nuevo elemento hermano, en el mismo nivel que el actual
	# y a continuacion del mismo. Tiene en cuenta si el nuevo nodo tiene
	# tambien nodos en su mismo nivel, y los enlaza correctamente
	
	function &appendSibling( &$oNewSibling, $nPosition = DOM_INSERT_AFTER )
	{
	 $oNewSibling->unlink();
	 $oNewSibling->parentNode = &$this->parentNode;
	 	
	 switch( $nPosition )
	 {
	  case DOM_INSERT_AFTER:
	  
	   if( $this->nextSibling !== NULL )
	   {
		$oNewSibling->nextSibling = &$this->nextSibling;
		$this->nextSibling->previousSibling = &$oNewSibling;
	   }
	   else
		$this->parentNode->lastChild = &$oNewSibling;
	  
	   $oNewSibling->previousSibling = &$this;
	   $this->nextSibling = &$oNewSibling;
	   
	  return( $oNewSibling );
	  
	  case DOM_INSERT_BEFORE:
	  
	   if( $this->previousSibling !== NULL )
	   {
	    $oNewSibling->previousSibling = &$this->previousSibling;
		$this->previousSibling->nextSibling = &$oNewSibling;
	   }
	   else
	    $this->parentNode->firstChild = &$oNewSibling;
		
	   $oNewSibling->nextSibling = &$this;
	   $this->previousSibling = &$oNewSibling;
	   
	  return( $oNewSibling );
	 }
	}
	
	# Reemplaza un nodo
	
	function &replace( $oNode )
	{
	 $this->nodeName   = $oNode->nodeName;
	 $this->nodeValue  = $oNode->nodeValue;
	 $this->attributes = $oNode->attributes;
	 
	 unset( $this->firstChild, $this->lastChild );
	 
	 if( $oNode->hasChildNodes() )
	 {
	  $this->firstChild = &$oNode->firstChild;
	  $this->lastChild  = &$oNode->lastChild;
	  $oNextSibling 	= &$oNode->firstChild;
	  
	  while( $oNextSibling !== NULL )
	  {
	   $oNextSibling->parentNode = &$this;
	   $oNextSibling = &$oNextSibling->nextSibling;
	  }
	 }
	 
	 return( $this );
	}
	
	# Clona un nodo y todos sus subhijos ( estos ultimos solo en caso de que
	# el primer parametro sea 'true'
	
	function &copy( $bCloneChildren = true )
	{
	 $oNewNode = $this->_root->createElement( $this->nodeName, $this->nodeValue );
	 $oNewNode->attributes = $this->attributes;
	 $oNewNode->_root = &$this->_root;
	 
	 if( $bCloneChildren == true && $oChild = &$this->firstChild )
	 {
	  while( $oChild !== NULL )
	  {
	   $oNewChild = &$oChild->copy( $bCloneChildren );
	   $oNewNode->appendChild( &$oNewChild, DOM_INSERT_LAST );
	   $oChild = &$oChild->nextSibling;
	  }
	 }
	
	 return( $oNewNode );
	}
	
	# Desvincula el nodo del arbol de elementos

	function unlink()
	{ 
	 if( $this->nextSibling !== NULL ) $this->nextSibling->previousSibling = &$this->previousSibling;
	 else $this->parentNode->lastChild = &$this->previousSibling;
	  
	 if( $this->previousSibling !== NULL ) $this->previousSibling->nextSibling = &$this->nextSibling;
	 else $this->parentNode->firstChild = &$this->nextSibling;
	  
	 unset( $this->parentNode, $this->nextSibling, $this->previousSibling );
	}
	
	# Devuelve el codigo del nodo y sus hijos
	# Recibe un argumento opcional 'nTabs' utilizado internamente que indica
	# el numero de tabuladores asociados al nodo actual ( nivel )
	
	function toString( $bIncludeChildren = true, $bOnlyChildren = false, $nTabs = -1 )
	{
	 # Cdata section. No tiene en cuenta atributos ni hijos
	
	 if( $this->nodeName === NULL )
	  return( "<![CDATA[" . $this->nodeValue . "]]>" );
	 
	 # Atributos del elemento
	  
	 foreach( $this->attributes as $sKey => $sValue )
	  $sAttributes .= " " . $sKey . "=\"" . $sValue . "\"";
	  
	 # Si tiene hijos almacenamos el codigo de estos en 'sChildren'
	 # Ademas lo tabulamos mediante la variable 'nTabs'
	  
	 if( $bIncludeChildren == true && $this->hasChildNodes() )
	 {
	  $oCurrentNode = &$this->firstChild;
	  
	  if( $nTabs != -1 && ( $this->firstChild->nextSibling !== NULL || $this->firstChild->hasChildNodes() ) )
	   $sTabCode = "\n" . str_repeat( "\t", $nTabs + 1 );
	  
	  while( $oCurrentNode !== NULL )
	  { 
	   $sChildren   .=  $sTabCode . $oCurrentNode->toString( true, false, ( $nTabs == -1 ? $nTabs : $nTabs + 1 ) );
	   $oCurrentNode =  &$oCurrentNode->nextSibling;
	  }
	  
	  if( $nTabs != -1 && ( $this->firstChild->nextSibling !== NULL || $this->firstChild->hasChildNodes() ) )
	   $sChildren .= "\n" . str_repeat( "\t", $nTabs );
	 }
	 
	 if( $bOnlyChildren == true )
	  return( $sChildren );
	 
	 # Finalmente devolvemos el codigo pertinente dependiendo de si el nodo
	 # tiene hijos o no y si tiene un valor no nulo
	 
	 if( $this->nodeValue === NULL && !isset( $sChildren ) )
	  return( "<" . $this->nodeName . $sAttributes . " />" );
	 else
 	  return( "<" . $this->nodeName . $sAttributes . ">" . $this->nodeValue . $sChildren . "</" . $this->nodeName . ">" );
	}


}

?>