<?php
/* * *********************************************************
 * [cml] (C)2012 - 3000 cml http://linhecheng.com
 * @Author  linhecheng<linhechengbush@gmail.com>
 * @Date: 13-6-26 上午11:23
 * @version  1.0 
 * cml框架 Mysql Db驱动类
 * *********************************************************** */

defined('CML_PATH') || exit();

import('DbBase',CML_LIB_DRIVER_PATH.DIR_SEP.'Db'.DIR_SEP, '.class.php');
class DbMySql extends DbBase
{

	public function __construct($conf)
	{
		$this->conf = $conf;
		$this->tablepre = $this->conf['MASTER']['TABLEPREFIX'];
		$this->isPdo = false;
	}

	/**
	 * 获取当前db所有表名
	 *
	 * @return array
	 */
	public function getTables()
	{
		$query = $this->query('SHOW TABLES;', $this->rlink);
		$tables = array();
		while($row = mysql_fetch_assoc($query)) {
			$tables[] = $row['Tables_in_'.$this->conf['MASTER']['DBNAME']];
		}
		return $tables;
	}

	/**
	 * 获取表字段
	 *
	 * @param type string $table
	 * @param type mixed $table 表前缀 为null时代表table已经带了前缀
	 * @param type int $filter 0 获取表字段详细信息数组 1获取字段以,号相隔组成的字符串
	 *
	 * @return mixed
	 */
	public function getDbFields($table, $tablepre = null, $filter = 0)
	{
		if($filter == 1 && $GLOBALS['APP_DEBUG']) return '*'; //debug模式时直接返回*
		$table = is_null($tablepre) ? strtolower($table) : $tablepre.strtolower($table);
		$info = F($this->conf['MASTER']['DBNAME'].'.'.$table);
		if(!$info || $GLOBALS['APP_DEBUG']) {
			$result = $this->query('SHOW COLUMNS FROM '.$table);
			while($row = mysql_fetch_assoc($result)) {
				$info[$row['Field']] = array(
					'name'    => $row['Field'],
					'type'    => $row['Type'],
					'notnull' => (bool) ($row['Null'] === ''), // not null is empty, null is yes
					'default' => $row['Default'],
					'primary' => (strtolower($row['Key']) == 'pri'),
					'autoinc' => (strtolower($row['Extra']) == 'auto_increment'),
				);
			}
			F($this->conf['MASTER']['DBNAME'].'.'.$table, $info);
		}          
		if($filter) {
			$info = implode('`,`', array_keys($info));
			$info = '`'.$info.'`';
		}
		return $info;
	}

	/**
	 * 根据key取出数据
	 *
	 * @param string $key get('user-uid-123');
	 * @param bool $and 多个条件之间是否为and  true为and false为or
	 * @return array array('uid'=>123, 'username'=>'abc')
	 *
	 * return array
	 */
	public function get($key, $and = true)
	{
		$return = array();
		list($tablename, $condition) = $this->parseKey($key, $and);
		$tablename = $this->tablepre.$tablename;
		$fields = C('DB_FIELDS_CACHE') ? $this->getDbFields($tablename, null, 1) : '*';
		$result = $this->execute("SELECT {$fields} FROM {$tablename} WHERE {$condition}", $this->rlink);
		while($row = mysql_fetch_assoc($result)) {
			$return[] = $row;
		}
		return $return;
	}

	/**
	 * 根据key 新增/更新 一条数据
	 *
	 * @param string $key eg 'user-uid-$uid'
	 * @param array $data eg: array('username'=>'admin', 'email'=>'linhechengbush@live.com')
	 * @param bool $and 多个条件之间是否为and  true为and false为or
	 *
	 * @return bool
	 */
	public function set($key, $data, $and = true)
	{
		$tablepre = $this->tablepre;
		list($table, $condition) = $this->parseKey($key, $and, 1);
		$tablename = $tablepre.$table;
		if(is_array($data)) {
			$keyarr = array();
			$arr = explode('-', $key);            
			$len = count($arr);
			for($i = 1; $i < $len; $i += 2) {
				if(isset($arr[$i + 1])) {		
					$t = $arr[$i + 1];
					$keyarr[$arr[$i]] = is_numeric($t) ? intval($t) : $t;
				} else {
					$keyarr[$arr[$i]] = null;
				}
			}
			$data += $keyarr;
			$s = self::arrToCondition($data, $table, $tablepre);
			return $this->execute("INSERT INTO {$tablename} SET {$s}", $this->wlink);
		} else {
			return false;
		}
	}

	/**
	 * 根据key更新一条数据
	 *
	 * @param string $key eg 'user-uid-$uid'
	 * @param array $data eg: array('username'=>'admin', 'email'=>'linhechengbush@live.com')
	 * @param bool $and 多个条件之间是否为and  true为and false为or
	 *
	 * @return boolean
	 */
	public function update($key, $data, $and = true)
	{
		$tablepre = $this->tablepre;
		list($tablename, $condition) = $this->parseKey($key, $and, 1, 1);
		$tablename = empty($tablename) ? key($this->_table) : $tablepre.$tablename;
		empty($tablename) && throw_exception(L('_PARSE_SQL_ERROR_NO_TABLE_', null, 'update'));
		$s = self::arrToCondition($data, substr($tablename, strlen($tablepre)), $tablepre);
		$whereCondition = $this->_sql['where'];
		$whereCondition .= empty($condition) ?  '' : (empty($whereCondition) ? 'WHERE ' : '').$condition;
		empty($whereCondition) && throw_exception(L('_PARSE_SQL_ERROR_NO_CONDITION_', null, 'update'));
		return $this->execute("UPDATE {$tablename} SET {$s} {$whereCondition}", $this->wlink);
	}

	/**
	 * 根据key值删除数据
	 *
	 * @param string $key eg: 'user-uid-$uid'
	 * @param bool $and 多个条件之间是否为and  true为and false为or
	 *
	 * @return boolean
	 */
	public function delete($key, $and = true)
	{
		list($tablename, $condition) = $this->parseKey($key, $and, 1, 1);
		$tablename = empty($tablename) ? key($this->_table) : $this->tablepre.$tablename; 	
		empty($tablename) && throw_exception(L('_PARSE_SQL_ERROR_NO_TABLE_', null, 'delete'));
		$whereCondition = $this->_sql['where'];	
		$whereCondition .= empty($condition) ?  '' : (empty($whereCondition) ? 'WHERE ' : '').$condition;
		empty($whereCondition) && throw_exception(L('_PARSE_SQL_ERROR_NO_CONDITION_', null, 'delete'));
		return $this->execute("DELETE FROM {$tablename} {$whereCondition}", $this->wlink);
	}

	/**
	 * 根据表名删除数据
	 *
	 * @param string $tablename
	 *
	 * @return bool
	 */
	public function truncate($tablename)
	{
		$tablename = $this->tablepre.$tablename;
		try {
			$this->query('TRUNCATE '.$tablename); //不存在会报错，但无关紧要
			return true;
		} catch(Exception $e) {
			return false;
		}
	}

	/**
	 * 获取多条数据
	 *
	 * @return array
	 */
	public function select()
	{
		$columns = ($this->_sql['columns'] == '*')  ? ( C('DB_FIELDS_CACHE') ? $this->getDbFields(key($this->_table), null, 1) : '*' ) : $this->_sql['columns'];
		$table = $deper = $joinOn = '';
		foreach($this->_table as $key => $val) {
			if(isset($this->_join[$key])) {
				$deper = ' INNER JOIN';
			} else if(isset($this->_leftJoin[$key])) {
				$deper = ' LEFT JOIN';
			}  else if(isset($this->_rightJoin[$key])) {
				$deper = ' RIGHT JOIN';
			} else {
				!empty($table) && $deper = ' ,';
			}
			if(is_null($val)) {
				$table .= "{$deper} {$key}";
			} else {
				$table .= "{$deper} {$key} AS {$val}";
			}
		}

		while($currentOn = current($this->_joinOn)) {
			$joinOn .= empty($joinOn) ? "ON {$currentOn}" : " AND {$currentOn}";
			next($this->_joinOn);
		}
		empty($joinOn) || $joinOn = ' '.$joinOn;
		$sql = "SELECT $columns FROM {$table} {$joinOn} ".$this->_sql['where'].$this->_sql['groupBy'].$this->_sql['having'].$this->_sql['orderBy'].$this->_sql['limit'];
		empty($table) && throw_exception(L('_PARSE_SQL_ERROR_NO_TABLE_', null, 'select'));
		$return = array();
		$result = $this->execute($sql, $this->rlink);
		while($row = mysql_fetch_assoc($result)) {
			$return[] = $row;
		}
		return $return;
	}

	/**
	 * 获取一条数据
	 *
	 * @param string $sql sql语句
	 * @return  array
	 */
	public function one($sql, $link = null)
	{
		is_null($link) && $link = $this->rlink;
		$result = $this->query($sql, $link);
		return mysql_fetch_assoc($result);
	}

	/**
	 * 统计符合条件的数据有多少条
	 *
	 * @param string $tablename 表名
	 *
	 * @return array
	 */
	public function count($tablename = '')
	{
		$tablename = empty($tablename) ? key($this->_table) : $this->tablepre.$tablename;
		$result = $this->execute("SELECT COUNT(*) AS num FROM {$tablename} ".$this->_sql['where'].$this->_sql['groupBy'].$this->_sql['having'].$this->_sql['orderBy'].$this->_sql['limit'], $this->rlink);
		$return = array();
		while($row = mysql_fetch_assoc($result)) {
			$return[] = $row['num'];
		}
		return $return;
	}

	/**
	 * 取出表某列的最大值
	 *
	 * @param $key string eg： user-id
	 *
	 * @return array
	 */
	public function max($key = '')
	{
		empty($key) && list($tablename, $col) = explode('-', $key);
		$tablename = empty($tablename) ? key($this->_table) : $this->tablepre.$tablename;
		$col = empty($col) ? $this->getPk(substr($tablename, strlen($this->tablepre)), $this->tablepre) : $col;
		$result = $this->execute("SELECT MAX({$col}) AS max FROM {$tablename} ".$this->_sql['where'].$this->_sql['groupBy'].$this->_sql['having'].$this->_sql['orderBy'].$this->_sql['limit'],$this->rlink);
		$return = array();
		while($row = mysql_fetch_assoc($result)) {
			$return[] = $row['max'];
		}
		return $return;
	}

	/**
	 * 取出表某列的最小值
	 *
	 * @param string $key eg： user-id
	 *
	 * @return array
	 */
	public function min($key = '')
	{
		empty($key) && list($tablename, $col) = explode('-', $key);
		$tablename = empty($tablename) ? key($this->_table) : $this->tablepre.$tablename;
		$col = empty($col) ? $this->getPk(substr($tablename, strlen($this->tablepre)), $this->tablepre) : $col;
		$result = $this->execute("SELECT MIN({$col}) AS min FROM {$tablename} ".$this->_sql['where'].$this->_sql['groupBy'].$this->_sql['having'].$this->_sql['orderBy'].$this->_sql['limit'],$this->rlink);
		$return = array();
		while($row = mysql_fetch_assoc($result)) {
			$return[] = $row['min'];
		}
		return $return;
	}



	/**
	 *执行sql并返回资源标识符 适用于非orm select
	 *
	 * @param string $sql
	 * @param object $link
	 *
	 * @return mixed
	 */
	public function query($sql, $link = null)
	{
		is_null($link) && $link = $this->wlink;
		$result = mysql_query($sql, $link);
		//$this->reset(); 执行非orm sql不用reset
		if(!$result) {
			throw_exception(L('_DB_QUERY_ERROR_')." [{$sql}] ".mysql_error());
		}
		$GLOBALS['APP_DEBUG'] &&  CmlDebug::addTipInfo($sql, 2);
		return $result;
	}

	/**
	 * 执行sql并返回资源标识符 适用于orm select
	 *
	 * @param string $sql
	 * @param object $link
	 *
	 * @return mixed
	 */
	public function execute($sql, $link = null)
	{
		$this->reset(); //执行orm sql reset()
		return $this->query($sql, $link);
	}

	/**
	 * 执行一条sql语句并返回受影响的行数 适用于 update|insert|delete php_mysql统一用mysql_query
	 *
	 * @param string $sql
	 * @param object $link
	 *
	 * @return int||false
	 */
	public function exec($sql, $link = null)
	{
		is_null($link) && $link = $this->wlink;
		$this->query($sql, $link);
		$result = $this->affectedRows($link);
		if($result === 0) return true; //此时sql为执行成功，只是影响的行数是0而已
		if($result === -1) return false; //执行失败
		return $result; //影响的行数 > 0
	}

	/**
	 *获取query执行的结果
	 *
	 *@param $handle mysql result
	 *
	 *@return array
	 */
	public function getQuery($handle)
	{
		$return = array();
		while($row = mysql_fetch_assoc($handle)) {
			$return[] = $row;
		}
		return $return;
	}

	/**
	 *返回select语句返回结果集中行的数目
	 *
	 *@param $handle mysql result
	 *
	 *@return int
	 */
	public function numRows($handle)
	{
		return mysql_num_rows($handle);
	}

	/**
	 * 返回INSERT，UPDATE 或 DELETE 查询所影响的记录行数。
	 *
	 * @param resource $handle mysql link
	 *
	 * @return int
	 */
	public function affectedRows($handle)
	{
		empty($handle) && $handle = $this->wlink;
		return mysql_affected_rows($handle);
	}

	/**
	 * 返回结果集中一个字段的值
	 *
	 * @param resource $result 结果标识符
	 * @param int $row 行号
	 * @param string | int 要获取的字段
	 *
	 * @return mixed
	 */
	public function result($result, $row = 1, $field = 0)
	{
		return mysql_result($result, $row, $field);
	}

	/**
	 *获取上一INSERT的主键值
	 *
	 *@param resource $link
	 *
	 *@return int 
	 */
	public function insertId($link = null)
	{
		is_null($link) && $link = $this->wlink;
		return mysql_insert_id($link);
	}

	/**
	 * Db连接
	 *
	 * @param string $host
	 * @param string $user,
	 * @param string $password
	 * @param string $name
	 * @param string $charset
	 * @param string $engine
	 *
	 * @return resource
	 */
	public function connect($host, $username, $password, $dbname, $charset = 'utf8', $engine = '', $pconnect = false)
	{
		$link = $pconnect ? mysql_pconnect($host, $username, $password) : mysql_connect($host, $username, $password);
		if(!$link) {
			throw_exception(L('_DB_CONNECT_FAIL_').mysql_error());
		}
		$bool = mysql_select_db($dbname, $link);
		if(!$bool) {
			throw_exception(L('_DB_SELECTDB_ERROR_', null, $dbname).mysql_error());
		}
		if(!empty($engine) && $engine == 'InnoDB') {
			$this->query('SET innodb_flush_log_at_trx_commit=2', $link);
		}
		$this->query("SET names $charset", $link);
		return $link;
	}

	/**
	 * 指定字段的值+1
	 *
	 * @param string $key user-id-1
	 * @param int $val
	 * @param string $field 要改变的字段
	 *
	 * @return bool
	 */
	public function increment($key, $val = 1, $field = null)
	{
		list($tablename, $condition) = self::parseKey($key, true);
		if(is_null($field) || empty($tablename) || empty($condition)) return false;
		$val = intval($val);
		return $this->exec('UPDATE  `'.$this->tablepre.$tablename."` SET  `{$field}` =  `{$field}` + {$val}  WHERE  $condition");
	}

	/**
	 * 指定字段的值-1
	 *
	 * @param string $key user-id-1
	 * @param int $val
	 * @param string $field 要改变的字段
	 *
	 * @return bool
	 */
	public function decrement($key, $val = 1, $field = null)
	{
		list($tablename, $condition) = self::parseKey($key, true);
		if(is_null($field) || empty($tablename) || empty($condition)) return false;
		$val = intval($val);
		return $this->exec('UPDATE  `'.$this->tablepre.$tablename."` SET  `$field` =  `$field` - $val  WHERE  $condition");
	}

	/**
	 *析构函数
	 *
	 */
	public function __destruct()
	{
		$this->close();
	}

	/**
	 * 关闭连接
	 *
	 */
	public function close()
	{
		if(is_resource($this->wlink)) {
			C('SESSION_USER') || mysql_close($this->wlink); //开启会话自定义保存时，不关闭防止会话保存失败
		}
	}

	/**
	 *获取mysql 版本
	 *
	 *@param resource $link
	 *
	 *@return string
	 */
	public function version($link = null)
	{
		is_null($link) && $link = $this->wlink;
		return mysql_get_server_info($link);
	}

	/**
	 * 开启事务
	 *
	 * @return bool
	 */
	public function  startTransAction()
	{
		mysql_query('START TRANSACTION', $this->wlink);
	}

	/**
	 * 提交事务
	 *
	 * @return bool
	 */
	public function commit()
	{
		mysql_query('COMMIT', $this->wlink);
	}

	/**
	 * 回滚事务
	 *
	 * @return bool
	 */
	public function rollBack()
	{
		mysql_query('ROLLBACK', $this->wlink);
	}
}