<?php

/*************************************************

morecache.php - easily display mysql query results with paging, tabular layout, etx

home page: www.samscripts.com/scripts/mysqltemplate
support: support.samscripts.com/index.php

licence: freeware / optionally giftware ( www.samscripts.com/donate.php )
author: Sam Yapp
date: 8th September 2002

documentation and examples at www.samscripts.com

*************************************************/

class mysqltemplate{

	var $cache = 0;

	var $queriesdone = 0;
	var $queriescached = 0;
	var $totalqueries = 0;

	var $connection;
	var $database;
	var $username;
	var $password;
	var $host;

	var $errors;
	var $header;
	var $footer;
	var $template;

	var $pagesize = 0;
	var $currentpage = 1;
	var $nextpage = 1;
	var $prevpage = 1;
	var $pagelist = "";

	var $tableorder = "columns";
	var $columns = 1;

	var $connection = 0;
	var $host = "";
	var $user = "";
	var $password = "";
	var $database = "";

	var $replace_object = "";
	var $replace_function = "";

	var $headervars = array();
	var $footervars = array();
	var $rowvars = array();

	var $cachename = "";
	var $cacheexpiry = 3600;

	var $queries = array();

	function setcallback($args){
		if( !is_array($args) ){
			$this->replace_object = 0;
			$this->replace_function = $args;
		}else{
			$this->replace_object = &$args[0];
			$this->replace_function = $args[1];
		}
	}


	function mysqltemplate($connection = 0, $database = "", $host = "", $user = "", $password = ""){
		$this->connection = $connection;
		$this->database = $database;
		$this->host = $host;
		$this->user = $user;
		$this->password = $password;
		$this->settemplate();
	}

	function settemplate($template="", $resetfields = true){
		if( $resetfields){
			unset($this->replace_object);
			$this->replace_object = "";
			$this->replace_function = "";
			$this->pagesize = 0;
			$this->currentpage = 1;
			$this->nextpage = 1;
			$this->prevpage = 1;
			$this->pagelist = "";

			$this->tableorder = "columns";
			$this->columns = 1;

			$this->cachename = "";
		}
		$this->headervars = array();
		$this->footervars = array();
		$this->template =& $template;
		$this->compiled = false;
		$this->rows = array();
		$this->rowcount = 0;
		$this->cnt = 0;
		$this->groupheader = "";
		$this->groupfooter = "";
	}

	function getmicrotime(){
		list($usec, $sec) = explode(" ",microtime());
		return ((float)$usec + (float)$sec);
	} 

	function connect(){
		if( !$this->connection ){
			if( isset($this->profiler) ) $this->profiler->profile("connect to database");
			$start = $this->getmicrotime();
			$this->connection = @mysql_connect($this->host, $this->user, $this->password)or die(mysql_error());
			if( isset($this->profiler) ) $this->profiler->profile("finished connect to database");
			$this->connecttime = $this->getmicrotime() - $start;
			if( !$this->connection ){
				return false;
			}
			if( isset($this->profiler) ) $this->profiler->profile("select database");
			$selected =@mysql_select_db($this->database, $this->connection);
			if( isset($this->profiler) ) $this->profiler->profile("finish select database");
			$this->selectdbtime = $this->getmicrotime() - $start + $this->connecttime;
			$this->connectselecttime = $this->selectdbtime + $this->connecttime;
			if( !$selected ) return false;
		}
		return true;
	}

	function print_debug(){
		echo '<table class="box" align="center"><tr><td><b>Query</b></td><td><b>Time Taken</b></td><td align="center"><b>From Cache?</b></td><td align="center"><b>Internal?</b></td></tr>';
		echo '<tr><td>Connect To MySQL</td><td>'.$this->connecttime.'</td><td>&nbsp;</td><td>&nbsp;</td></tr>';
		echo '<tr><td>Select Database</td><td>'.$this->selectdbtime.'</td><td>&nbsp;</td><td>&nbsp;</td></tr>';
		echo '<tr><td>Connect and Select Database</td><td>'.$this->connectselecttime.'</td><td>&nbsp;</td><td>&nbsp;</td></tr>';
		for( $i = 0; $i < $this->totalqueries; $i++){
			$q = $this->queries[$i];
			echo '<tr><td>'.htmlspecialchars($q["sql"]).'</td><td>'.$q["timetaken"].'</td><td align="center">'.($q["cached"] == 1 ? 'Yes' : 'No').'</td><td align="center">'.($q["internal"]==1 ? 'Yes' : 'No').'</td></tr>';
		}
		echo '</table>';
	}

	function mysql_query($sql, $internal=0){
		if( !$this->connect() ) return false;
		$this->queriesdone++;
		$this->totalqueries++;
		$this->queries[$this->totalqueries-1]["sql"] = $sql;
		$this->queries[$this->totalqueries-1]["cached"] = 0;
		$this->queries[$this->totalqueries-1]["internal"] = $internal;
		$start = $this->getmicrotime();
		if( isset($this->profiler) ) $this->profiler->profile("query: $sql");
		$result = @mysql_query($sql, $this->connection);
		if( isset($this->profiler) ) $this->profiler->profile("finished query: $sql");
		$this->queries[$this->totalqueries-1]["timetaken"] = $this->getmicrotime()-$start;
		return $result;
	}

	function query($sql){
		if( $this->cache != 0 && $this->cachename != "" ){
			$caching = 1;
			$start = $this->getmicrotime();
			if( isset($this->profiler) ) $this->profiler->profile("checking caching): $sql");
			$update = $this->cache->cache($this->cachename, $this->cacheexpiry);
			if( !$update ){
				if( isset($this->profiler) ) $this->profiler->profile("query (cached): $sql");
				$this->totalqueries++;
				$this->queriescached++;
				$this->queries[$this->totalqueries-1]["internal"] = 0;
				$this->queries[$this->totalqueries-1]["sql"] = $sql;
				$this->queries[$this->totalqueries-1]["cached"] = 1;
				$this->queries[$this->totalqueries-1]["timetaken"] = $this->getmicrotime() - $start;
			}
		}else{
			$caching = 0;
			$update = true;
		}
		if( $update ){
			$start = $this->getmicrotime();
			if( $this->do_query(&$sql)){
				if( $this->columns > 1 ){
					$result = true;
					$this->printquery_tabled();
				}else{
					$result = true;
					$this->printquery();
				}
			}else{
				$result = false;
			}
			if( $caching ){
				$this->cache->stop();
			}
		}else{
			return true;
		}
		return $result;
	}


	function printquery_tabled(){
		$result =& $this->result;

		$cols = $this->columns;
		$method = preg_match("/vert/is", $this->tableorder) ? "vert" : "horiz";

		$headervars =& array_merge(&$this->headervars, &$this->defaultvars);
		$footervars =& array_merge(&$this->footervars, &$this->defaultvars);
		$rowvars =& array_merge(&$this->rowvars, &$this->defaultvars);

		$cnt = $this->numrows;
		$this->printheader(array_keys($headervars), array_values($headervars));
		$numrows = (int)(($cnt + ($cols-1))/ $cols);
		$fullcols = $cnt % $cols;
		if ($fullcols == 0 ) $fullcols = $cols;
		$numxfull = $numrows * $fullcols;
		$colsminusfullcols = $cols - $fullcols;
		$i = 0;
		for( $r = 0; $r < $numrows; $r++){
			$rx = $r * $colsminusfullcols + $numxfull - $fullcols;
			$this->printgroupheader("","");
			for( $col = 0; $col < $cols; $col++){
				if( $method != "horiz" ){
					if( $col < $fullcols ){
						$rownum = $r + ($col * ($numrows));
					}else{
						$rownum = $rx + $col;
					}
					if( $rownum < $cnt ){
						mysql_data_seek($result,$rownum);
						$row = mysql_fetch_assoc($result);
					}else{
						$row = array();
					}
				}else{
					$row = mysql_fetch_assoc($result);
				}
				$i++;
				if( $row ){
					if( $this->replace_function != "" ){
						$func = $this->replace_function;
						if( $this->replace_object != "" ){
							$obj = $this->replace_object;
							$obj->$func(&$row);
						}else{
							$func(&$row);
						}
//						eval($repfunc.'($row);');
					}
					$row["rownumber"] = $i;
					$this->printrow($row);
				}else{
					$this->printerror("emptycell", "", "");
				}
			}
			$this->printgroupfooter("","");
		}
		$this->printfooter(array_keys($footervars), array_values($footervars));
	}

	function printquery(){

		$headervars =& array_merge(&$this->headervars, &$this->defaultvars);
		$footervars =& array_merge(&$this->footervars, &$this->defaultvars);
		$rowvars =& array_merge(&$this->rowvars, &$this->defaultvars);

		$this->printheader(array_keys($headervars), array_values($headervars));

		$i = 0;
		while( $row = mysql_fetch_assoc($this->result) ){
			if( $this->replace_function != "" ){
				$func = $this->replace_function;
				if( $this->replace_object != "" ){
					$obj = $this->replace_object;
					$obj->$func(&$row);
				}else{
					$func(&$row);
				}
//				eval($repfunc.'($row);');
			}
			$i++;
			$row["rownumber"] = $i;
			$this->printrow(array_merge($rowvars, $row));
		}

		$this->printfooter(array_keys($footervars), array_values($footervars));

	}


	function printheader($search="", $replace=""){
		if( is_array($search) )	$search = split(":::", "<:".join(">:::<:", $search).">");
//		echo "search:",$search,"<br>replace:",$replace, "<br>header:",$this->header,"<br>";
		echo str_replace(&$search, &$replace, &$this->header);
	}

	function printfooter($search="", $replace=""){
		if( is_array($search) )	$search = split(":::", "<:".join(">:::<:", $search).">");
		echo str_replace(&$search, &$replace, &$this->footer);
	}

	function printgroupheader($search, $replace){
		if( is_array($search) &&count($search) ) $search = split(":::", "<:".join(">:::<:", $search).">");
		echo str_replace(&$search, &$replace, &$this->groupheader[0]);
	}

	function printgroupfooter($search, $replace){
		if( is_array($search) && count($search) )	$search = split(":::", "<:".join(">:::<:", $search).">");
		echo str_replace(&$search, &$replace, &$this->groupfooter[0]);
	}

	function printerror($errorname, $search, $replace){
		if( is_array($search) )	$search = split(":::", "<:".join(">:::<:", $search).">");
		if( isset($this->errors[$errorname]) ) echo str_replace(&$search, &$replace, &$this->errors[$errorname]);
//		echo join("<br>", $replace);
	}

	function printrow($row){
		eval($this->rows[$this->cnt]);
		$this->cnt++;
		if( $this->cnt == $this->rowcount) $this->cnt = 0;
	}

	function converttemplate(&$t){
		$tnew = 'echo "'.preg_replace('~<:[ ]*?([a-z0-9_]+)[ ]*.*?>~is', '".$row["$1"]."', str_replace('"', '\"',$t)).'";';
		return $tnew;
	}

	function do_query(&$sql){
		if( !$this->compiled ){
			$this->splittemplate();
			$this->compiletemplate();
		}
		if( !$this->connect() ) return $this->printerror("dberror", array("message", "sql"), array(mysql_error(), $sql));
		if( $this->pagesize > 0 ){
			if( preg_match("/group by (.*?) /is", $sql, $match) ){
				$selectme = "COUNT(DISTINCT ".trim($match[1]).")";
			}else{
				$selectme = "COUNT(*)";
			}
			preg_match_all("/select (.*?) from (.*)/is", $sql, $matches);
			$fieldstr = $matches[1][0];
			$conditions = $matches[2][0];
			$len = strlen($fieldstr);
			$fields = array();
			$brackets = 0;
			$quotes = 0;
			$field = "";
			for( $i = 0; $i < $len; $i++){
				$dontadd = false;
				switch ($fieldstr[$i]){
					case "'":
						$quotes = 1 - $quotes;
						break;
					case "(":
						$brackets++;
						break;
					case ")":
						$brackets--;
						break;
					case ",":
						if( !$quotes && $brackets == 0 ){
							$fields[] = trim($field);
							$field = "";
							$dontadd = true;
						}
						break;
				}
				if( !$dontadd ) $field.=$fieldstr[$i];
			}
			if( trim($field) != "" ) $fields[] = trim($field);

			for( $i = 0; $i < count($fields); $i++){
				if( preg_match("/(.*) as ([\w\d_-]*)/is", $fields[$i], $matches) ){
					$conditions = preg_replace("/[^\.]\b".$matches[2]."\b/is", $matches[1], $conditions);
				}
			}
			$conditions = preg_replace("/ order by.*/is", "", $conditions);
			$conditions = preg_replace("/ group by.*/is", "", $conditions);

			$res = $this->mysql_query("SELECT $selectme FROM $conditions", 1);

			if( !$res )return  $this->printerror("dberror", array("message", "sql"), array(mysql_error(), $sql));

			$this->rowtotal = mysql_result($res, 0,0);
			$this->pagecount = (int)(($this->rowtotal + $this->pagesize - 1) / $this->pagesize);
			if( $this->currentpage < 1 ) $this->currentpage = 1;
			if( $this->currentpage > $this->pagecount) $this->currentpage = $this->pagecount;
			$this->firstrow = $this->pagesize * ($this->currentpage - 1) + 1;
			if( $this->firstrow > $this->rowtotal ) $this->firstrow = $this->rowtotal - $this->pagesize;
			if( $this->firstrow < 0 ) $this->firstrow = 0;
			$this->lastrow = $this->firstrow + $this->pagesize - 1;
			if( $this->lastrow > $this->rowtotal ) $this->lastrow = $this->rowtotal;
			$sql .= " LIMIT ".($this->firstrow-1).", ".$this->pagesize;
			if( $this->currentpage == $this->pagecount ){
				$this->nextpage = $this->pagecount;
				$nextrep = "";
			}else{
				$this->nextpage = $this->currentpage+1;
				$nextrep = '$1';
			}
			if( $this->currentpage == 1 ){
				$this->prevpage = 1;
				$prevrep = "";
			}else{
				$this->prevpage = $this->currentpage - 1;
				$prevrep = '$1';
			}
			$pagelists = array();
			for( $i = 1; $i <= $this->pagecount; $i++){
				if( $i == $this->currentpage ){
					$pagelists[$i-1] = str_replace("<:page>", $i, $this->pagelistthispageformat);
				}else{
					$pagelists[$i-1] = str_replace("<:page>", $i, $this->pagelistformat);
				}
			}
			$this->pagelist = join("", $pagelists);
			$this->header = preg_replace(array("~<:ifnext>(.*?)</:ifnext>~is","~<:ifprev>(.*?)</:ifprev>~"),
													array($nextrep, $prevrep), $this->header);
			$this->footer = preg_replace(array("~<:ifnext>(.*?)</:ifnext>~is","~<:ifprev>(.*?)</:ifprev>~"),
													array($nextrep, $prevrep), $this->footer);
		}

		$this->result = $this->mysql_query($sql, 1);
		if( $this->result ){
			$this->numrows = mysql_num_rows($this->result);
			if( $this->pagesize == 0 ){
				$this->rowtotal = $this->lastrow = $this->numrows;
				$this->firstrow = 1;
				$this->pagecount = 1;
				$this->currentpage = 1;
			}
			if( $this->numrows == 0 ){
				$this->printerror("norecords", "", "");
				return false;
			}
			$this->defaultvars = array("rowcount"=>$this->numrows, "totalrows"=>$this->rowtotal, "currentpage"=>$this->currentpage,
									"pagesize"=>$this->pagesize, "pagecount"=>$this->pagecount, "firstrow"=>$this->firstrow,
									"lastrow"=>$this->lastrow, "nextpage"=>$this->nextpage, "prevpage"=>$this->prevpage,"pagelist"=>$this->pagelist);
			return true;
		}else{
			$this->printerror("dberror", array("message", "sql"), array(mysql_error(),$sql));
			return false;
		}
	}

	function compileheaders(){
		$this->ghcompiled = array();
		$this->gfcompiled = array();
		for( $i = 0; $i < count($this->groupheader); $i++){
			$this->ghcompiled[$i] = $this->converttemplate($this->groupheader[$i]);
		}
		for( $i = 0; $i < count($this->groupfooter); $i++){
			$this->gfcompiled[$i] = $this->converttemplate($this->groupfooter[$i]);
		}
		$this->ghcount = count($this->ghcompiled);
		$this->gfcount = count($this->gfcompiled);
	}

	function compiletemplate(){
		$this->rows = array();
		$found = preg_match_all('~<:row>(.*?)</:row>~is', $this->template, $temp);
		if( $found ){
			for( $i =0; $i < count($temp[1]); $i++){
				$this->rows[] = $this->converttemplate($temp[1][$i]);
			}
		}else{
			$this->rows[] = $this->converttemplate($this->template);
		}
		$this->rowcount = count($this->rows);
		$this->compiled = true;
	}

	function splittemplate(){
		$tags = array("pagelistformat", "header", "footer", "error",  "groupheader", "groupfooter");
		$tags = join("|", $tags);
		$regex = "~<:($tags)[=]?([a-z0-9_]*)[ ]*>(.*?)</:\\1[ =]?\\2?[ ]*>[\n]?~is";
		if( preg_match_all($regex, $this->template, $temps) ){
			$out = array();
			for( $i = 0; $i < count($temps[1]); $i++){
				if( $temps[2][$i] == "" ){
					$out[$temps[1][$i]][] = $temps[3][$i];
				}else{
					$out[$temps[1][$i]][$temps[2][$i]] = $temps[3][$i];
				}
			}
		}
		$this->template =& preg_replace($regex, "", &$this->template);
		$this->errors = isset($out["error"]) ? $out["error"] : array();
		$this->header = isset($out["header"][0]) ? $out["header"][0] : "";
		$this->footer = isset($out["footer"][0]) ? $out["footer"][0] : "";
		$this->groupheader = isset($out["groupheader"]) ? $out["groupheader"] : array("");
		$this->groupfooter = isset($out["groupfooter"]) ? $out["groupfooter"] : array("");
		$this->plformat = isset($out["pagelistformat"]) ? $out["pagelistformat"][0] : "";
		global $REQUEST_URI, $HTTP_SERVER_VARS;
		$query = isset($HTTP_SERVER_VARS["QUERY_STRING"]) ? $HTTP_SERVER_VARS["QUERY_STRING"] : "";
		$query = preg_replace("/[&|?]*currentpage=[0-9]*/", "", $query);
		$this->pagelistformat = '<a href="'.$REQUEST_URI.'?currentpage=<:page>&'.$query.'"><:page></a> ';
		$this->pagelistthispageformat = "<b><:page></b> ";
		if( $this->plformat != "" ){
			if( preg_match("~<:thispage>(.*?)</:thispage>~is", $this->plformat, $match) ){
				$this->pagelistthispageformat = $match[1];
				$this->plformat = preg_replace("~<:thispage>.*</:thispage>~is", "", $this->plformat);
			}
			$this->pagelistformat = $this->plformat;
		}
	}

}

?>