<?php
class tidy {
	var $tags;
	var $tidy_content;
	var $src_content;
	var $content_length;
	var $pointer;
	var $pattern_tag_start = '/(<\w+?>)/i';
	var $pattern_tag_end = '/(<\/[^>]+?>)/i';
	var $pattern_tag = '/(<[^>]+?>)/i';
	var $allow_unclose_tag = array('br', 'img', 'hr', 'input', 'area');
	function tidy(){
		$this->tags = array();
		$this->pointer = 0;
	}

	function __destruct(){
		unset($this->tags);
		$this->pointer = 0;
		$this->tags = array();
	}

	function clean($srcContent){
		$this->tidy_content = '';
		$this->src_content = $srcContent;
		$this->content_length = strlen($srcContent);
		$this->process();
		$this->__destruct();
		return $this->tidy_content;
	}

	function getTagStartName($tagString, &$tagName){
		$result = preg_match('/<(\w+)/', $tagString, $match);
		$tagName = $match[1];
		return $result;
	}

	function getTagEndName($tagString, &$tagName){
		$result = preg_match('/<\/(\w+)>/', $tagString, $match);
		$tagName = $match[1];
		return $result;
	}

	function getTagName($tagString){
		$result = preg_match('/<\/*(\w+)/', $tagString, $match);
		return $match[1];
	}

	function process(){
		while(preg_match($this->pattern_tag, $this->src_content, $match, null, $this->pointer)){
			$tagstring = $match[0];
			$tagType = $this->getTagType($tagstring);
			switch ($tagType){
				case 'START':
					$this->processTagStart($tagstring);
				break;
				case 'END':
					$this->processTagEnd($tagstring);
				break;
				default:
					return;
				break;
			}
		}
		while(count($this->tags)){
			$tag_name = array_pop($this->tags);
			if(in_array(strtolower($tag_name), $this->allow_unclose_tag)) continue;
			$this->tidy_content .= sprintf('</%s>', $tag_name);
		}
		
		$this->tidy_content .= substr($this->src_content, $this->pointer, $this->content_length - $this->pointer);
	}

	function getTagType($tagstring){
		if(preg_match('/^<\//', $tagstring)){
			return 'END';
		}elseif (preg_match('/^<\w+/', $tagstring)){
			return 'START';
		}else {
			return null;
		}
	}

	function processTagStart($tagstring){
		$pos = strpos($this->src_content, $tagstring, $this->pointer);
		$len = $pos + strlen($tagstring) - $this->pointer;
		$sss = substr($this->src_content, $this->pointer, $len);
		$this->tidy_content .= $sss;
		$this->pointer += strlen($sss);
		$tagname = '';
		$ret = $this->getTagStartName($tagstring, $tagname);
		if(in_array(strtolower($tagname), $this->allow_unclose_tag)) return;
		array_push($this->tags, $tagname);
	}

	function processTagEnd($tagstring){
		$tagname = '';
		$ret = $this->getTagEndName($tagstring, $tagname);
		$pos = strpos($this->src_content, $tagstring, $this->pointer);
		$len = $pos + strlen($tagstring) - $this->pointer;
		$sss = substr($this->src_content, $this->pointer, $len);
		$this->pointer += strlen($sss);
		$closetag = array_pop($this->tags);
		if(empty($closetag)){ 
			$sss = str_replace($tagstring, '', $sss);
		}else if( 0 !== strcmp($tagname, $closetag)){
			$sss = preg_replace($this->pattern_tag_end, '</' .$closetag. '>', $sss);
		}
		$this->tidy_content .= $sss;
	}
}
?>