Location: PHPKode > scripts > ubNode > ubnode/node.php
<?php
/**
 * Permite un parseo de árbol de  un xml, un array,o un objeto
 * y realizar la transformación a cualquiera de ellos
 *
 * @author Reynier Blanco Zambrano
 */
class ubNode
{
        /**
	 * Establece el prefijo de los métodos a tener en cuenta para obtener
	 */
	static public $get = "get__";
	
	/**
	 * Establece el prefijo de los métodos a tener en cuenta para cambiar
	 */
	static public $set = "set__";

	protected $name;
	
	protected $type;
	
	protected $nodes;
	
	protected $value;

	const T_VAR = "0";
	const T_OBJECT = "1";
	const T_ARRAY = "2";
	
	/**
	 * Constructor
	 *
	 * @param string $name
	 */
	public function __construct($name,$type = "var",$value=null)
	{
		$this->name = $type;
		$this->name = $name;
		$this->nodes = array();
		$this->value = ($value)?$value:null;
	}
	
	/**
	 * Devuelve true en caso de que sea hoja
	 * (no tenga nodos hijos)
	 * 
	 * @return bool
	 */
	public function is_leaf()
	{
		return (count($this->nodes) ==0);
	}
	
	/**
	 * Devuelve la cantidad de nodos que posee
	 * @return int
	 */
	public function count()
	{
		return count($this->nodes);
	}
	
	/**
	 * Devuelve un nodo de una posicióon dada
	 *
	 * @param int $pos
	 * @return ubNode
	 */
	public function node($pos = 0)
	{
		$pos = (int)$pos;
		if($pos < 0 || $pos > $this->count())
			throw new Exception("Posición fuera de rabgo");
		
		return $this->nodes[$pos];
	}
	
	public function get_nodes()
	{
		return $this->nodes;
	}
	
	public function get_value()
	{
		return $this->value;
	}
	
	public function get_name()
	{
		return $this->name;
	}
	
	public function get_type()
	{
		return $this->type;
	}
	
	public function set_type($value)
	{
		$this->type = $value;
	}
	
	public function set_value($value)
	{
		$this->value = $value;
	}
	
	/**
	 * Adiciona un nodo
	 *
	 * @param string $name
	 * @param mixed $value
	 * @return ubNode
	 */
	public function add_node($name,$value=null)
	{
		$node = new ubNode($name,$value);
		$this->nodes[] = $node;
		return $node;
	}
	
	/**
	 * Adiciona un nodo
	 *
	 * @param ubNode $node
	 */
	public function append_node($node)
	{
		if(!is_a($node,ubNode))
			throw new Exception("El objecto pasado no es un nodo");
		
		$this->nodes[] = $node;
	}
	
	/**
	 * Devuelve la cadena en xml
	 *
	 * @return string
	 */
	public function to_xml($tab = "\t")
	{		
		$return = "";
		
		if(!$this->is_leaf())
		{
			foreach ($this->nodes as $node) 
				$return .= $node->to_xml($tab."\t");
				
			$return .= "\n$tab";
		}
		else
		{
			if(is_bool($this->value))
			{
				if($this->value)
					$value = "true";
				else
					$value = "false";
			}
			elseif ($this->value == '')
				$value = "''";
			else
				$value = $this->value;
			
			$return .= "$value";
		}
		
		$type = "";
		if($this->type == self::T_OBJECT)
			$type = " ub_class_name = '$this->value'";
		
		if($return == "''")
			return "\n$tab<$this->name$type/>";
		else
			return "\n$tab<$this->name$type>$return</$this->name>";
	}
	
	/**
	 * Convierte el nodo en un array
	 *
	 * @return array
	 */
	public function to_array()
	{
		if(!$this->is_leaf())
		{			
			$return = array();
			foreach ($this->nodes as $node) 
			{
				$name = $node->get_name();
				
				if(ereg("^_([0-9]*$)",$name,$list))
					$name = preg_replace("/_/","",$name);
					
				if(substr($name,0,9) == "ub_value_")
					$name = preg_replace("/ub_value_/","",$name);
				
				if($name)
				{
					if(isset($return[$name]))
					{
						if(!is_array($return[$name]))
							$return[$name] = array($return[$name]);
							
						$return[$name][] = $node->to_array();
					}
					else
					{
						if($node->get_type() == self::T_ARRAY && $node->is_leaf())
							$return[$name] = array($node->to_array());
						else
							$return[$name] = $node->to_array();
					}
				}
				else
					$return[] = $node->to_array();
			}
			
			return $return;
		}else {
			return $this->value;
		}
	}
	
	/**
	 * Devuelve una variable de acuerdo a la información
	 * en caso de que aparesca "ub_class_name" se tratará de crear un objeto
	 * con el nombre de dicho atributo como clase
	 *
	 * @return mixed
	 */
	public function to_object($object = null)
	{
		if(!$this->is_leaf())
		{			
			$return = array();
			foreach ($this->nodes as $node) 
			{
				$name = $node->get_name();
				
				if($object == null)
				{
					if(ereg("^_([0-9]*$)",$name,$list))
						$name = preg_replace("/_/","",$name);
						
					if(substr($name,0,9) == "ub_value_")
						$name = preg_replace("/ub_value_/","",$name);
				}
				
				if($name)
				{
					if(isset($return[$name]))
					{
						if(!is_array($return[$name]))
							$return[$name] = array($return[$name]);
							
						$return[$name][] = $node->to_object();
					}
					else
					{
						if($node->get_type() == self::T_ARRAY && $node->is_leaf())
							$return[$name] = array($node->to_object());
						else
							$return[$name] = $node->to_object();
					}
				}
				else
					$return[] = $node->to_object();
			}
			
			if($this->type == self::T_OBJECT || ($object !== null && is_object($object)))
			{
				if($object == null)
				{
					$class = $this->value;
					$object = new $class();
				}
				
				return self::instance($object,$return);
			}
			else
				return $return;
		}else {
			return $this->value;
		}
	}
	
	/**
	 * Convierte un objecto a un nodo
	 *
	 * @param array $values
	 * @return bool
	 */
	public function object_to_node($object)
	{
		if(is_object($object))
		{
			$arr_get = preg_grep('/^'.self::$get.'/',get_class_methods(get_class($object)));
			$arr = array();
			foreach ($arr_get as $value) 
			{
				$key = preg_replace("/".self::$get."/","",$value);
				$obj_get = $object->$value();
				$node = $this->add_node($key);
				$node->object_to_node($obj_get);
			}
			
			$this->type = self::T_OBJECT;
			$this->value = get_class($object);
		}
		elseif (is_array($object))
		{
			foreach ($object as $key => $value)
			{
				if(is_numeric($key))
					$key = "_$key";
					
				$node = $this->add_node($key);
				$node->object_to_node($value);
			}
			$this->type = self::T_ARRAY;
		}
		else
		{
			if($object === "true")
				$object = true;
			elseif($object === "false")
				$object = false;
			
			$this->set_value($object);
		}
	}
	
	
	/**
	 * Convierte una porsión del array struct de un xml
	 *
	 * @param array $values
	 * @param int $i
	 * @return bool
	 */
	public function struct_to_node($values, &$i)
	{
		while (++$i < count($values)) 
		{
			switch ($values[$i]['type']) {
				case 'cdata':
					$node = $this->add_node($values[$i]['tag'],$values[$i]['value']);
				break;
				
				case 'complete':
				case 'open':
					$node = $this->add_node($values[$i]['tag']);
					$pos = $i;
					if($values[$pos]['type'] != 'complete')
						$node->struct_to_node($values,$i);
						
					if(isset($values[$pos]['value']))
					{
						if($values[$pos]['value'] === "true")
							$set_value = true;
						elseif($values[$pos]['value'] === "false")
							$set_value = false;
						else
							$set_value = $values[$pos]['value'];
							
						$node->set_value($set_value);
					}
						
					if(isset($values[$pos]['attributes']))
					{
						if($values[$pos]['attributes']['ub_class_name'])
						{
							$node->set_type(self::T_OBJECT);
							$node->set_value($values[$pos]['attributes']['ub_class_name']);
						}
						elseif($values[$pos]['attributes']['ub_is_array'])
						{
							$node->set_type(self::T_ARRAY);
						}
						elseif(isset($values[$pos]['attributes']['value']))
						{
							if($values[$pos]['attributes']['value'] === "true")
								$set_value = true;
							elseif($values[$pos]['attributes']['value'] === "false")
								$set_value = false;
							else
								$set_value = $values[$pos]['attributes']['value'];
								
							$node->set_value($set_value);
							
						}

						unset($values[$pos]['attributes']['ub_class_name']);
						unset($values[$pos]['attributes']['ub_is_array']);
						unset($values[$pos]['attributes']['value']);
					}
					
					if(!$node->is_leaf() && $node->get_type() != self::T_OBJECT)
						$node->set_type(self::T_ARRAY);
				break;

				case 'close':
					return true;
				break;
			}
		}
	}
	
	/**
	 * Convierte a una cadena que se puede guardar en cache
	 *
	 * @return string
	 */
	public function to_string($self = false, $tab = "\t")
	{
		$string = "";
		if($this->type == self::T_OBJECT)
		{
			if($self == false)
			{
				foreach ($this->nodes as $key => $node) {
					if($string !== "")
						$string .= ",";
					
					$string .= "\n";
					
					$name = $node->get_name();
					$value = $node->to_string(false,"$tab\t");
					$string .= "$tab$name => $value";
				}
				
				$string = "ubNode::instance($this->value,array($string))";
			}
			else
			{
				foreach ($this->nodes as $key => $node) 
				{
					$key = $node->get_name();
					$method = self::$set.$key;
					$string .= "\$this->$method(".$node->to_string(false,"$tab\t").");\n";
				}
			}
		}
		elseif ($this->type == self::T_ARRAY)
		{
			foreach ($this->nodes as $key => $node) {
				if($string !== "")
					$string .= ",";
				
				$name = $node->get_name();
				
				if(ereg("^_([0-9]*$)",$name,$list))
					$name = preg_replace("/_/","",$name);
					
				if(substr($name,0,9) == "ub_value_")
					$name = preg_replace("/ub_value_/","",$name);
				
				$string .= "\n";
				
				if(!is_numeric($name))
					$name = "'$name'";
					
				$value = $node->to_string(false,"$tab\t");
				$string .= "$tab$name => $value";
			}
			

			$string = "array($string)";
		}
		else
		{
			$value = $this->value;
			if(is_bool($value))
			{
				if($value)
					$value = "true";
				else
					$value = "false"	;
			}
			elseif(is_numeric($value))
				$value = $value;
			elseif ($value == "" || $value == "''")
				$value = "''";
			elseif ($value === null)
				$value = 'null';
			elseif(is_string($value))
				$value = "'$value'";
								
			$string .= $value;
		}
		
		return $string;
	}
	
	/**
	 * Devuelve el o los nodoa correspodiente a ese nombre
	 *
	 * @param string $key
	 * @return ubNode
	 */
	public function __get($key)
	{
		$nodes = self::filter_by_method($this->nodes,"get_name",$key);
		if(count($nodes) != 0)
			return $nodes;
			
		throw new Exception("No existe ningún node con ese nombre");
	}
	
	//Métodos státicos
	/**
	 * Permite crear una instancia de una clase que cumpla las reglas de 
	 * parseo
	 *
	 * @param string or object $class_or_object
	 * @param array $params
	 * @return mixed
	 */
	static public function instance($class_or_object,$params = array())
	{
		if (is_object($class_or_object))
			$object = $class_or_object;
		else
			$object = new $class_or_object();
			
		foreach ($params as $key => $value)
		{
			$method = self::$set.$key;
			if(method_exists($object,$method))
				$object->$method($value);
		}
		return $object;
	}
	
	/**
	 * Devuelve un arreglo de los elementos correspondientes a esa llave
	 *
	 * @param array $array
	 * @param string $method
	 * @param string $value
	 * @return array
	 */
	static private function filter_by_method($array,$method,$value)
	{
		self::validate($array);
		self::validate($value);
		
		$result = array();
		foreach ($array as $obj) 
		{
			if(in_array($obj->$method(),$value))
				$result[] = $obj;
		}
		return $result;
	}
	
	/**
	 * Verifica que el parámetro sea un array de lo contrario crea uno con dicho 
	 * parámetro como valor correspondiente a esa llave
	 *
	 * @param array $array
	 * @param string $key
	 * @return array
	 */
	static private function validate(&$array,$key = 0)
	{
		if(!is_array($array))
		{
			if($array !== null)
				$array = array($key => $array);
			else	
				$array = array();
		}
		
		return $array;
	}
}

/**
 * Es la raíz del parseo realizado por la clase ubNode
 *
 */
class ubRoot extends ubNode
{
	private $version = "1.0";
	private $encoding = "UTF-8";
	
	/**
	 * Constructor
	 *
	 * @param string $name
	 */
	public function __construct($name = "body")
	{
		parent::__construct($name,ubNode::T_VAR,null);
	}
	
	public function get_version()
	{
		return $this->version;
	}
	
	public function set_version($value)
	{
		$this->version = $value;
	}
	
	public function get_encoding()
	{
		return $this->encoding;
	}
	
	public function set_encoding($value)
	{
		$this->encoding = $value;
	}
	
	/**
	 * Devuelve la cadena en xml
	 *
	 * @return string
	 */
	public function to_xml()
	{
		$return = "<?xml version=\"$this->version\" encoding=\"$this->encoding\"?>";
		return $return.parent::to_xml("");
	}
        
    /**
     * Permite guardar la informaci
     * @param string $dir
     * @return bool 
     */
    public function save_as_xml($dir)
    {
        if (($fd = @fopen($dir, 'w+')))
		{
	        fwrite($fd,$this->to_xml());
			fclose($fd);
			return true;
		}else return false;
    }
	
	/**
	 * Convierte un fichero en un objecto de tipo ubRoot
	 *
	 * @param file or string $xml
	 * @return ubRoot
	 */
	static public function load_xml($xml)
	{
		if (is_file($xml)) 
		{
			if (is_file($xml))
			{
				$xml_data = file_get_contents($xml);
			}else
				$xml_data = "";
		}else
			$xml_data = $xml;
		
		$values = array();
		$index  = array();
		$parser = xml_parser_create();
		xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
		xml_parse_into_struct($parser, $xml_data, $values, $index);
		xml_parser_free($parser);
		$i = 0;
		$rb_xml = new ubRoot($values[$i]['tag']);
		
		if($values[$i]['attributes']['ub_class_name'])
		{
			$rb_xml->set_type(self::T_OBJECT);
			$rb_xml->set_value($values[$i]['attributes']['ub_class_name']);
			unset($values[$i]['attributes']['ub_class_name']);
		}
		
		$rb_xml->struct_to_node($values,$i);
		return $rb_xml;
	}
	
	/**
	 * Convierte un arreglo en un árbol de nodos
	 *
	 * @param mixed $object
	 * @param string $name
	 * @return ubRoot
	 */
	static public function load_object($object,$name = "body")
	{
		$return = new ubRoot($name);
		$return->object_to_node($object);
		return $return;
	}
}
?>
Return current item: ubNode