Location: PHPKode > scripts > Kxparse > kxparse.php
<?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();
	}
}
?>
Return current item: Kxparse