Codice PHP:
<?php
  
// ...

  /**
   * Sets an array of attributes
   * @access public
   * @param mixed $attributes The attributes to assing to the current node
   */           
  
public function setAttributes($attributes$merge false)
  {
    if (
is_array($attributes))
    {
      
$this->attributes $merge array_merge($this->attributes$attributes) : $attributes;
    }
  }

  
/**
   * Returns the request attribute of the node
   * @access public
   * @param string $name The name of the attribute
   * @param string $default The default value to return, in case the attribute is not availlable         
   * @return mixed The value of the attribute
   */        
  
public function getAttribute($name$default null)
  {
    return 
$this->hasAttribute($name) ? $this->attributes[$name] : $default;
  }

  
/**
   * Returns an array of the attributes
   * @access public
   * @return array The array of the attributes
   */           
  
public function getAttributes()
  {
    return 
$this->attributes;
  }

  
/**
   * Returns the sons of the current node filtered by a set of defined field
   *
   * @access public
   * @param array $filters An array in a key => value form
   * @return array The array of filtered sons
   */
  
public function getFilteredSons(array $filters)
  {
    
$sons = array();
    foreach (
$this->getSons() as $son)
    {
      
$pass true;
      foreach (
$filters as $name => $value)
      {
        if (
$son->getAttribute($name) != $value)
        {
          
$pass false;
          break;
        }
      }
      if (
$pass)
      {
        
$sons[] = $son;
      }
    }
    return 
$sons;
  }

  
/**
   * Returns the parent (if exists) of the current node
   *
   * @access public
   * @return mixed The parent if exists, otherwise null
   */
  
public function getParent()
  {
    return 
$this->parent;
  }

  
/**
   * Sets the parent of the current node
   *
   * @access public   
   * @param exXmlNode $parent The parent of the current node
   */
  
public function setParent(esXmlNode $parent)
  {
    
$this->parent $parent;
  }

  
/**
   * Filter the sons by the tag name
   * 
   * @access public
   * @param string $name The name of the requested node
   * @return array An array of esXmlNode
   */
  
public function getSonsByName($name)
  {
    
$sons = array();
    foreach (
$this->getSons() as $son)
    {
      if (
$son->getName() == $name)
      {
        
$sons[] = $son;
      }
    }
    return 
$sons;
  }

  
/**
   * Finds all the nodes (with the actual included) that pass the defined filters
   * 
   * @access public
   * @param string $name The name of the node we are looking for
   * @param array $filters An array of defined filters
   * @param array $moreFilters An array of special filters
   * @return array An array containing all the nodes that pass the defined filters
   */
  
public function find($name$filters = array(), $moreFilters = array())
  {
    
$results = array();
    
$pass true;
    if (
$name)
    {
      if (
$this->getName() != $name)
      {
        
$pass false;
      }
    }
    if (
$pass and is_array($filters) and count($filters))
    {
      foreach (
$filters as $filter)
      {
        
$field $filter[0];
        
$value $filter[1];
        
$comparison array_key_exists(2$filter) ? $filter[2] : '==';
        switch (
$comparison)
        {
          case 
'>':
          {
            
$isGood = (float) $this->attributes[$field] > $value;
            break;
          }
          case 
'>=':
          {
            
$isGood = (float) $this->attributes[$field] >= $value;
            break;
          }
          case 
'<':
          {
            
$isGood = (float) $this->attributes[$field] < $value;
            break;
          }
          case 
'<=':
          {
            
$isGood = (float) $this->attributes[$field] <= $value;
            break;
          }
          case 
'==':
          {
            
$isGood $this->attributes[$field] == $value;
            break;
          }
          case 
'!=':
          {
            
$isGood $this->attributes[$field] != $value;
            break;
          }
          default:
          {
            
$isGood true;
            break;
          }
        }
        if (!
$isGood)
        {
          
$pass false;
          break;
        }
      }
    }
    if (
is_array($moreFilters))
    {
      if (
$pass and array_key_exists('is_leaf'$moreFilters) and (bool) $moreFilters['is_leaf'] != $this->isLeaf())
      {
        
$pass false;
      }
      if (
$pass and array_key_exists('has_parent'$moreFilters) and (bool) $moreFilters['has_parent'] != ($this->parent !== null))
      {
        
$pass false;
      }
      if (
$pass and array_key_exists('sons_more_than'$moreFilters) and (int) $moreFilters['sons_more_than'] <= count($this->sons))
      {
        
$pass false;
      }
      if (
$pass and array_key_exists('sons_less_than'$moreFilters) and (int) $moreFilters['sons_less_than'] >= count($this->sons))
      {
        
$pass false;
      }
      if (
$pass and array_key_exists('sons_more_equal_than'$moreFilters) and (int) $moreFilters['sons_more_equal_than'] < count($this->sons))
      {
        
$pass false;
      }
      if (
$pass and array_key_exists('sons_less_equal_than'$moreFilters) and (int) $moreFilters['sons_less_equal_than'] > count($this->sons))
      {
        
$pass false;
      }
    }
    if (
$pass)
    {
      
$results[] = $this;
    }
    foreach (
$this->getSons() as $son)
    {
      foreach (
$son->find($name$filters$moreFilters) as $occurency)
      {
        
$results[] = $occurency;
      }
    }
    return 
$results;
  }

  
/**
   * Returns the xml rappresentation of the node
   * @access public
   * @param integer $spaces The spaces in the beginning of the line
   * @param boolean $xmlHeader If the method has to display the header of the xml
   * @param boolean $cdata If the node must print the cdata string   
   * @return string The xml rappresentation of the node         
   */        
  
public function toXml($spaces 0$xmlHeader true$cdata false)
  {
    
$xml '';
    if (
$xmlHeader)
    {
      
$xml '<?xml version="1.0" encoding="UTF-8" ?>' "\n";
    }
    for (
$i 0$i $spaces; ++$i)
    {
      
$xml .= ' ';
    }
    
$xml .= '<' $this->getName();
    foreach (
$this->getAttributes() as $key => $value)
    {
      
$xml .= ' ' $key '="' $value '"';
    }
    if (
$this->isLeaf())
    {
      if (
$this->getValue())
      {
        
$xml .= '>';
        if (
$this->cdata)
        {
          
$xml .= '<![CDATA[';
          
$xml .= $this->getValue();
          
$xml .= ']]>';
        }
        elseif (
$cdata)
        {
          
$xml .= '<![CDATA[';
          
$xml .= $this->getValue();
          
$xml .= ']]>';
        }
        else
        {
          
$xml .= $this->getValue();
        }
        
$xml .= '</' $this->getName() . '>';
      }
      elseif (
$this->shortIfEmpty)
      {
        
$xml .= ' />';
      }
      else
      {
        
$xml .= '>';
        if (
$this->cdata or $cdata)
        {
          
$xml .= '<![CDATA[]]>';
        }
        
$xml .= '</' $this->getName() . '>';
      }
    }
    else
    {
      
$xml .= '>' "\n";
      foreach (
$this->getSons() as $son)
      {
        
$xml .= $son->toXml($spaces 2false$cdata);
      }
      for (
$i 0$i $spaces; ++$i)
      {
        
$xml .= ' ';
      }
      
$xml .= '</' $this->getName() . '>';
    }
    return 
$xml "\n";
  }

}
Ho dovuto spezzare la classe perché era troppo lunga..

L'utilizzo è abbastanza semplice:
Codice PHP:
<?php
$nodes 
esXmlParser::parse(file_get_contents('file.xml'));
E verra creato l'oggetto nodes, comprendente tutte le proprietà, i figli, filtrabili tramite i metodi getFilteredSons e find, e un metodo toXml per rappresentare tutta l'oggetto in una stringa xml corretta.

Ho postato qui sia per dare aiuto a chi magari serve una classe del genere, sia per chiedere consiglio su cos'altro implementare.. Già cosi mi sembra abbastanza completa, ma non si sa mai!

Un'ultima cosa.. La classe non è ottimizzata per le prestazioni, anche se se la cavicchia discretamente. Ho preferito seguire la strada della semantica e bellezza del codice piuttosto che quella della velocità ad ogni costo. In ogni modo, c'è sempre la possibilità di sistemare..

Ora aspetto.. Giudizi, critiche, domande.. Fate voi!