<?php

/**
 *  mysqli.php 数据库实现类
 *
 * @copyright			(C) 2019 YUNYECMS
 * @lastmodify			2019-2-24
 */
final class yymysqli
{

	/**
	 * 数据库配置信息
	 */
	private $config;

	/**
	 * 数据库连接资源句柄
	 */
	private $link;

	/**
	 *	最后一次查询的资源句柄
	 */
	public $lastresult;

	/**
	 *  统计数据库查询次数
	 */
	public $querycount = 0;

	/**
	 * 数据类实例
	 */
	static private $mysqlobj = array();

	private function __construct($configs)
	{
		$this->config = $configs;
		$this->connect();
	}

	public function __destruct()
	{
		$this->close();
	}

	/* 兼容同名函数实例化*/
	/*
	private function db_mysqli($configs){
		$this->config=$configs;
		$this->connect();	
	}	
	*/
	private function __clone()
	{
	}
	private function connect()
	{

		if (!$this->link = @mysqli_connect($this->config['hostname'], $this->config['username'], $this->config['password'], $this->config['database'], $this->config['port'] ? intval($this->config['port']) : 3306)) {
			$this->DisplayError('Can not Connect to MySQL server', "hook_mysql_install");
		}
		if ($this->GetVersion(true) > '4.1') {
			$charset = isset($this->config['dbchar']) ? $this->config['dbchar'] : 'utf8';
			$serverset = $charset ? "character_set_connection='$charset',character_set_results='$charset',character_set_client=binary" : '';
			$serverset .= $this->GetVersion() > '5.0.1' ? ((empty($serverset) ? '' : ',') . " sql_mode='' ") : '';
			$serverset && $this->link->query("SET $serverset");
		}
	}
	public function DisplayError($message = '', $hook = '')
	{
		if (!empty($hook)) {
			$this->$hook($message);
		} else {
			$html = "<b><a href='http://www.yunyecms.com/' target='_blank'><i class='fa fa-life-ring'></i> Need Help? </a></b>";
			if (APP_DEBUG) {
				$errorinfo = $message . '<br/>';
				$errorinfo .= '<b>MySQL Error: </b>' . $this->GetError() . '<br/>';
				$errorinfo .= '<b>MySQL Errno: </b>' . $this->GetErrno() . '<br/>';
				$errorinfo .= $html;
				messagebox("数据库错误", 'nback', "info", "", $errorinfo);
				exit;
			} else {
				messagebox("数据库错误", 'nback', "info", "", $html);
				exit;
			}
		}
	}

	public function hook_mysql_install($message = '')
	{
		if (!file_exists(ROOT . '/data/install.lock')) {
			if (file_exists(ROOT . '/install')) {
				echo "<script>";
				echo "window.location.href='" . ROOT . "/install'";
				echo "</script>";
			} else {
				$html = "<a href='http://www.yunyecms.com/faq' target='_blank' ><i class='fa fa-life-ring'></i> Need Help?</a>";
				if (APP_DEBUG) {
					$errorinfo = '<b>sql 语句: </b>' . $message . '<br/>';
					$errorinfo .= '<b>MySQL Error: </b>' . $this->GetError() . '<br/>';
					$errorinfo .= '<b>MySQL Errno: </b>' . $this->GetErrno() . '<br/>';
					$errorinfo .= $html;
					messagebox("数据库错误", 'nback', "info", "", $errorinfo);
					exit;
				} else {
					messagebox($message, 'nback', "info", $html);
					exit;
				}
			}
		} else {
			$html = "<a href='http://www.yunyecms.com/faq' target='_blank' ><i class='fa fa-circle-o'></i> Need Help?</a>";
			if ($this->config['debug']) {
				$errorinfo = '<b>sql 语句: </b>' . $message . '<br/>';
				$errorinfo .= '<b>MySQL Error: </b>' . $this->GetError() . '<br/>';
				$errorinfo .= '<b>MySQL Errno: </b>' . $this->GetErrno() . '<br/>';
				$errorinfo .= $html;
				messagebox($message, 'nback', "info", $errorinfo);
				exit;
			} else {
				messagebox($message, 'nback', "info", $html);
				exit;
			}
		}
		exit;
	}

	final static public function GetObject($configs = array('hostname' => '', 'database' => ''))
	{
		$db = $configs['hostname'] . $configs['database'];
		if (!isset(self::$mysqlobj[$db])) {
			if (!is_array($configs)) {
				$this->DisplayError("The configuration file is not an array");
			}
			$C = __CLASS__;
			self::$mysqlobj[$db] = new $C($configs);
		}
		return self::$mysqlobj[$db];
	}


	public function close()
	{
		return (($this->link) ? $this->link->close() : mysqli_connect_error());
	}

	public function GetVersion($version = false)
	{

		$mysql_version = $this->link->server_info;
		$mysql_version = explode(".", trim($mysql_version));
		if ($version) {
			return $mysql_version[0] . '.' . $mysql_version[1];
		} else {
			return $mysql_version[0] . '.' . $mysql_version[1] . '.' . $mysql_version[2];
		}
	}

	public function GetError()
	{
		return (($this->link) ? $this->link->error : mysqli_connect_error());
	}

	public function GetErrno()
	{
		return intval(($this->link) ? $this->link->errno : mysqli_connect_error());
	}

	/**
	 * 执行sql查询
	 * @param $data 		需要查询的字段值[例`name`,`gender`,`birthday`]
	 * @param $table 		数据表
	 * @param $where 		查询条件[例`name`='$name']
	 * @param $limit 		返回结果范围[例：10或10,10 默认为空]
	 * @param $order 		排序方式	[默认按数据库默认方式排序]
	 * @param $group 		分组方式	[默认为空]
	 * @param $key 			返回数组按键名排序
	 */
	public function getlist($data, $table, $where = '', $limit = '20', $order = '', $group = '', $info = array('type' => 1, 'key' => ''))
	{
		$where = $where == '' ? '' : ' WHERE ' . $where;
		$order = $order == '' ? '' : ' ORDER BY ' . $order;
		$group = $group == '' ? '' : ' GROUP BY ' . $group;
		$limit = $limit == '' ? '' : ' LIMIT ' . $limit;
		$field = explode(',', $data);
		array_walk($field, array($this, 'add_special_char'));
		$data = implode(',', $field);
		$sql = 'SELECT ' . $data . ' FROM `' . $this->config['database'] . '`.`' . $table . '`' . $where . $group . $order . $limit;
		$this->execute($sql);
		$type = isset($info['type']) ? $info['type'] : 1;
		$key = isset($info['key']) ? $info['key'] : '';
		return $this->get_fetch_array($type, $key);
	}



	public function insert($data, $table, $return_insert_id = false, $replace = false)
	{
		if (!is_array($data) || $table == '' || count($data) == 0) {
			return false;
		}
		$fielddata = array_keys($data);
		$valuedata = array_values($data);
		array_walk($fielddata, array($this, 'add_special_char'));
		array_walk($valuedata, array($this, 'addquote'));
		$field = implode(',', $fielddata);
		$value = implode(',', $valuedata);
		$strsql = $replace ? 'REPLACE INTO' : 'INSERT INTO';
		$strsql = $strsql . ' `' . $this->config['database'] . '`.`' . $table . '`(' . $field . ') VALUES (' . $value . ')';
		$query = $this->execute($strsql);
		return $return_insert_id ? $this->insert_id() : $query;
	}


	public function update($data, $table, $where = '')
	{
		$where = $where ? ' WHERE ' . $where : '';
		if (is_array($data)) {
			$datas = array();
			foreach ($data as $key => $value) {
				$datas[] = "`" . $key . "`='" . mysqli_real_escape_string($this->link, $value) . "'";
			}
			$setdata = implode(',', array_values($datas));
		} else {
			$setdata = mysqli_real_escape_string($this->link, $data);
		}
		$sql = 'UPDATE `' . $this->config['database'] . '`.`' . $table . '` SET ' . $setdata . ' ' . $where;
		return $this->execute($sql);
	}


	/**
	 * 获取最后一次添加记录的主键号
	 * @return int 
	 */

	public function insert_id()
	{
		return $this->link->insert_id;
	}


	/**
	 * 数据库查询执行方法
	 * @param $sql 要执行的sql语句
	 * @return 查询资源句柄
	 */

	public function execute($sql, $error_exit = true)
	{
		if ($error_exit) {
			$this->lastresult = $this->link->query($sql) or $this->displayerror($sql);
		} else {
			$this->lastresult = $this->link->query($sql);
		}
		$this->querycount++;
		return $this->lastresult;
	}


	/**
	 * 释放查询资源
	 * @return void
	 */
	public function free_result()
	{
		if ($this->lastresult) $this->lastresult->free();
	}

	/**
	 * 检查表是否存在
	 * @param $table 表名
	 * @return boolean
	 */
	public function table_exists($table)
	{
		$tables = $this->list_tables();
		return in_array($table, $tables) ? 1 : 0;
	}
	/*
	* 列表
	*/
	public function list_tables()
	{
		$tables = array();
		$this->execute("SHOW TABLES");
		while ($r = mysqli_fetch_assoc($this->lastresult)) {
			$tables[] = $r['Tables_in_' . $this->config['database']];
		}
		return $tables;
	}

	//数据总数查询，需要一个结果集
	public function num_rows($lastresult)
	{
		$lastresult->num_rows;
	}

	//数据总数查询

	public function num_count($lastresult)
	{
		$data = $this->get_one($lastresult, MYSQLI_NUM);
		return $data[0];
	}
	/**
	 * 获取最后数据库操作影响到的条数
	 * @return int
	 */

	public function affected_rows()
	{
		return $this->link->affected_rows;
	}

	/**
	 * 返回一条查询结果集
	 * $lastresult      外部的结果集，如果没有就调用内部的结果集
	 * @param $type		返回结果集类型	
	 * 					MYSQL_ASSOC，MYSQL_NUM 和 MYSQL_BOTH
	 * @return array
	 */
	final public function get_one($lastresult = null, $type = 1)
	{
		if (!$type) $type = 1;
		$type = intval($type);
		if (gettype($lastresult) === "object") {
			$list =  $lastresult->fetch_array($type);
		} else {
			$list = $this->lastresult->fetch_array($type);
		}
		$this->free_result();
		return $list;
	}




	public function delete($where = '', $table)
	{
		//$where=$this->escape_string($where);
		$where = $where ? ' WHERE ' . $where : '';
		$sql = 'DELETE FROM `' . $table . '`' . $where;
		return $this->link->query($sql);
	}


	/**
	 * 遍历查询结果集
	 * @param $type		返回结果集类型	
	 * 					MYSQL_ASSOC，MYSQL_NUM 和 MYSQL_BOTH
	 * @param $type		按照键名排序	
	 * @return array
	 */
	final public function get_fetch_array($type = 1, $key = '')
	{
		if (gettype($this->lastresult) !== "object") {
			$this->free_result();
			return false;
		}
		$datalist = $data = array();
		if (!$key) {
			while ($data = $this->lastresult->fetch_array($type)) {
				$datalist[] = $data;
			}
		} else {
			while ($data = $this->lastresult->fetch_array($type)) {
				$datalist[$data[$key]] = $data;
			}
		}
		$this->free_result();
		return $datalist;
	}



	final public  function sqls($where, $font = ' AND ', $op = '=')
	{
		if (is_array($where)) {
			$sql = '';
			foreach ($where as $key => $val) {
				$sql .= $sql ? " $font `$key` $op '$val' " : " `$key` $op '$val'";
			}
			return $sql;
		} else {
			return $where;
		}
	}
	public function escape_string($string)
	{
		return mysqli_real_escape_string($this->link, $string);
	}

	public function addquote(&$value, $quotation = 1)
	{
		$value = mysqli_real_escape_string($this->link, $value);
		if ($quotation) {
			$quote = '\'';
		} else {
			$quote = "'";
		}
		$value = $quote . $value . $quote;
		return $value;
	}
	//加反引号
	protected function parseKey(&$key)
	{
		$key   =  trim($key);
		if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
			$key = '`' . $key . '`';
		}
		return $key;
	}

	/**
	 * 对字段两边加反引号，以保证数据库安全
	 * @param $value 数组值
	 */
	public function add_special_char(&$value)
	{
		if ('*' == $value || false !== strpos($value, '(') || false !== strpos($value, '.') || false !== strpos($value, '`')) {
			//不处理包含* 或者 使用了sql方法。
		} else {
			$value = '`' . trim($value) . '`';
		}
		if (preg_match("/\b(select|insert|update|delete)\b/i", $value)) {
			$value = preg_replace("/\b(select|insert|update|delete)\b/i", '', $value);
		}
		return $value;
	}
}
