PHP XMLTree class
Below code was my first contact with both, PHP5 and XML technology, back in 2006. Now I can write much faster algorithm, but I'm not working with PHP anymore. Now I think the best way to handle XML in any programming language is JSON approach. JSON is subset of actual language (JavaScript) and can handle XML structure and data using only dictionaries and arrays. These structures are fast and have great amount of functions you can use with them.
XMLTree interface is similar to ordinary tree data structure. It has pointer to current element and this element have pointers to children, parent, and root elements. There is very limited XPath subset implemented so you can change current element with /root/elem[3].
This code is on GPL license so use it as you want but don't sell it.
//xml element: table of subelements and/or value
class elem {
public $tag;//tag name
public $value;//value of xml element
public $attribs;
public $subelems;//tab of elem objects
public $i;//number of subelems
private $parent=null;
function __construct($name,$parent)
{
$this->tag=$name;
$this->parent=$parent;
$this->i=0;
}
//accessors for $value
function set_value($val)//must translate special characters to &xx; symbols and translate text to utf8
{
$this->value=$val;
}
function get_value()
{
return $this->value;
}
function set_value_iso($val)//for iso-02 compatibility
{
$this->value.=$val;
}
function add_subelement($name)
{
$this->i++;
return $this->subelems[$this->i-1]=new elem($name,$this);
}
function end_element()
{
return $this->parent;
}
function add_tree($elem)
{
$this->i++;
$this->subelems[$this->i-1]=$elem;
}
function add_attribute($name,$val)
{
$this->attribs[$name]=$val;
}
/* function print($handler)
{
if ($this->i==0 && !$ref->get_value())
$handler->get('<'.$ref->tag." />\n");
else
{
$handler->get('<'.$ref->tag.">".$ref->get_value());
for($i=0;$i<$this->i;$i++)
$this->$ref->subelems[$i]->print($handler);
$handler->get("</".$ref->tag.">\n");
}
}*/
}
class XMLTree {
private $xml_obj=null;
private $curr_elem=null;
private $root_elem=null;
private $stylesheet;
function readFile($path)
{
$this->xml_obj = xml_parser_create("");
xml_set_object($this->xml_obj,$this);
xml_parser_set_option($this->xml_obj, XML_OPTION_CASE_FOLDING,0);
xml_parser_set_option($this->xml_obj, XML_OPTION_SKIP_WHITE,1);
xml_set_character_data_handler($this->xml_obj, 'dataHandler');
xml_set_element_handler($this->xml_obj, "startHandler", "endHandler");
if (!($fp = fopen($path, "r")))
{
echo "Cannot open XML data file: $path";
return false;
}
while ($data = fread($fp, 2048))
if (!xml_parse($this->xml_obj, $data, feof($fp)))
{
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->xml_obj)),
xml_get_current_line_number($this->xml_obj)));
}
xml_parser_free($this->xml_obj);
fclose ($fp);
return true;
}
//$str is string containing xml data
function read($str)
{
$this->xml_obj = xml_parser_create("");
xml_set_object($this->xml_obj,$this);
xml_parser_set_option($this->xml_obj, XML_OPTION_CASE_FOLDING,0);
xml_parser_set_option($this->xml_obj, XML_OPTION_SKIP_WHITE,1);
xml_set_character_data_handler($this->xml_obj, 'dataHandler');
xml_set_element_handler($this->xml_obj, "startHandler", "endHandler");
if (!xml_parse($this->xml_obj, $str))
{
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->xml_obj)),
xml_get_current_line_number($this->xml_obj)));
}
xml_parser_free($this->xml_obj);
return true;
}
private function startHandler($parser, $name, $attribs)
{
$this->add_element($name);
while (list($k, $v) = each($attribs))
{
$v= str_replace("'", "'", str_replace("&", "&", $v));
$this->add_attribute($k,$v);
}
}
private function dataHandler($parser, $data)//i dont know why in iso-02 this function is
//called twice. one for text from 0 to first iso-02 speciall letter, one for rest separated by
//<br/> causing use of .= operator and special function set_value_iso in elem class
{
//chop: cause XML_OPTION_SKIP_WHITE dont work(?)
$data= chop(str_replace("&", "&", $data));
if ($data!="")
$this->curr_elem->set_value_iso($data);
}
private function endHandler($parser, $name)
{
$this->parent();
}
private function print_all_recursive(&$ref,&$handler)
{
$handler->get('<'.$ref->tag);
if ($ref->attribs)
while (list($k, $v) = each($ref->attribs))
$handler->get(" ".$k."='".$v."'");
if ($ref->i==0 && !isset($ref->value))
$handler->get(" />\n");
else
{
$handler->get(">".$ref->get_value());
for($i=0;$i<$ref->i;$i++)
$this->print_all_recursive($ref->subelems[$i],$handler);
$handler->get("</".$ref->tag.">\n");
}
}
// $handler is an object having the "get" function that gets parts of xml file and do something with it
function print_all($handler)
{
$handler->get( '<?xml version="1.0" encoding="UTF-8"?>'."\n");
if ($this->stylesheet)
$handler->get( '<?xml-stylesheet type="text/xsl" href="'.$this->stylesheet.'"?>'."\n");
$handler->get($this->print_all_recursive($this->root_elem,$handler));
}
private function path_spliter($path)
{
if ($path=="/")
return $this->root_elem;
elseif ($path[0]=="/")
{
$curr_elem=$this->root_elem;
$path=substr($path,1,strlen($path)-1);
}
else
$curr_elem=$this->curr_elem;
$arr=explode("/",$path);
foreach ($arr as $tag)
{
$instance=1;
if ($tag && $tag[strlen($tag)-1]=="]")//there is number of node in the path; default is 1(first)
{
$pos=strrpos($tag,"[");
$pos2=strrpos($tag,"]");
$instance=substr($tag,$pos+1,strlen($tag)-$pos-2);
$tag=substr($tag,0,$pos);
}
$found=false;
for ($i=0,$k=1;$i<$curr_elem->i && $found==false;$i++)
if ($curr_elem->subelems[$i]->tag==$tag)
{
if ($k==$instance)
{
$found=true;
$curr_elem=&$curr_elem->subelems[$i];
break;
}
$k++;
}
}
if (!$found)
return false;
else
return $curr_elem;
}
function set_root($path)
{
if ($p=$this->path_spliter($path))
$this->curr_elem=$p;
}
//similar to xpath getting value of tag specified by $path (eg. /path/to/something[3])
function get_xpath_val($path)
{
$node=$this->path_spliter($path);
if ($node)
return $node->get_value();
else return false;
}
function add_element($name)
{
if ($this->root_elem==null)
{
$this->root_elem=new elem($name,null);
$this->curr_elem=$this->root_elem;
}
else
$this->curr_elem=$this->curr_elem->add_subelement($name);
}
function set_value($val)
{
$this->curr_elem->set_value($val);
}
function get_value()
{
return $this->curr_elem->get_value();
}
function add_tree($node)
{
if ($node)
$this->curr_elem->add_tree($node);
}
function parent()
{
$this->curr_elem=$this->curr_elem->end_element();
}
function set_stylesheet($style)
{
$this->stylesheet=$style;
}
function get_root()
{
return $this->root_elem;
}
function add_subelement($name,$value)
{
$this->add_element($name);
$this->set_value($value);
$this->parent();
}
function add_attribute($name,$val)
{
$this->curr_elem->add_attribute($name,$val);
}
}
And here is simple usage of this class:
class form
{
private $xml;
function __construct($target)
{
$this->xml=new XMLTree();
$this->xml->add_element("form");
$this->xml->add_attribute("name","form");
$this->xml->add_attribute("method","post");
$this->xml->add_attribute("action",$target);
}
function add_text($show,$name,$value)
{
$this->xml->add_subelement("text",$show);
$this->xml->add_element("input");
$this->xml->add_attribute("name",$name);
$this->xml->add_attribute("size","20");
$this->xml->add_attribute("value",$value);
$this->xml->parent();
}
function add_textarea($show,$name,$value)
{
$this->xml->add_element("textarea");
$this->xml->add_attribute("name",$name);
$this->xml->add_attribute("rows","10");
$this->xml->add_attribute("cols","100");
$this->xml->set_value($value);
$this->xml->parent();
}
function add_select($show,$values,$names,$selected)
{
$this->xml->add_subelement("text",$show);
$this->xml->add_element("select");
$this->xml->add_attribute("name",$name);
while (list($k, $v) = each($values))
{
$this->xml->add_element("option");
$this->xml->add_attribute($k,$v);
if ($selected==$k)
$this->xml->add_attribute("selected","selected");
$this->xml->parent();
}
$this->xml->parent();
}
function add_hidden($name,$value)
{
$this->xml->add_element("input");
$this->xml->add_attribute("type","hidden");
$this->xml->add_attribute("name",$name);
$this->xml->add_attribute("value",$value);
$this->xml->parent();
}
function add_password($show,$name)
{
$this->xml->add_subelement("text",$show);
$this->xml->add_element("input");
$this->xml->add_attribute("type","password");
$this->xml->add_attribute("name",$name);
$this->xml->parent();
}
function add_other($text)
{
$this->xml->add_subelement("text",$text);
}
function add_error($text)
{
$this->xml->add_subelement("error",$text);
}
function add_submit($show,$name)
{
$this->xml->add_element("input");
$this->xml->add_attribute("type","submit");
$this->xml->add_attribute("name",$name);
$this->xml->add_attribute("value",$show);
$this->xml->parent();
}
function get_form()
{
return $this->xml->get_root();
}
}
?>
