<?php
	
	/**
	 * Multi database connection class
	 *
	 */
	class DBConnection {
		
		/**
		 * Current database type
		 *
		 * @var string
		 * @access private
		 */
		var $dbType = 'mysql';
		/**
		 * Created connection handle
		 *
		 * @var resource
		 * @access private
		 */
		var $connectionID = null;
		/**
		 * Handle of currenty processed recordset
		 *
		 * @var resource
		 * @access private
		 */
		var $queryID = null;
		/**
		 * DB type specific function mappings
		 *
		 * @var Array
		 * @access private
		 */
		var $metaFunctions = Array();
		
		/**
		 * Function to handle sql errors
		 *
		 * @var string
		 * @access private
		 */
		var $errorHandler = '';
		
		/**
		 * Error code
		 *
		 * @var int
		 * @access private
		 */
		var $errorCode = 0;
		/**
		 * Error message
		 *
		 * @var string
		 * @access private
		 */
		var $errorMessage = '';
		
		/**
		 * Initializes connection class with
		 * db type to used in future
		 *
		 * @param string $dbType
		 * @return DBConnection
		 * @access public
		 */
		function DBConnection($dbType, $errorHandler = '')
		{
			$this->dbType = $dbType;
			$this->initMetaFunctions();
			if(!$errorHandler)
			{
				$this->errorHandler = Array(&$this,'handleError');
			}
		}
		
		/**
		 * Set's custom error
		 *
		 * @param int $code
		 * @param string $msg
		 * @access public
		 */
		function setError($code,$msg)
		{
			$this->errorCode=$code;
			$this->errorMessage=$msg;	
		}
		
		/**
		 * Checks if previous query execution
		 * raised an error.
		 *
		 * @return bool
		 * @access public
		 */
		function hasError()
		{
			return !($this->errorCode == 0);	
		}
		
		/**
		 * Caches function specific to requested
		 * db type
		 *
		 * @access private
		 */
		function initMetaFunctions()
		{
			$ret = Array();
			switch($this->dbType)
			{
				case 'mysql':
					 $ret = Array(); // only define functions, that name differs from "dbType_<meta_name>"
				
					break;
				
				
			}
			$this->metaFunctions = $ret;
		}		
								
		/**
		 * Get's function for specific db type
		 * based on it's meta name
		 *
		 * @param string $name
		 * @return string
		 * @access private
		 */
		function getMetaFunction($name)
		{
			if( !isset($this->metaFunctions[$name]) )
			{
				if(function_exists($this->dbType.'_'.$name)) return $this->dbType.'_'.$name;	
			}
			else 
			{
				return $this->dbType.$name;
			}
			return false;
		}
		
		
		/**
		 * Try to connect to database server
		 * using specified parameters and set
		 * database to $db if connection made
		 *
		 * @param string $host
		 * @param string $user
		 * @param string $pass
		 * @param string $db
		 * @access public
		 */
		function Connect($host,$user,$pass,$db)
		{
			$func = $this->getMetaFunction('connect');
			$this->connectionID = $func($host,$user,$pass) or trigger_error("Can't connect to db", E_USER_ERROR);
			if($this->connectionID)
			{
				$this->setDB($db);
				$this->showError();
			}
		}
		
		/**
		 * Shows error message from previous operation
		 * if it failed
		 *
		 * @access private
		 */
		function showError($sql='')
		{
			$this->setError(0,''); // reset error
			if($this->connectionID)
			{
				$func = $this->getMetaFunction('errno'); $this->errorCode = $func($this->connectionID);
				if($this->hasError())
				{
					$func = $this->getMetaFunction('error'); $this->errorMessage = $func($this->connectionID);
					if(is_array($this->errorHandler))
					{
						$func = $this->errorHandler[1];
						$ret = $this->errorHandler[0]->$func($this->errorCode,$this->errorMessage,$sql);
					}
					else 
					{
						$func = $this->errorHandler;
						$ret = $func($this->errorCode,$this->errorMessage,$sql);
					}
					if(!$ret) exit;
				}
			}
		}
		
		/**
		 * Default error handler for sql errors
		 *
		 * @param int $code
		 * @param string $msg
		 * @param string $sql
		 * @return bool
		 * @access private
		 */
		function handleError($code,$msg,$sql)
		{
			echo '<b>Processing SQL</b>: '.$sql.'<br>';
			echo '<b>Error ('.$code.'):</b> '.$msg.'<br>';
			return false;
		}
		
		/**
		 * Set's database name for connection
		 * to $new_name
		 *
		 * @param string $new_name
		 * @return bool
		 * @access public
		 */
		function setDB($new_name)
		{
			if(!$this->connectionID) return false;
			$func = $this->getMetaFunction('select_db');
			return $func($new_name);
		}
		
		/**
		 * Returns first field of first line
		 * of recordset if query ok or false
		 * otherwise
		 * 
		 * @param string $sql
		 * @return string
		 * @access public
		 */
		function GetOne($sql)
		{
			$row = $this->GetRow($sql);
			if(!$row) return false;
			
			return array_shift($row);
		}
		
		/**
		 * Returns first row of recordset
		 * if query ok, false otherwise
		 * 
		 * @param stirng $sql
		 * @return Array
		 * @access public
		 */
		function GetRow($sql)
		{
			$sql .= ' '.$this->getLimitClause(0,1);
			$ret = $this->Query($sql);
			if(!$ret) return false;	
			
			return array_shift($ret);
		}
		
		/**
		 * Returns 1st column of recordset as
		 * one-dimensional array or false otherwise
		 * Optional parameter $key_field can be used
		 * to set field name to be used as resulting
		 * array key
		 *
		 * @param string $sql
		 * @param string $key_field
		 * @return Array
		 * @access public
		 */
		function GetCol($sql, $key_field = null)
		{
			$rows = $this->Query($sql);
			if(!$rows) return $rows;
			
			$i = 0; $row_count = count($rows);
			$ret = Array();
			if(isset($key_field))
			{
				while ($i < $row_count)
				{
					$ret[$rows[$i][$key_field]] = array_shift($rows[$i]);
					$i++;
				}
			}
			else 
			{
				while ($i < $row_count)
				{
					$ret[] = array_shift($rows[$i]);
					$i++;
				}
			}
			return $ret;
		}
		
		/**
		 * Queries db with $sql query supplied
		 * and returns rows selected if any, false
		 * otherwise. Optional parameter $key_field
		 * allows to set one of the query fields
		 * value as key in string array.
		 *
		 * @param string $sql
		 * @param string $key_field
		 * @return Array
		 */
		function Query($sql, $key_field = null)
		{
			$query_func = $this->getMetaFunction('query');
			$this->queryID = $query_func($sql,$this->connectionID);
			if( is_resource($this->queryID) )
			{
				$ret = Array();
				$fetch_func = $this->getMetaFunction('fetch_assoc');
				if( isset($key_field) )
				{
					while( ($row = $fetch_func($this->queryID)) )
					{
						$ret[$row[$key_field]] = $row;
					}
				}
				else 
				{
					while( ($row = $fetch_func($this->queryID)) )
					{
						$ret[] = $row;
					}
				}
				$this->Destroy();
				return $ret;
			}
			$this->showError($sql);
			return false;
		}
		
		/**
		 * Free memory used to hold recordset handle
		 *
		 * @access private
		 */
		function Destroy()
		{
			if($this->queryID)
			{
				$free_func = $this->getMetaFunction('free_result');	
				$free_func($this->queryID);
				$this->queryID = null;
			}
		}
		
		/**
		 * Returns auto increment field value from
		 * insert like operation if any, zero otherwise
		 *
		 * @return int
		 * @access public
		 */
		function getInsertID()
		{
			$func = $this->getMetaFunction('insert_id');
			return $func($this->connectionID);
		}
		
		/**
		 * Returns row count affected by last query
		 *
		 * @return int
		 * @access public
		 */
		function getAffectedRows()
		{
			$func = $this->getMetaFunction('affected_rows');
			return $func($this->connectionID);	
		}
		
		/**
		 * Returns LIMIT sql clause part for specific db
		 *
		 * @param int $offset
		 * @param int $rows
		 * @return string
		 * @access private
		 */
		function getLimitClause($offset, $rows)
		{
			switch ($this->dbType) {
			
				default:
					return 'LIMIT '.$offset.','.$rows;
					break;
			}
		}
		
		/**
		 * Returns last error code occured
		 *
		 * @return int
		 */
		function getErrorCode()
		{
			return $this->errorCode;
		}
		
		/**
		 * Returns last error message
		 *
		 * @return string
		 * @access public
		 */
		function getErrorMsg()
		{
			return $this->errorMessage;
		}
		
		/**
		 * Correctly quotes a string so that all strings are escaped. We prefix and append
		 * to the string single-quotes.
		 * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
		 * 
		 * @param s			the string to quote
		 * @param [magic_quotes]	if $s is GET/POST var, set to get_magic_quotes_gpc().
		 *				This undoes the stupidity of magic quotes for GPC.
		 *
		 * @return  quoted string to be sent back to database
		 */
		function qstr($s,$magic_quotes=false)
		{
			$replaceQuote = "\\'";
			if (!$magic_quotes)
			{
				if ($replaceQuote[0] == '\\')
				{
					// only since php 4.0.5
					$s = str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
					//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
				}
				return  "'".str_replace("'",$replaceQuote,$s)."'";
			}

			// undo magic quotes for "
			$s = str_replace('\\"','"',$s);

			if($replaceQuote == "\\'")  // ' already quoted, no need to change anything
			{
				return "'$s'";
			}
			else  // change \' to '' for sybase/mssql
			{
				$s = str_replace('\\\\','\\',$s);
				return "'".str_replace("\\'",$replaceQuote,$s)."'";
			}
		}
	}
?>