<?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;
}
}
?>