Location: PHPKode > scripts > HTML and XML builder > XHBuilder.php
<?php
/**
 * @author Adhikari Shrawan
 * VERSION : 0.1
 * Email : hide@address.com
 * Date : JUN 2 11:33
 * Copyright (c) Real Time Solutions
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided must retain the above copyright notice . 
 * 
 * No Warranties
 * 
 * The copyright owner or author will not be liable for any kind of damages.
 */

class XHBuilder{
	
	/*
	 * holds the html tags
	 */
	protected $__tags=array();
	
	
	/*
	 * holds the tags attributes
	 */
	protected $__attributes=array();

	
	/*
	 * holds the text of the tags
	 */
	protected $__texts=array();

	
	/*
	 * holds the array index of the tag which has been closed
	 */
	protected $__closes=array();
	
	
	/*
	 * holds the closing tags closed by using method close()
	 * used in close() and getHtml() method
	 */
	protected $__closingTag=array();
	
	
	
	/*
	 * holds the previous tag index of the current closed tag index
	 * use in close() text() __call()
	 */
	protected $__closeNow=null;
	
	
	
	/*
	 * holds the last array index of $this->__tags
	 */
	protected $__now=0;
	
	
	/*
	 *holds the last array index of $this->__tags
	 *
	 *when we close the tag $this->__now is set to previous tag index 
	 *so to get the last $this->__tags index we set it    
	 */
	protected $__current=null;
	
	
	/*
	 * used in close()
	 * holds the previous tag indx of closing tag
	 */
	protected $__previous=null;
	
	
	/*
	 * holds the tags which has been defined using insert() method
	 */
	protected $__forInsert=array();
	
	
	/*
	 * holds the array tag index that has been found searching
	 */
	protected $__foundindex=null;
	
	
	
		
	/**
	 * @param string $tag
	 * @return $this
	 */
	public function __construct($tag){
		$this->setTag($tag);
		return $this;
	}
	
	
	
	/**
	 * @param string $tag
	 * @return $this
	 */
	public function __get($tag){
		$aa=$tag;
		$this->setTag($tag);
		return $this;
	}
	
	
	

	
	/**
	 * sets the attributes of the tag
	 * 
	 * @param string $method
	 * @param array $args
	 * @return $this
	 */
	public function __call($method,$args){
		
		if(count($args)===0){
			return $this;
		}		
	
		/*
		 * index refers to the current tag being processed
		 */
		if($this->__foundindex!==null){ 
			$index=$this->__foundindex;
		}else{
			if($this->__closeNow===null){
				$index=$this->__now;
			}else{
				$index=$this->__closeNow;
				if($index<1){
					$index=1;
				}else{
					do{
						/*
						 * go up in the closes tag array and find the near tag not closed
						 */
						if($this->__closes[$index]===0){
							break;
						}
						$index--;
					}while($index>1);
					
					if($index<1)$index=1;
				}
			}
		}
		
		$method=trim(strtolower($method));
		
		if(is_array($index)){
			
			foreach($index as $arrayIndex){
				if(isset($this->__attributes[$arrayIndex][$method])){
					if($args[0]===NULL){
						unset($this->__attributes[$arrayIndex][$method]);
					}else{
						$this->__attributes[$arrayIndex][$method].=' '.$args[0];
					}		
				}else{
					$this->__attributes[$arrayIndex][$method]=$args[0];
				}
			}
			
		}else{
			if(isset($this->__attributes[$index][$method])){
				if($args[0]===NULL){
					unset($this->__attributes[$index][$method]);
				}else{
					$this->__attributes[$index][$method].=' '.$args[0];
				}		
			}else{
				$this->__attributes[$index][$method]=$args[0];
			}
		}
		
		return $this;
	}
	
	
	
	/**
	 * set tag
	 * 
	 * @param string $tag
	 * @return $this
	 */
	final public function setTag($tag=null){
				
		if(!trim($tag)){
			return $this;
		}
		
		/*
		 * by default __current is null
		 * when we close the tag this is set to length of __tags
		 */
		if($this->__current!==null){
			$this->__now=$this->__current;
			$this->__current=null;
		}
		
		$this->__now=$this->__now+1;
		
		$this->__closes[$this->__now]=0;
		$this->__previous=null;
		$this->__closeNow=null;
		$this->__foundindex=null;
		
		$tag=trim(strtolower($tag));
		$this->__tags[]=array($this->__now=>$tag);
		return $this;
	}
	
	
	/**
	 * sets the text for the tag
	 * if $pureHtml is false $text is a string, if false text is html string
	 * 
	 * @param string $text
	 * @param boolean $pureHtml
	 * @return $this
	 */
	final public function text($text=null,$pureHtml=FALSE){
		
		/*
		 * index refers to the current tag being processed
		 */
		if($this->__foundindex!==null){ 
			$index=$this->__foundindex;
		}else{
			if($this->__closeNow===null){
				$index=$this->__now;
			}else{
				$index=$this->__closeNow;
				if($index<1){
					$index=1;
				}else{
					do{
						/*
						 * go up in the closes tag array and find the near tag not closed
						 */
						if($this->__closes[$index]===0){
							break;
						}
						$index--;
					}while($index>1);
					if($index<1)$index=1;
				}
			}
		}
		
		
		/*if($text==null){
			$this->__texts[$index]='';
			return $this;
		}*/
		
		if($pureHtml===TRUE){
			$txt=(string)$text;
		}else{
			$txt=htmlentities((string)$text,ENT_QUOTES);
		}
		
		
		if(is_array($index)){
			foreach($index as $arrayIndex){
				if(isset($this->__texts[$arrayIndex])){
					if($text===null){
						unset($this->__texts[$arrayIndex]);
					}else{
						$this->__texts[$arrayIndex].=$txt;
					}
				}else{
					if($text===null){
						unset($this->__texts[$arrayIndex]);
					}else{
						$this->__texts[$arrayIndex]=$txt;
					}	
				}
			}
		}else{
				if($text==null){
					$txt='';
				}
			if(isset($this->__texts[$index])){
				$this->__texts[$index].=$txt;
			}else{
				$this->__texts[$index]=$txt;
			}	
		}
		
		
		/*if(isset($this->__texts[$index])){
			$this->__texts[$index].=$txt;
		}else{
			$this->__texts[$index]=$txt;
		}*/
		
		
		return $this;
	}
	
	
	
	
	
	
	/**
	 * sets the closing tag of the tag
	 * 
	 * @return $this
	 */
	final public function close(){
		/*
		 * resert foundindex
		 */
		$this->__foundindex=null;
		
		
		
		/*
		 * store the tags array last in index
		 */
		$this->__current=count($this->__tags);
		
		
		
		/*
		 * previous is set to null when we close a tag
		 * 
		 * previous is set when we close the tag
		 * it hold the previous tag index after current tag is closed
		 * 
		 * previous is reset when we add a new tag  
		 *  
		 */
		if($this->__previous===null){
			$this->__previous=$this->__now;
			
			/*
			 * close tag lookup array
			 * 0 means its has not been closed with close()
			 * 1 means it has to be closed with close()
			 * 2 means it has been processed for close()
			 */
			$this->__closes[$this->__now]=1;
		}else{
			
			/*
			 * this is actually done to find the tag which has been closed
			 * this occures when we use repetative close()
			 * ie . ->close()->close()->close()
			 * 
			 * 
			 * loop until we find the tag index requested to be closed 
			 */
			do{
				$this->__previous=$this->__previous-1;
				if($this->__previous<1){
					$this->__previous=0;
					break;
				}
			}while($this->__closes[$this->__previous]!==0);
		}
		
		$this->__closes[$this->__previous]=1;
		
		if($this->__previous===0){
			return $this;
		}
		
		/*
		 * now close all the tag requested to be closed
		 */
		if($this->__previous===1){
			   
				
				if(isset($this->__closingTag[$this->__now])){
					array_push($this->__closingTag[$this->__now],1);
				
				}else{
					$this->__closingTag[$this->__now] =array();
					array_push($this->__closingTag[$this->__now],1);
				}
				
				
				
				/*
				* when a tag is closed set (processed tag) the close tag lookup array to 2
				* means its already processed 
				*/
			
				$this->__closes[$this->__previous]=2;
			
				/*
				* points to previous tag of the current tag
				*/
				$this->__closeNow=$this->__previous;
		
		}else{
			$i=$this->__previous;
			
			/*
			 * loop until we find and process all the tag to be closed
			 */
			do{
				/*
				 * means this tag has be to closed
				 */
				if($this->__closes[$i]===1){
					if(isset($this->__closingTag[$this->__now])){
						
					   	array_push($this->__closingTag[$this->__now],$i);
						
						/*
						 * when a tag is closed set (processed tag) the close tag lookup array to 2
						 * means its already processed 
						 */
						$this->__closes[$i]=2;
						
						/*
						 * points to previous tag of the current tag
			 			*/
						$this->__closeNow=$i-1;
					}else{
						
							$this->__closingTag[$this->__now] =array();
							array_push($this->__closingTag[$this->__now],$i);
						
						$this->__closes[$i]=2;

						/*
						 * points to previous tag of the current tag
			 			*/
						$this->__closeNow=$i-1;
					}
				}
			  $i--;	
			}while($i!==0);
		}
		
		return $this;
	}
	
	
	
	/**
	 * checks if the tag has closing tag or not
	 * 
	 * @param string $tag
	 * @return boolean
	 */
	final public function hasClosingTag($tag){
		$single=array('img','hr','input','area','br','base','col');		
		if(in_array($tag,$single)){
			return FALSE;
		}else{
			return TRUE;
		}			
	}
	
	
	
	/**
	 * insert another htmlobject inside a tag
	 * 
	 * 
	 * @param object $objOfHtml
	 * @return $this
	 */
	final public function insert($objOfHtml,$inEnd=FALSE){
		if($objOfHtml instanceof HTML){
			
			/*
			 * index refers to the current tag being processed
			 */
			if($this->__foundindex!==null){
				$index=$this->__foundindex;
			}else{
				if($this->__closeNow===null){
					$index=$this->__now;
				}else{
					$index=$this->__closeNow;
					if($index<1){
						$index=1;
					}else{
						do{
							/*
							 * go up in the closes tag array and find the near tag not closed
							 */
							if($this->__closes[$index]===0){
								break;
							}
							$index--;
						}while($index>1);
						if($index<1)$index=1;
					}
				}
			}
			if(is_array($index)){
				foreach($index as $arrayIndex){
					if(isset($this->__forInsert[$arrayIndex])){
						array_push($this->__forInsert[$arrayIndex],array($objOfHtml,$inEnd));
					}else{
						$this->__forInsert[$arrayIndex]=array();
						array_push($this->__forInsert[$arrayIndex],array($objOfHtml,$inEnd));
					}			
				}
			}else{
				if(isset($this->__forInsert[$index])){
					array_push($this->__forInsert[$index],array($objOfHtml,$inEnd));
				}else{
					$this->__forInsert[$index]=array();
					array_push($this->__forInsert[$index],array($objOfHtml,$inEnd));
				}	
			}
			
			
			/*if(isset($this->__forInsert[$index])){
				array_push($this->__forInsert[$index],array($objOfHtml,$inEnd));
			}else{
				$this->__forInsert[$index]=array();
				array_push($this->__forInsert[$index],array($objOfHtml,$inEnd));
			}*/
			
		}
		return $this;
	}
	
	
	/**
	 * find the $findTagIdClass(tagname,id,class)
	 * 
	 *  id=#[id]
	 *  class=.[class]
	 *  anyAttribute=&[attributeTyle]
	 *  
	 *   find([attributeName],[attributeValue])
	 *   
	 *   find(&[attributeType])
	 *   
	 *   find(.[className])
	 *   
	 *   find(#[idName])
	 * 
	 * 
	 * i.e: if <div>div1<div> <div>div2</div>
	 *      it would point to the llast div [div2]
	 * 
	 * @param string $findTagIdClass
	 * @param string $findValue
	 * @return $this
	 */
	final public function find($findTagIdClass=null,$findValue=null){
		$this->__foundindex=null;
		
		if(!trim($findTagIdClass)){
			return $this;
		}
		
		$this->__current=count($this->__tags);
		
		
		if($findValue!==null){
			$this->__foundindex=array();
			foreach($this->__tags as $tKey => $tag){
				if(isset($this->__attributes[$tKey+1])){
					foreach($this->__attributes[$tKey+1] as $aType => $aValue){
						if($aType===$findTagIdClass){
							$values=explode(' ',$aValue);
							if(in_array($findValue,$values)){
								//$this->__foundindex=$tKey+1;
								array_push($this->__foundindex,$tKey+1);
							}
						}
					}
				}
			}
			
			
		}else{
			    $this->__foundindex=array();
				foreach($this->__tags as $tKey => $tag){
					
						if(strpos($findTagIdClass,'.')!==0 && strpos($findTagIdClass,'#')!==0 && strpos($findTagIdClass,'&')!==0){
							if($tag[$tKey+1]===strtolower($findTagIdClass)){
								array_push($this->__foundindex,$tKey+1);
							}
						}elseif(strpos($findTagIdClass,'#')===0){
								if(isset($this->__attributes[$tKey+1])){
									foreach($this->__attributes[$tKey+1] as $aType => $aValue){
										if($aType==='id'){
											$values=explode(' ',$aValue);
											
											/*remove hash(#)*/
											$id=substr($findTagIdClass,1);
											
											
											if(in_array($id,$values)){
												//$this->__foundindex=$tKey+1;
												array_push($this->__foundindex,$tKey+1);
											}
										}
									}
								}
						}elseif(strpos($findTagIdClass,'.')===0){
							if(isset($this->__attributes[$tKey+1])){
									foreach($this->__attributes[$tKey+1] as $aType => $aValue){
										if($aType==='class'){
											$values=explode(' ',$aValue);
											
											/*remove dot(.)*/
											$class=substr($findTagIdClass,1);
											
												
											if(in_array($class,$values)){
												//$this->__foundindex=$tKey+1;
												array_push($this->__foundindex,$tKey+1);
											}
										}
									}
								}	
						
						}elseif(strpos($findTagIdClass,'&')===0){
							if(isset($this->__attributes[$tKey+1])){
								foreach($this->__attributes[$tKey+1] as $aType => $aValue){
									/*remove amp%(&)*/
									$attrName=substr($findTagIdClass,1);
									if($aType===$attrName){
										array_push($this->__foundindex,$tKey+1);
									}
								}
							}
						}
					}	
				}
		
		if(is_array($this->__foundindex) && count($this->__foundindex)===0){
			$this->__foundindex=null;
		}		
				
		return $this;
	}
	
	
	final public function alterTag($newTagName=null){
			
	   if(!trim($newTagName)){
			return $this;
		}
			/*
			 * index refers to the current tag being processed
			 */
			if($this->__foundindex!==null){
				$index=$this->__foundindex;
			}else{
				if($this->__closeNow===null){
					$index=$this->__now;
				}else{
					$index=$this->__closeNow;
					if($index<1){
						$index=1;
					}else{
						do{
							/*
							 * go up in the closes tag array and find the near tag not closed
							 */
							if($this->__closes[$index]===0){
								break;
							}
							$index--;
						}while($index>1);
						if($index<1)$index=1;
					}
				}
			}	
			
			
			if(is_array($index)){
				foreach($index as $arrayIndex){
					$newTagName=strtolower(trim($newTagName));
					$this->__tags[$arrayIndex-1][$arrayIndex]=$newTagName;
				}
			}else{
				$newTagName=strtolower(trim($newTagName));
				$this->__tags[$index-1][$index]=$newTagName;
			}
			/*$newTagName=strtolower(trim($newTagName));
			$this->__tags[$index-1][$index]=$newTagName;*/
			return $this;
	}
	
	
	
	final public function clearAttribute($attributeName=null){
		
		if(!trim($attributeName)){
			return $this;
		}
		
		/*
		 * index refers to the current tag being processed
		 */
		if($this->__foundindex!==null){ 
			$index=$this->__foundindex;
		}else{
			if($this->__closeNow===null){
				$index=$this->__now;
			}else{
				$index=$this->__closeNow;
				if($index<1){
					$index=1;
				}else{
					do{
						/*
						 * go up in the closes tag array and find the near tag not closed
						 */
						if($this->__closes[$index]===0){
							break;
						}
						$index--;
					}while($index>1);
					
					if($index<1)$index=1;
				}
			}
		}
		
		$attributeName=trim(strtolower($attributeName));
		
		if(is_array($index)){
			
			foreach($index as $arrayIndex){
				if(isset($this->__attributes[$arrayIndex][$attributeName])){
					$this->__attributes[$arrayIndex][$attributeName]='';
				}
			}
			
		}else{
			if(isset($this->__attributes[$index][$attributeName])){
				$this->__attributes[$index][$attributeName]='';
			}
		}
		
		return $this;
	}
	
	
	
	/**
	 * return the generated html as string
	 * 
	 * @return string
	 */
	final public function getHtml(){
		$__buildHtml='';
		$__lastSegHtml='';	
		
		foreach($this->__tags as $tKey => $tag){

			if(isset($tag[$tKey+1])){
							
						
						$__buildHtml.='<'.$tag[$tKey+1];
						
						if(isset($this->__attributes[$tKey+1])){
							foreach($this->__attributes[$tKey+1] as $aType => $aValue){
								$__buildHtml .=' '.$aType.'="'.$aValue.'"';
							}
						}
						
						$__buildHtml.=$this->hasClosingTag($tag[$tKey+1])? '>':'/>';
						
						if(isset($this->__texts[$tKey+1])){
							$__buildHtml .=$this->__texts[$tKey+1];
						}
						
						
						if(isset($this->__forInsert[$tKey+1])){
							foreach($this->__forInsert[$tKey+1] as $cKey => $value){
								    if($value[1]===FALSE){
								 		$__buildHtml.=$value[0];   	
								    }	
								}
						}
						
						/*if current tag has been closed using close()*/
						if(isset($this->__closingTag[$tKey+1])){
							
							foreach($this->__closingTag[$tKey+1] as $closeKey => $closeTag){
								
								$__insertEnd='';
								if(isset($this->__forInsert[$closeTag])){
									foreach($this->__forInsert[$closeTag] as $cKey => $value){
										    if($value[1]===TRUE){
										 		$__insertEnd.=$value[0];   	
										    }	
										}
								}
								if($this->hasClosingTag($this->__tags[$closeTag-1][$closeTag])===TRUE){
									$__buildHtml .=$__insertEnd.'</'.$this->__tags[$closeTag-1][$closeTag].'>';
								}else{
									$__buildHtml .=$__insertEnd;
								}	
							}
							
							
						}elseif($this->__closes[$tKey+1]===0){
							
							$__insertEnd='';
							if(isset($this->__forInsert[$tKey+1])){
									foreach($this->__forInsert[$tKey+1] as $cKey => $value){
										    if($value[1]===TRUE){
										 		$__insertEnd.=$value[0];   	
										    }	
										}
								}
							
							if($this->hasClosingTag($tag[$tKey+1])===TRUE){
								$__lastSegHtml.=strrev($__insertEnd.'</'.$tag[$tKey+1].'>');
							}else{
								$__lastSegHtml.=strrev($__insertEnd);
							}	
					}
			}		
						
		}
		
		return $__buildHtml.strrev($__lastSegHtml);
	}
	
	
	
	public function __toString(){
		return $this->getHtml();
	}
	
	
}


class html extends XHBuilder{
	public function __construct($tagName){
		parent::__construct($tagName);
	}
}

class xml extends XHBuilder{
	public function __construct($tagName){
		parent::__construct($tagName);
	}
}




?>
Return current item: HTML and XML builder