<?php
/*Khalid XML files parser :: class kxparse, Started in March 2002 by Khalid Al-kary
-- Version 1.2 based on 1.1-dev2
*/
class kxparse{
var $xml;
var $cursor;
var $cursor2;
//the variable that carries name space support
var $namespace;
//the variables that holds the current CDATA areas
var $invalid_area;
//the variable that holds if CDATA sections are supported
var $CD;
//the variable that holds the start of CDATA in the invalid area array
var $stop;
//the cache
var $cache;
//the chaching support
var $csupport;
//the constructor $xmlfile is the file you want to load into the parser
function kxparse($xmlfile)
{
//just read the file
$file=fopen($xmlfile,"r");
//put the text inside the file in the XML object variable
while (!feof($file))
{
$this->xml.=fread($file,4096);
}
//close the opened file
fclose($file);
//set the cursor to 0 (start of document), the cursor is later used by another functions
$this->cursor=0;
//set the second curosr to the end of document
$this->cursor2=strlen($this->xml);
//set name spaces support to default (true)
$this->namespace=true;
//enable CDATA support
$this->CD=true;
//enable caching
$this->csupport=true;
}
/*this function first gets a copy of the XML file starting from cursor and ending with cursor2
and then counts the number of occurences of the given tag name inside that area
returns an array (occurrence index -> occurence position in the XML file)
this function is half of the engine that moves Kxparse */
function track_tag_cursors($tname)
{
//getting the copy as intended
$currxml=substr($this->xml,$this->cursor,$this->cursor2-$this->cursor);
//counting the number of occurences in the cut area
$occurs=substr_count($currxml,"<".$tname.">");
$occurs+=substr_count($currxml,"<".$tname." ");
$occurs+=substr_count($currxml,"<".$tname."/");
//fix of the tag selection confusion bug
$this->get_invalid_area($tname);
//the aray that will be returned
$tag_poses=array();
//setting its 0 to 0 because indeces in Kxparse start from 1
$tag_poses[0]=0;
//for each of the occurences
for ($i=1;$i<=$occurs;$i++)
{
if ($i!=1)
{
//fixed the bug of confusion between similar tag names
$separ=null;
$place=0;
do {
$pos=strpos($currxml,"<".$tname,($place==0) ? $tag_poses[$i-1]+1-$this->cursor : $place);
$separ=substr($currxml,$pos+strlen($tname)+1,1);
$place+=$pos+1;
} while ($separ!=" " && $separ!=">" && $separ!="/");
//if it's not the first occurence
//start checking for the next occurence but first cut the previous occurences off from the string
$tag_poses[$i]=strpos($currxml,"<".$tname.$separ,$tag_poses[$i-1]+1-$this->cursor)+$this->cursor;
}
else
{
//fixed the bug of confusion between similar tag names
$separ=null;
$place=0;
do {
$pos=strpos($currxml,"<".$tname,$place);
$separ=substr($currxml,$pos+strlen($tname)+1,1);
$place+=$pos+1;
} while ($separ!=" " && $separ!=">" && $separ!="/");
//if its the first occurence just assign its value + the cursor (because the position is in the XML file wholly
//but first you mst know if the tag is ended with > or " " to avoid confusion
$tag_poses[$i]=strpos($currxml,"<".$tname.$separ)+$this->cursor;
}
}
//in case that there's invalid areas'
if (count($this->invalid_area)>0) {
//the array that holds the final result
$res=array();
//the number of god elements
$ecount=0;
//fix the tag selection confusion bug
//check if the returned result contains positions within the invalid areas
for ($i=0;$i<count($tag_poses);$i++)
{
//loop through the invalid areas
for ($b=0;$b<count($this->invalid_area);$b++)
{
//if the position was found within any of them
//terminate the loop and don't add it to the resulting array'
if ($tag_poses[$i]>=$this->invalid_area[$b][0] && $tag_poses[$i]<=$this->invalid_area[$b][1])
{
break;
}
//if the position wasn't found and this is the last invalid area
//add the position to the array
if (($tag_poses[$i]<$this->invalid_area[$b][0] || $tag_poses[$i]>$this->invalid_area[$b][1]) && $b==(count($this->invalid_area)-1))
{
$res[$ecount]=$tag_poses[$i];
$ecount++;
}
}
}
//return that array
return $res;}
else
{return $tag_poses;}
}
//this function strips and decodes the tag text...
function get_tag_text_internal($tname)
{
//strip the tags from the returned text and the decode it
return $this->htmldecode(strip_tags($tname));
}
//function that returns a particular attribute value ...
//tag is the tag itself(with its start and end)
function get_attribute_internal($tag,$attr,$has)
{
if (strpos($tag," ")<strpos($tag,">"))
{
$separ=" ";
}
else
{
$separ=">";
}
//cutting of the tag name according to separ
$tname=substr($tag,1,strpos($tag,$separ)-1);
//cut the tag starting from the white space after the tag name, ending with(not containing) the > of the tag start
$work=substr($tag,strlen($tname)+1,strpos($tag,">")-strlen($tname)-1);
//work with the function has_attribute
if ($has==true)
{
if (is_integer(strpos($work," ".$attr."=\"")))
{
return true;
}
else
{
return false;
}
}
else
{
//get the index of the tag occurence inside $work
$index_of_attr=strpos($work," ".$attr."=\"")+1;
//check if the attribute was found in the tag
if ($index_of_attr)
{
//now get the attributename+"=""+attrbutevalue+""" and extract the value from between them
//calculate from where we will cut
$index_of_value=$index_of_attr+strlen($attr)+2;
//cut note the last argument for calculating the end
$work=substr($work,$index_of_value,strpos($work,"\"",$index_of_value)-$index_of_value);
//now return the attribute value
return $work;
}
//if the attribute wasn't found, return false'
else
{
return FALSE;
}
}
}
//this function HTML-decodes the var $text...
function htmldecode($text)
{
$text=str_replace("&lt;","<",$text);
$text=str_replace("&gt;",">",$text);
$text=str_replace("&amp;","&",$text);
$text=str_replace("&ltt;","&lt;",$text);
$text=str_replace("&gtt;","&gt;",$text);
$text=str_replace("&qout;","\"",$text);
$text=str_replace("&apos;","'",$text);
return $text;
}
//a function for encoding from HTML
function htmlencode($text,$fname)
{
$text=str_replace("<","&lt;",$text);
$text=str_replace(">","&gt;",$text);
$text=str_replace("&","&amp;",$text);
$text=str_replace("&lt;","&ltt;",$text);
$text=str_replace("&gt;","&gtt;",$text);
if ($fname=="set_attribute") {$text=str_replace("\"","&qout;",$text);
$text=str_replace("'","&apos;",$text);
}
return $text;
}
//the function that saves a file to a particular location
function save($file)
{
//open the file and overwrite of already avilable
$my_file=fopen($file,"wb");
//$my_status holds wether the operation is okay
$my_status=fwrite($my_file,$this->xml);
//close the file handle
fclose($my_file);
if ($my_status!=-1)
{
return TRUE;
}
else
{
return FALSE;
}
}
//function that gets a tag in the XML tree (with its starting and ending)
function get_tag_in_tree($tname,$tindex)
{
$this->get_work_space($tname,$tindex);
return substr($this->xml,$this->cursor,$this->cursor2-$this->cursor);
}
//function that gets the text of a tag
function get_tag_text($tname,$tindex)
{
$mytag=$this->get_tag_in_tree($tname,$tindex);
if ($this->CD)
return $this->extract_CD($mytag,(is_integer(strpos($tname,":"))) ? (substr($tname,strrpos($tname,":")+1)) : ($tname));
else
return $this->get_tag_text_internal($mytag);
}
//funtion that counts the number of occurences of a tag in the XML tree
function count_tag($tname,$tindex)
{
return $this->get_work_space($tname,$tindex);
}
//functoin that gets the attribute value in a tag
function get_attribute($tname,$tindex,$attrname)
{
$mytag=$this->get_tag_in_tree($tname,$tindex);
return $this->get_attribute_internal($mytag,$attrname,false);
}
//function for explod+name space support
function explode_collect($explosive,$explosion)
{
//first normally explode the given var
$myarr=explode($explosive,$explosion);
//pass the returned to collect_name_space
return $this->collect_name_space($myarr);
}
//function for name spaces support
function collect_name_space($arr)
{
$arrcount=count($arr);
//the array that will hold the output
$rarr=array();
//counter for the inserted values
$ecount=0;
//loop through the array elements to find (name:spaces)
for ($i=0;$i<$arrcount;$i++)
{
//if found a name space
if (is_integer(strpos($arr[$i],"(")))
{
//add to the current element in $rarr the element that contained the name space and the element after it
//also cut the "(" and the ")"
$rarr[$ecount]=substr($arr[$i],1).":".substr($arr[++$i],0,strlen($arr[$i])-1);
$ecount++;
}
//if not found a names space just add the same thing to the new array
else
{
$rarr[$ecount]=$arr[$i];
$ecount++;
}
}
return $rarr;
}
//Very important function, half of the engine
//sets the $this->cursor and $this->cursor2 to the place where it's intended to work
function get_work_space($tname,$tindex)
{
//counts the number of ":" in the given colonedtagindex
$num_of_search=substr_count($tindex,":");
//now get the numbers in an array
$search_array=explode(":",$tindex);
//and also get the corresponding tag names
$search_text_array=($this->namespace) ? ($this->explode_collect(":",$tname)) : (explode(":",$tname));
//print_r($search_text_array);
//checks if they are not equal this regarded an error
if (count($search_array)!=count($search_text_array))
{
return false;
}
else
{
//set the cursor to 0 in order to erase former work
$this->cursor=0;
//set the cursor2 to the end of the file for the same reason
$this->cursor2=strlen($this->xml);
//the loop according to number of ":"
for ($i=0;$i<count($search_array);$i++)
{
//caching support
if ($this->csupport)
{
if (isset($this->cache["$this->cursor,$this->cursor2,$search_text_array[$i]"]))
{
//get the result from the chache instead of track_tag_cursors
$arr=$this->cache["$this->cursor,$this->cursor2,$search_text_array[$i]"];
}
else
{
//$arr holds the number of occurences of the current tag name between the cursor and cursor2
$arr=$this->track_tag_cursors($search_text_array[$i]);
$this->cache["$this->cursor,$this->cursor2,$search_text_array[$i]"]=$arr;
}
}
else
{
//$arr holds the number of occurences of the current tag name between the cursor and cursor2
$arr=$this->track_tag_cursors($search_text_array[$i]);
}
//the index which you want to get the position of
$tem=$search_array[$i];
//to support count_tag_in_tree
//when given a ? it returns the number of occurences of the current tag name
if ($tem=="?")
{
return count($arr)-1;
}
else {
//to support the auto-last method
//if the current tag index equals "-1" so replace it by the last occurence index
if ($tem==-1)
{
$tem=count($arr)-1;
}
//now just set cursor one to the occurence position in the XML file accrding to $tem
$this->cursor=$arr[(int)$tem];
//carry the values for faster excution
$pos1=strpos($this->xml,"/>",$this->cursor);
$pos2=strpos($this->xml,">",$this->cursor);
//if not found set to $pos2+1
$pos1=($pos1) ? ($pos1) : ($pos2+1);
//check if the tag is internally closed
if ($pos1<$pos2)
{
//if internally closed
$this->cursor2=$pos1+2;
}
else
{
$br=true;
$co=count($this->invalid_area);
if ($co-1-$this->stop>0) {
//and set cursor2 at the end of that tag note support for internal tag closing
$pos=$this->cursor;
do {
$pos=strpos($this->xml,"</".$search_text_array[$i].">",$pos+1);
//check if the end is contained within an invalid area
for ($b=$this->stop;$b<$co;$b++)
{
if ($pos>=$this->invalid_area[$b][0] && $pos <=$this->invalid_area[$b][1])
{
break;
}
if (($pos<$this->invalid_area[$b][0] || $pos>$this->invalid_area[$b][1]) && $b==$co-1)
{
$br=false;
break;
}
}
} while($br);
//set the cursor to the safe position now
$this->cursor2=$pos+strlen("</".$search_text_array[$i].">");}
else {
$this->cursor2=strpos($this->xml,"</".$search_text_array[$i].">",$this->cursor)+strlen("</".$search_text_array[$i].">");
}
}
}
}
}
}
//the function that appends a tag to the XML tree
function create_tag($tname,$tindex,$ntname)
{
//check for validity of the tag name
$this->check_tag_name($ntname,"create_tag");
//first get the intended father tag
$this->get_work_space($tname,$tindex);
//explode the given colonedtagname into an array
$search_text_array=explode(":",$tname);
//get the current tag name
$tagname=$search_text_array[count($search_text_array)-1];
//after setting the cursors using get_work_space
//get a cope of the returned tag
$workarea=substr($this->xml,$this->cursor,$this->cursor2-$this->cursor);
//check if the tag is internally closed
if ($this->is_internal_close($workarea))
{
//first get the replacment index
$close_index=strlen($workarea)-2;
//replace
$workarea=substr_replace($workarea,"></".$tagname.">",$close_index,2);
//change the xml tree to be the same
$this->xml=substr_replace($this->xml,$workarea,$this->cursor,$this->cursor2-$this->cursor);
}
//calculate the place where you will put the tag start and end
$inde=$this->cursor+strpos($workarea,"</".$tagname.">");
//here, replace means insert because the length argument is set to 0
$this->xml=substr_replace($this->xml,"<".$ntname."/>",$inde,0);
$this->empty_cache();
}
//the function that sets the value of an attribute
function set_attribute($tname,$tindex,$attr,$value)
{
//chech the attribute name for invalid insertion
$this->check_tag_name($attr,"set_attribute");
//replace qoutes with &qout;
$value=$this->htmlencode($value,"set_attribute");
//first set the cursors using get_work_space
$this->get_work_space($tname,$tindex);
//now get a copy of the XML tag between cursor and cursor2
$currxml=substr($this->xml,$this->cursor,$this->cursor2-$this->cursor);
//cut the area of the tag on which you want to work
//starting from the tag "<" and ending with the opening tag ">"
$work=substr($currxml,0,strpos($currxml,">")+1);
//if the attribute is already available
if (strpos($work," ".$attr."=\""))
{
//calculate the current value's length
$currval_length=strlen($this->get_attribute_internal($currxml,$attr,false));
//get the position of the attribute inside the tag
$my_attribute_pos=strpos($work," ".$attr."=\"")+1;
//get the length of the attribute
$my_attribute_length=strlen($attr);
//now replace the old value
$this->xml=substr_replace($this->xml,$value,$this->cursor+$my_attribute_pos+$my_attribute_length+2,$currval_length);
}
//if the attribute wasn't already available'
else
{
//check if there are other attributes in the tag
if (is_integer(strpos($work," ")))
{
$separ=" ";
}
else
{
if (strpos($work,"/")<strpos($work,">") && is_integer(strpos($work,"/")))
$separ="/";
else
$separ=">";
}
//prepare the attribute
$newattr=" ".$attr."=\"".$value."\"";
//insert the new attribute
$this->xml=substr_replace($this->xml,$newattr,$this->cursor+strpos($work,$separ),0);
}
$this->empty_cache();
}
//the function that changes or adds the text of a tag
function set_tag_text($tname,$tindex,$text)
{
//HTML encode first
$text=$this->htmlencode($text,"set_tag_text");
//firs get set the cursors using get_work_space
$this->get_work_space($tname,$tindex);
//explode the given colonedtagname in an array
$search_text_array=explode(":",$tname);
//get the latest name
$currtname=$search_text_array[count($search_text_array)-1];
//the whole tag
$workarea=substr($this->xml,$this->cursor,$this->cursor2-$this->cursor);
//check if the tag is internally closed
if ($this->is_internal_close($workarea))
{
//first get the replacment index
$close_index=strlen($workarea)-2;
//replace
$workarea=substr_replace($workarea,"></".$currtname.">",$close_index,2);
//change the xml tree to be the same
$this->xml=substr_replace($this->xml,$workarea,$this->cursor,$this->cursor2-$this->cursor);
}
//calculate the start of replacement
$replace_start_index=strpos($this->xml,">",$this->cursor)+1;
//calculate the end of replacement
$replace_end_index=strpos($this->xml,"</".$currtname.">",$this->cursor)-1;
//calculate the length between them
$tem=$replace_end_index-$replace_start_index+1;
//and now replace
$this->xml=substr_replace($this->xml,$text,$replace_start_index,$tem);
$this->empty_cache();
}
//functio that removes a tag
function remove_tag($tname,$tindex)
{
//set the cursors using get_work_space
$this->get_work_space($tname,$tindex);
//now replace with ""
$this->xml=substr_replace($this->xml,"",$this->cursor,$this->cursor2-$this->cursor);
$this->empty_cache();
}
//function that sets the processing instructions of the XML file
function set_pi($name,$value)
{
//locate the PI location first
$pi_pos=strpos($this->xml,"<?xml");
//get the end of the PI
$pi_end=strpos($this->xml,"?>")+2;
//cut this area
$pi_tag=substr($this->xml,$pi_pos,$pi_end-$pi_pos);
//if there's an attribute of the pi that's already available
if (strpos($pi_tag," ".$name."=\""))
{
//get the attribute position
$attr_pos=strpos($pi_tag," ".$name."=\"")+$pi_pos;
//tell the place of replacement
$attr_pos+=strlen($name)+3;
//get the length of the current value
$val_len=strlen(substr($this->xml,$attr_pos,strpos($this->xml,"\"",$attr_pos)-$attr_pos));
//now just replace
$this->xml=substr_replace($this->xml,$value,$attr_pos,$val_len);
}
//if there was no attribute in the pi with the same name
else
{
//the place where i'll insert the attribute
$insert_pos=$pi_end-2;
//now just insert
$this->xml=substr_replace($this->xml," ".$name."=\"".$value."\" ",$insert_pos,0);
}
$this->empty_cache();
}
//function that loads a XML file into the parser and removes the current content
function load($xmlfile)
{
//empty the current content
$this->xml="";
//get the current value on namespace support
$val=$this->namespace;
//reload using the class constructor
$this->kxparse($xmlfile);
//add the same value of name space support before loading
$this->namespace=$val;
//empty the cache
$this->empty_cache();
}
//function that searches for a tag whose text matches a query
function search_tags($tagname,$tagindex,$text)
{
//count the number of tags under the specified parent
$count=$this->count_tag($tagname,$tagindex);
//the array that will be returned
$arr=array();
//holds the number of matches
$vals=0;
//loop to check for the required tags
for ($i=1;$i<=$count;$i++)
{
//put the number of the tags instead of the ?
$currtagindex=str_replace("?",$i,$tagindex);
//if the text of the tag equals the given text fill the array with it
if (trim($this->get_tag_text($tagname,$currtagindex))==trim($text))
{
$arr[$vals++]=$i;
}
}
//if no results matched return false
if (count($arr)==0)
{
return false;
}
else
{
return $arr;
}
}
//identical to search_tags but case-insensitive
function isearch_tags($tagname,$tagindex,$text)
{
//count the number of tags under the specified parent
$count=$this->count_tag($tagname,$tagindex);
//the array that will be returned
$arr=array();
//holds the number of matches
$vals=0;
//loop to check for the required tags
for ($i=1;$i<=$count;$i++)
{
//put the number of the tags instead of the ?
$currtagindex=str_replace("?",$i,$tagindex);
//if the text of the tag equals the given text fill the array with it
if (trim(strtoupper($this->get_tag_text($tagname,$currtagindex)))==trim(strtoupper($text)))
{
$arr[$vals++]=$i;
}
}
//if no results matched return false
if (count($arr)==0)
{
return false;
}
else
{
return $arr;
}
}
//function that searches for a particular tag that contains a particular attribute
function search_attributes($tagname,$tagindex,$attrname,$attrvalue)
{
//count the number of tags under the specified parent
$count=$this->count_tag($tagname,$tagindex);
//the array that will be returned
$arr=array();
//holds the number of matches
$vals=0;
//loop to check for the required tags
for ($i=1;$i<=$count;$i++)
{
//put the number of the tags instead of the ?
$currtagindex=str_replace("?",$i,$tagindex);
//if the text of the tag equals the given text fill the array with it
if (@trim($this->get_attribute($tagname,$currtagindex,$attrname))==trim($attrvalue))
{
$arr[$vals++]=$i;
}
}
//if no results matched return false
if (count($arr)==0)
{
return false;
}
else
{
return $arr;
}
}
//like search_attributes but case-insensitive
function isearch_attributes($tagname,$tagindex,$attrname,$attrvalue)
{
//count the number of tags under the specified parent
$count=$this->count_tag($tagname,$tagindex);
//the array that will be returned
$arr=array();
//holds the number of matches
$vals=0;
//loop to check for the required tags
for ($i=1;$i<=$count;$i++)
{
//put the number of the tags instead of the ?
$currtagindex=str_replace("?",$i,$tagindex);
//if the text of the tag equals the given text fill the array with it
if (@trim(strtoupper($this->get_attribute($tagname,$currtagindex,$attrname)))==trim(strtoupper($attrvalue)))
{
$arr[$vals++]=$i;
}
}
//if no results matched return false
if (count($arr)==0)
{
return false;
}
else
{
return $arr;
}
}
//function that checks wether an attribute is available in the tag
function has_attribute($tname,$tindex,$attrname)
{
$mytag=$this->get_tag_in_tree($tname,$tindex);
return $this->get_attribute_internal($mytag,$attrname,true);
}
//function for collection of qouts inside an exploded string array
function collect_qoutes($arr)
{
//the resulting (cllected) array
$res=array();
//the current number of elements
$ecount=0;
//loop through the array elements
for ($i=0;$i<count($arr);$i++)
{
$pos=strpos($arr[$i],"=\"");
//if found the start of an atttribute
if (is_integer($pos) && !is_integer(strpos($arr[$i],"\"",$pos+2)))
{
//the string that will carry the final collection
$collect=$arr[$i];
//looop until finding the end of the attribute and then add them wholly in only one elemnt
for ($b=$i+1;$b<count($arr);$b++)
{
$collect.=" ".$arr[$b];
//increase the original loop var by 1
$i++;
//if found the end of the attribute break the loop
if (is_integer(strpos($arr[$b],"\"")))
{
//finally add the collected string into one array element
$res[$ecount]=$collect;
$ecount++;
break;
}
}
}
else
{
$res[$ecount]=$arr[$i];
//echo $res[$ecount];
$ecount++;
}
}
return $res;
}
//function that returns an array containing attribute->value pairs
function get_attributes($tname,$tindex)
{
//get the tag with its start and end
$tag=$this->get_tag_in_tree($tname,$tindex);
//get the area of work
$work=substr($tag,0,strpos($tag,">"));
//now explode with " " to get the attriutes and their values in an array
$attrarr=explode(" ",$work);
//the fix for the spaces bug
$attrarr=$this->collect_qoutes($attrarr);
//the return array
$rearr=array();
//loop through them
for ($i=0;$i<count($attrarr);$i++)
{
//check if this is an attribute
if (is_integer(strpos($attrarr[$i],"=\"")))
{
//get the attribute name
$attname=substr($attrarr[$i],0,strpos($attrarr[$i],"=\""));
//calculate the start of cutting to get the value
$val=strpos($attrarr[$i],"=\"")+2;
//cut
$attvalue=substr($attrarr[$i],$val);
//remove the last double qoute
$attvalue=substr($attvalue,0,strpos($attvalue,"\""));
//just assign to the array
$rearr[$attname]=$attvalue;
}
}
//now just return the array
return $rearr;
}
//function for diabling and enabling namespaces support
function set_ns($val)
{
$this->namespace=$val;
}
//function that checks for the validity of a tag name (Later Added: or an attribute name)
function check_tag_name($ntname,$fname)
{
$text=($fname=="create_tag") ? ("Tag name") : ("Attribute name");
//tag names must not contain spaces
if (is_integer(strpos($ntname," ")))
{
die("Error: Kxparse: $fname: $text contains space(s)");
}
//check if the name starts with xml, XML, Xml, xMl, xmL, xML, XmL, XMl :)
if (strpos(strtoupper($ntname),"XML")==0 && is_integer(strpos(strtoupper($ntname),"XML")))
{
die("Error: Kxparse: $fname: $text starts with \"xml\"");
}
//check for inavlid characters in the tag name
if (is_integer(strpos($ntname,"?")) || is_integer(strpos($ntname,"/")) || is_integer(strpos($ntname,"&")) || is_integer(strpos($ntname,"~")) || is_integer(strpos($ntname,"!")) || is_integer(strpos($ntname,"@")) || is_integer(strpos($ntname,"#")) || is_integer(strpos($ntname,"$")) || is_integer(strpos($ntname,"%")) || is_integer(strpos($ntname,"^")) || is_integer(strpos($ntname,"*")) || is_integer(strpos($ntname,"(")) || is_integer(strpos($ntname,")")) || is_integer(strpos($ntname,"{")) || is_integer(strpos($ntname,"}")) || is_integer(strpos($ntname,"+")) || is_integer(strpos($ntname,"\\")) || is_integer(strpos($ntname,",")) || is_integer(strpos($ntname,";")) || is_integer(strpos($ntname,"`")) || is_integer(strpos($ntname,"\"")) || is_integer(strpos($ntname,"'")))
{
die("Error: Kxparse: $fname: $text contains an invalid character");
}
//if no namespace support don't allow adding namespaces
if ($fname=="create_tag") {
if (!$this->namespace)
{
if (is_integer(strpos($ntname,":")))
{
die("Error: Kxparse: $fname: Must enable namespace support to accept a namespace in the tag name");
}
}}
}
//check if the tag is internally closed
function is_internal_close($tag)
{
//carry the values for faster excution
$pos1=strpos($tag,"/>");
$pos2=strpos($tag,">");
//if not found set to $pos2+1
$pos1=($pos1) ? ($pos1) : ($pos2+1);
if ($pos1<$pos2)
{
return true;
}
else
{
return false;
}
}
//function to identify the areas that mustn't be chcked for tag avilablity
function get_invalid_area($atname)
{
//first cut the are betwee cursors
$cuxml=substr($this->xml,$this->cursor+1,$this->cursor2-$this->cursor+1);
//first check for direct children not with the same name
//calculate the nubmer of "<" in the cut area
$tnum=substr_count($cuxml,"<");
//the starting position
$pos=0;
//the resulting array
$this->invalid_area=array();
$ecount=0;
//loop through the number of occurences
for ($i=1;$i<=$tnum;$i++)
{
//first get the place of the tag
$pos=strpos($cuxml,"<",$pos+1);
//cut the place from the position until the ">"
if (substr($cuxml,$pos+1,1)!="/")
$cut=substr($cuxml,(substr($cuxml,$pos,1)=="<") ? ($pos+1) : ($pos),strpos($cuxml,">",$pos)-1-$pos);
else
continue;
//now get the tag name
$pos2=strpos($cut," ");
if (is_integer($pos2))
{
$tname=substr($cut,0,$pos2);
}
else
{
$tname=$cut;
}
$pos3=strpos($tname,"/");
if (is_integer($pos3))
{
$tname=substr($tname,0,$pos3);
}
//now check if it's of our tag name
if ($tname!=$atname)
{
//file the result array
$this->invalid_area[$ecount]=array();
$this->invalid_area[$ecount][0]=$pos+$this->cursor;
$pos3=strpos($cuxml,"</".$tname.">",$pos);
$pos4=strpos($cuxml,"/>",$pos);
$pos4=(is_integer($pos4)) ? ($pos4) : ($pos3+1);
$this->invalid_area[$ecount][1]=($pos4<$pos3) ? ($pos4+3+$this->cursor+1) : ($pos3+strlen($tname)+3+$this->cursor+1);
$ecount++;
}
}
$this->stop=($ecount<=0) ? $ecount : $ecount-1;
if ($this->CD)
$this->locate_CD();
}
//function that sets the attributes of a tag from an array (attribute->value)
function set_attributes($tname,$tindex,$arr)
{
$keys=array_keys($arr);
//loop through the array
for ($i=0;$i<count($keys);$i++)
{
//set the attribute for each array element
$this->set_attribute($tname,$tindex,$keys[$i],$arr[$keys[$i]]);
}
}
//function that finds out positions of CDATAs between the two cursors
function locate_CD()
{
//cut the xml content
$currxml=substr($this->xml,$this->cursor,$this->cursor2-$this->cursor);
//locate the number of CDATAS
$num=substr_count($currxml,"<![CDATA[");
//the current position
$pos=0;
//the array that will be filled
$co=count($this->invalid_area)-1;
//loop through the number
for ($i=1;$i<=$num;$i++)
{
//get the position
$pos=strpos($currxml,"<![CDATA[",$pos+1);
//the position of the end
$endpos=strpos($currxml,"]]>",$pos);
//echo $endpos;
$this->invalid_area[$co]=array();
$this->invalid_area[$co][0]=$pos+$this->cursor;
if (is_integer($endpos)) {
$this->invalid_area[$co][1]=$endpos+3+$this->cursor;
$co++;
}
else
die("Error: Kxparse: locate_CD: A CDATA section that has no closing tag was found");
}
}
//function that enables/disables CDATA support
function set_CD($to)
{
$this->CD=$to;
$this->empty_cache();
}
//function which gives special treatment to tags containing CDATA
//for example: doesn't strip the tags inside the CDATA section
function extract_CD($tag,$tname)
{
//if the tag is inetrnaly closed ofcourse it won't contain text! :)
if ($this->is_internal_close($tag))
{
return "";
}
else
{
$cdarr=$this->get_CD_in($this->cursor,$this->cursor2);
//the resulting string
$rstr="";
//loop through the returned CDATAs
//<= for the section that' after the last CDATA
for ($i=0;$i<=count($cdarr);$i++)
{
//if it the first CDATA in tag
if ($i==0)
$startindex=strpos($tag,">")+1;
else
$startindex=$cdarr[$i-1][1]+1;
//if it's not the last text
if ($i<count($cdarr)) {
//cut the space before the current CDATA and add it to the result string
$num=$cdarr[$i][0]-$startindex;
if ($num>0)
$rstr.=$this->htmldecode(strip_tags(substr($tag,$startindex,$num)));
//now add the CDATA section stripping the CDATA tags
$rstr.=substr($tag,$cdarr[$i][0]+9,$cdarr[$i][1]-3-($cdarr[$i][0]+9));}
else {
$val=strpos($tag,"</".$tname,$startindex)-$startindex;
//echo $startindex;
if ($val>0)
$rstr.=$this->htmldecode(strip_tags(substr($tag,$startindex,$val)));
}
}
return $rstr;
}
}
//function that returns the CDATA(s) between indexes
function get_CD_in($start,$end)
{
//the result array
$result=array();
//element count
$ecount=0;
//loop through the global CDATA(s)
for ($i=$this->stop;$i<count($this->invalid_area)-1;$i++)
{
//if between the indexes fill the array
if ($this->invalid_area[$i][0]>$start && $this->invalid_area[$i][1]<$end)
{
$result[$ecount]=array();
$result[$ecount][0]=$this->invalid_area[$i][0]-$start;
$result[$ecount][1]=$this->invalid_area[$i][1]-$start;
$ecount++;
}
}
return $result;
}
//function that combinates a CDATA section of text
function create_CD($text)
{
if (is_integer(strpos($text,"<![CDATA[")) || is_integer(strpos($text,"]]>")))
{
die("Error: Kxparse: create_CD: A CDATA section cannot contain an opening or closing symbol of CDATA");
}
return "<![CDATA[".$text."]]>";
}
//function that turns the tag into an internally closed tag
function to_internal_close ($tname,$tindex)
{
$mytag=$this->get_tag_in_tree($tname,$tindex);
//check if the tag is already internally closed
if ($this->is_internal_close($mytag))
{
return true;
}
//check if the tag contains text
if (strpos($mytag,"</")-strpos($mytag,">")>1)
{
return false;
}
//get the current attributes of the tag
$att=$this->get_attributes($tname,$tindex);
//print_r($atts);
$cutname=(is_integer(strpos($tname,":"))) ? (substr($tname,strrpos($tname,":")+1)) : ($tname);
$this->xml=substr_replace($this->xml,"<".$cutname."/>",$this->cursor,$this->cursor2-$this->cursor);
$this->set_attributes($tname,$tindex,$att);
$this->empty_cache();
}
//function that changes caching support
function set_cache($to)
{
$this->csupport=$to;
if ($to==false) $this->empty_cache();
}
function empty_cache()
{
if ($this->csupport)
$this->cache=array();
}
}
?>