Files
framework/Library/Think/Db/Driver.php

1020 lines
36 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// +----------------------------------------------------------------------
// | TOPThink [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011 http://topthink.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think\Db;
use Think\Config;
use Think\Debug;
use Think\Log;
use PDO;
abstract class Driver {
// PDO操作实例
protected $PDOStatement = null;
// 当前操作所属的模型名
protected $model = '_think_';
// 当前SQL指令
protected $queryStr = '';
protected $modelSql = [];
// 最后插入ID
protected $lastInsID = null;
// 返回或者影响记录数
protected $numRows = 0;
// 事务指令数
protected $transTimes = 0;
// 错误信息
protected $error = '';
// 数据库连接ID 支持多个连接
protected $linkID = [];
// 当前连接ID
protected $_linkID = null;
// 数据库连接参数配置
protected $config = [
'dbms' => '', // 数据库类型
'connection' => [
'hostname' => '127.0.0.1', // 服务器地址
'database' => '', // 数据库名
'username' => 'root', // 用户名
'password' => '', // 密码
'hostport' => '', // 端口
'socket' => '',
'dsn' => '', //
],
'params' => [], // 数据库连接参数
'charset' => 'utf8', // 数据库编码默认采用utf8
'prefix' => '', // 数据库表前缀
'debug' => false, // 数据库调试模式
'deploy' => 0, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'rw_separate' => false, // 数据库读写是否分离 主从式有效
'master_num' => 1, // 读写分离后 主服务器数量
'slave_no' => '', // 指定从服务器序号
];
// 数据库表达式
protected $comparison = ['eq'=>'=','neq'=>'<>','gt'=>'>','egt'=>'>=','lt'=>'<','elt'=>'<=','notlike'=>'NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN'];
// 查询表达式
protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%';
// 查询次数
protected $queryTimes = 0;
// 执行次数
protected $executeTimes = 0;
// PDO连接参数
protected $options = [
PDO::ATTR_CASE => PDO::CASE_LOWER,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
];
protected $bind = []; // 参数绑定
/**
* 架构函数 读取数据库配置信息
* @access public
* @param array $config 数据库配置数组
*/
public function __construct($config=''){
if(!empty($config)) {
$this->config = array_merge($this->config,$config);
$this->config['params'] = $this->options+$this->config['params'];
}
}
/**
* 连接数据库方法
* @access public
*/
public function connect($config='',$linkNum=0) {
if ( !isset($this->linkID[$linkNum]) ) {
if(empty($config)) $config = $this->config['connection'];
try{
if(empty($config['dsn'])) {
$config['dsn'] = $this->config['dbms'].':dbname='.$config['database'].';host='.$config['hostname'];
if(!empty($config['hostport'])) {
$config['dsn'] .= ';port='.$config['hostport'];
}elseif(!empty($config['socket'])){
$config['dsn'] .= ';unix_socket='.$config['socket'];
}
}
$this->linkID[$linkNum] = new PDO( $config['dsn'], $config['username'], $config['password'],$this->config['params']);
}catch (\PDOException $e) {
E($e->getMessage());
}
if(!empty($this->config['charset'])) {
$this->linkID[$linkNum]->exec('SET NAMES '.$this->config['charset']);
}
}
return $this->linkID[$linkNum];
}
/**
* 释放查询结果
* @access public
*/
public function free() {
$this->PDOStatement = null;
}
/**
* 执行查询 返回数据集
* @access public
* @param string $str sql指令
* @param array $bind 参数绑定
* @return mixed
*/
public function query($str,$bind=[]) {
$this->initConnect(false);
if ( !$this->_linkID ) return false;
$this->queryStr = $str;
if(!empty($bind)){
$this->queryStr .= '[ '.print_r($bind,true).' ]';
}
//释放前次的查询结果
if ( !empty($this->PDOStatement) ) $this->free();
$this->queryTimes++;
// 调试开始
$this->debug(true);
$this->PDOStatement = $this->_linkID->prepare($str);
if(false === $this->PDOStatement)
E($this->error());
$result = $this->PDOStatement->execute($bind);
// 调试结束
$this->debug(false);
if ( false === $result ) {
$this->error();
return false;
} else {
return $this->getResult();
}
}
/**
* 执行语句
* @access public
* @param string $str sql指令
* @param array $bind 参数绑定
* @return integer
*/
public function execute($str,$bind=[]) {
$this->initConnect(true);
if ( !$this->_linkID ) return false;
$this->queryStr = $str;
if(!empty($bind)){
$this->queryStr .= '[ '.print_r($bind,true).' ]';
}
//释放前次的查询结果
if ( !empty($this->PDOStatement) ) $this->free();
$this->executeTimes++;
// 记录开始执行时间
$this->debug(true);
$this->PDOStatement = $this->_linkID->prepare($str);
if(false === $this->PDOStatement) {
E($this->error());
}
$result = $this->PDOStatement->execute($bind);
$this->debug(false);
if ( false === $result) {
$this->error();
return false;
} else {
$this->numRows = $this->PDOStatement->rowCount();
if(preg_match("/^\s*(INSERT\s+INTO|REPLACE\s+INTO)\s+/i", $str)) {
$this->lastInsID = $this->getLastInsertId();
}
return $this->numRows;
}
}
/**
* 获取最后插入id
* @access public
* @return integer
*/
public function getLastInsertId() {
return $this->_linkID->lastInsertId();
}
/**
* 启动事务
* @access public
* @return void
*/
public function startTrans() {
$this->initConnect(true);
if ( !$this->_linkID ) return false;
//数据rollback 支持
if ($this->transTimes == 0) {
$this->_linkID->beginTransaction();
}
$this->transTimes++;
return ;
}
/**
* 用于非自动提交状态下面的查询提交
* @access public
* @return boolen
*/
public function commit() {
if ($this->transTimes > 0) {
$result = $this->_linkID->commit();
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}
/**
* 事务回滚
* @access public
* @return boolen
*/
public function rollback() {
if ($this->transTimes > 0) {
$result = $this->_linkID->rollback();
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}
/**
* 获得所有的查询数据
* @access private
* @return array
*/
private function getResult() {
//返回数据集
$result = $this->PDOStatement->fetchAll(PDO::FETCH_ASSOC);
$this->numRows = count( $result );
return $result;
}
/**
* 获得查询次数
* @access public
* @param boolean $execute 是否包含所有查询
* @return integer
*/
public function getQueryTimes($execute=false){
return $execute?$this->queryTimes+$this->executeTimes:$this->queryTimes;
}
/**
* 获得执行次数
* @access public
* @return integer
*/
public function getExecuteTimes(){
return $this->executeTimes;
}
/**
* 关闭数据库
* @access public
*/
public function close() {
$this->_linkID = null;
}
/**
* 数据库错误信息
* 并显示当前的SQL语句
* @access public
* @return string
*/
public function error() {
if($this->PDOStatement) {
$error = $this->PDOStatement->errorInfo();
$this->error = $error[1].':'.$error[2];
}else{
$this->error = '';
}
if('' != $this->queryStr){
$this->error .= "\n [ SQL语句 ] : ".$this->queryStr;
}
Log::record($this->error,'ERR');
return $this->error;
}
/**
* 设置锁机制
* @access protected
* @return string
*/
protected function parseLock($lock=false) {
return $lock? ' FOR UPDATE ' : '';
}
/**
* set分析
* @access protected
* @param array $data
* @return string
*/
protected function parseSet($data) {
foreach ($data as $key=>$val){
$value = $this->parseValue($val);
if(is_scalar($value)) {// 过滤非标量数据
$name = md5($key);
$set[] = $this->parseKey($key).'=:T'.$name;
$this->bindParam($name,$value);
}
}
return ' SET '.implode(',',$set);
}
/**
* 参数绑定
* @access protected
* @param string $name 绑定参数名
* @param mixed $value 绑定值
* @return void
*/
protected function bindParam($name,$value){
$this->bind[':T'.$name] = $value;
}
/**
* 字段名分析
* @access protected
* @param string $key
* @return string
*/
protected function parseKey(&$key) {
return $key;
}
/**
* value分析
* @access protected
* @param mixed $value
* @return string
*/
protected function parseValue($value) {
if(is_string($value)) {
$value = strpos($value,':') === 0 ? $this->escapeString($value) : '\''.$this->escapeString($value).'\'';
}elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp'){
$value = $this->escapeString($value[1]);
}elseif(is_array($value)) {
$value = array_map([$this, 'parseValue'],$value);
}elseif(is_bool($value)){
$value = $value ? '1' : '0';
}elseif(is_null($value)){
$value = 'null';
}
return $value;
}
/**
* field分析
* @access protected
* @param mixed $fields
* @return string
*/
protected function parseField($fields) {
if(is_string($fields) && strpos($fields,',')) {
$fields = explode(',',$fields);
}
if(is_array($fields)) {
// 完善数组方式传字段名的支持
// 支持 'field1'=>'field2' 这样的字段别名定义
$array = [];
foreach ($fields as $key=>$field){
if(!is_numeric($key))
$array[] = $this->parseKey($key).' AS '.$this->parseKey($field);
else
$array[] = $this->parseKey($field);
}
$fieldsStr = implode(',', $array);
}elseif(is_string($fields) && !empty($fields)) {
$fieldsStr = $this->parseKey($fields);
}else{
$fieldsStr = '*';
}
//TODO 如果是查询全部字段并且是join的方式那么就把要查的表加个别名以免字段被覆盖
return $fieldsStr;
}
/**
* table分析
* @access protected
* @param mixed $table
* @return string
*/
protected function parseTable($tables) {
if(is_array($tables)) {// 支持别名定义
$array = [];
foreach ($tables as $table=>$alias){
if(!is_numeric($table))
$array[] = $this->parseKey($table).' '.$this->parseKey($alias);
else
$array[] = $this->parseKey($table);
}
$tables = $array;
}elseif(is_string($tables)){
$tables = explode(',',$tables);
array_walk($tables, [&$this, 'parseKey']);
}
return implode(',',$tables);
}
/**
* where分析
* @access protected
* @param mixed $where
* @return string
*/
protected function parseWhere($where) {
$whereStr = '';
if(is_string($where)) {
// 直接使用字符串条件
$whereStr = $where;
}else{ // 使用数组表达式
$operate = isset($where['_logic'])?strtoupper($where['_logic']):'';
if(in_array($operate,['AND','OR','XOR'])){
// 定义逻辑运算规则 例如 OR XOR AND NOT
$operate = ' '.$operate.' ';
unset($where['_logic']);
}else{
// 默认进行 AND 运算
$operate = ' AND ';
}
foreach ($where as $key=>$val){
$whereStr .= '( ';
if(0===strpos($key,'_')) {
// 解析特殊条件表达式
$whereStr .= $this->parseThinkWhere($key,$val);
}else{
// 查询字段的安全过滤
if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/',trim($key))){
E(L('_EXPRESS_ERROR_').':'.$key);
}
// 多条件支持
$multi = is_array($val) && isset($val['_multi']);
$key = trim($key);
if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段
$array = explode('|',$key);
$str = [];
foreach ($array as $m=>$k){
$v = $multi?$val[$m]:$val;
$str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')';
}
$whereStr .= implode(' OR ',$str);
}elseif(strpos($key,'&')){
$array = explode('&',$key);
$str = [];
foreach ($array as $m=>$k){
$v = $multi?$val[$m]:$val;
$str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')';
}
$whereStr .= implode(' AND ',$str);
}else{
$whereStr .= $this->parseWhereItem($this->parseKey($key),$val);
}
}
$whereStr .= ' )'.$operate;
}
$whereStr = substr($whereStr,0,-strlen($operate));
}
return empty($whereStr)?'':' WHERE '.$whereStr;
}
// where子单元分析
protected function parseWhereItem($key,$val) {
$whereStr = '';
if(is_array($val)) {
if(is_string($val[0])) {
if(preg_match('/^(EQ|NEQ|GT|EGT|LT|ELT)$/i',$val[0])) { // 比较运算
$whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]);
}elseif(preg_match('/^(NOTLIKE|LIKE)$/i',$val[0])){// 模糊查找
if(is_array($val[1])) {
$likeLogic = isset($val[2])?strtoupper($val[2]):'OR';
if(in_array($likeLogic,['AND','OR','XOR'])){
$likeStr = $this->comparison[strtolower($val[0])];
$like = [];
foreach ($val[1] as $item){
$like[] = $key.' '.$likeStr.' '.$this->parseValue($item);
}
$whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';
}
}else{
$whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]);
}
}elseif('bind'==strtolower($val[0])){ // 使用表达式
$whereStr .= ' ('.$key.' = :'.$val[1].') ';
}elseif('exp'==strtolower($val[0])){ // 使用表达式
$whereStr .= ' ('.$key.' '.$val[1].') ';
}elseif(preg_match('/IN/i',$val[0])){ // IN 运算
if(isset($val[2]) && 'exp'==$val[2]) {
$whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1];
}else{
if(is_string($val[1])) {
$val[1] = explode(',',$val[1]);
}
$zone = implode(',',$this->parseValue($val[1]));
$whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')';
}
}elseif(preg_match('/BETWEEN/i',$val[0])){ // BETWEEN运算
$data = is_string($val[1])? explode(',',$val[1]):$val[1];
$whereStr .= ' ('.$key.' '.strtoupper($val[0]).' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]).' )';
}else{
E(L('_EXPRESS_ERROR_').':'.$val[0]);
}
}else {
$count = count($val);
$rule = isset($val[$count-1])?strtoupper($val[$count-1]):'';
if(in_array($rule,['AND','OR','XOR'])) {
$count = $count -1;
}else{
$rule = 'AND';
}
for($i=0;$i<$count;$i++) {
$data = is_array($val[$i])?$val[$i][1]:$val[$i];
if('exp'==strtolower($val[$i][0])) {
$whereStr .= '('.$key.' '.$data.') '.$rule.' ';
}else{
$op = is_array($val[$i])?$this->comparison[strtolower($val[$i][0])]:'=';
$whereStr .= '('.$key.' '.$op.' '.$this->parseValue($data).') '.$rule.' ';
}
}
$whereStr = substr($whereStr,0,-4);
}
}else {
//对字符串类型字段采用模糊匹配
if($this->conf['db_like_fields'] && preg_match('/('.$this->conf['db_like_fields'].')/i',$key)) {
$val = '%'.$val.'%';
$whereStr .= $key.' LIKE '.$this->parseValue($val);
}else {
$whereStr .= $key.' = '.$this->parseValue($val);
}
}
return $whereStr;
}
/**
* 特殊条件分析
* @access protected
* @param string $key
* @param mixed $val
* @return string
*/
protected function parseThinkWhere($key,$val) {
$whereStr = '';
switch($key) {
case '_string':
// 字符串模式查询条件
$whereStr = $val;
break;
case '_complex':
// 复合查询条件
$whereStr = substr($this->parseWhere($val),6);
break;
case '_query':
// 字符串模式查询条件
parse_str($val,$where);
if(isset($where['_logic'])) {
$op = ' '.strtoupper($where['_logic']).' ';
unset($where['_logic']);
}else{
$op = ' AND ';
}
$array = [];
foreach ($where as $field=>$data)
$array[] = $this->parseKey($field).' = '.$this->parseValue($data);
$whereStr = implode($op,$array);
break;
}
return $whereStr;
}
/**
* limit分析
* @access protected
* @param mixed $lmit
* @return string
*/
protected function parseLimit($limit) {
return !empty($limit)? ' LIMIT '.$limit.' ':'';
}
/**
* join分析
* @access protected
* @param mixed $join
* @return string
*/
protected function parseJoin($join) {
$joinStr = '';
if(!empty($join)) {
if(is_array($join)) {
foreach ($join as $key=>$_join){
if(false !== stripos($_join,'JOIN'))
$joinStr .= ' '.$_join;
else
$joinStr .= ' LEFT JOIN ' .$_join;
}
}else{
$joinStr .= ' LEFT JOIN ' .$join;
}
}
//将__TABLE_NAME__这样的字符串替换成正规的表名,并且带上前缀和后缀
$joinStr = preg_replace("/__([A-Z_-]+)__/esU",$this->config['prefix'].".strtolower('$1')",$joinStr);
return $joinStr;
}
/**
* order分析
* @access protected
* @param mixed $order
* @return string
*/
protected function parseOrder($order) {
if(is_array($order)) {
$array = [];
foreach ($order as $key=>$val){
if(is_numeric($key)) {
$array[] = $this->parseKey($val);
}else{
$array[] = $this->parseKey($key).' '.$val;
}
}
$order = implode(',',$array);
}
return !empty($order)? ' ORDER BY '.$order:'';
}
/**
* group分析
* @access protected
* @param mixed $group
* @return string
*/
protected function parseGroup($group) {
return !empty($group)? ' GROUP BY '.$group:'';
}
/**
* having分析
* @access protected
* @param string $having
* @return string
*/
protected function parseHaving($having) {
return !empty($having)? ' HAVING '.$having:'';
}
/**
* comment分析
* @access protected
* @param string $comment
* @return string
*/
protected function parseComment($comment) {
return !empty($comment)? ' /* '.$comment.' */':'';
}
/**
* distinct分析
* @access protected
* @param mixed $distinct
* @return string
*/
protected function parseDistinct($distinct) {
return !empty($distinct)? ' DISTINCT ' :'';
}
/**
* union分析
* @access protected
* @param mixed $union
* @return string
*/
protected function parseUnion($union) {
if(empty($union)) return '';
if(isset($union['_all'])) {
$str = 'UNION ALL ';
unset($union['_all']);
}else{
$str = 'UNION ';
}
foreach ($union as $u){
$sql[] = $str.(is_array($u)?$this->buildSelectSql($u):$u);
}
return implode(' ',$sql);
}
/**
* 参数绑定分析
* @access protected
* @param array $bind
* @return array
*/
protected function parseBind($bind){
return array_merge($this->bind,$bind);
}
/**
* 插入记录
* @access public
* @param mixed $data 数据
* @param array $options 参数表达式
* @param boolean $replace 是否replace
* @return false | integer
*/
public function insert($data,$options=[],$replace=false) {
$values = $fields = [];
$this->model = $options['model'];
foreach ($data as $key=>$val){
$value = $this->parseValue($val);
if(is_scalar($value)) { // 过滤非标量数据
$fields[] = $this->parseKey($key);
$name = md5($key);
$values[] = ':'.$name;
$this->bindParam($name,$value);
}
}
$sql = ($replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')';
$sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
$sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:[]));
}
/**
* 通过Select方式插入记录
* @access public
* @param string $fields 要插入的数据表字段名
* @param string $table 要插入的数据表名
* @param array $option 查询数据参数
* @return false | integer
*/
public function selectInsert($fields,$table,$options=[]) {
$this->model = $options['model'];
if(is_string($fields)) $fields = explode(',',$fields);
array_walk($fields, [$this, 'parseKey']);
$sql = 'INSERT INTO '.$this->parseTable($table).' ('.implode(',', $fields).') ';
$sql .= $this->buildSelectSql($options);
return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:[]));
}
/**
* 更新记录
* @access public
* @param mixed $data 数据
* @param array $options 表达式
* @return false | integer
*/
public function update($data,$options) {
$this->model = $options['model'];
$sql = 'UPDATE '
.$this->parseTable($options['table'])
.$this->parseSet($data)
.$this->parseWhere(!empty($options['where'])?$options['where']:'')
.$this->parseOrder(!empty($options['order'])?$options['order']:'')
.$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
.$this->parseLock(isset($options['lock'])?$options['lock']:false)
.$this->parseComment(!empty($options['comment'])?$options['comment']:'');
return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:[]));
}
/**
* 删除记录
* @access public
* @param array $options 表达式
* @return false | integer
*/
public function delete($options=[]) {
$this->model = $options['model'];
$sql = 'DELETE FROM '
.$this->parseTable($options['table'])
.$this->parseWhere(!empty($options['where'])?$options['where']:'')
.$this->parseOrder(!empty($options['order'])?$options['order']:'')
.$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
.$this->parseLock(isset($options['lock'])?$options['lock']:false)
.$this->parseComment(!empty($options['comment'])?$options['comment']:'');
return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:[]));
}
/**
* 查找记录
* @access public
* @param array $options 表达式
* @return mixed
*/
public function select($options=[]) {
$this->model = $options['model'];
$sql = $this->buildSelectSql($options);
$cache = isset($options['cache'])?$options['cache']:false;
if($cache) { // 查询缓存检测
$key = is_string($cache['key'])?$cache['key']:md5($sql);
$value = S($key,'',$cache);
if(false !== $value) {
return $value;
}
}
$result = $this->query($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:[]));
if($cache && false !== $result ) { // 查询缓存写入
S($key,$result,$cache);
}
return $result;
}
/**
* 生成查询SQL
* @access public
* @param array $options 表达式
* @return string
*/
public function buildSelectSql($options=[]) {
if(isset($options['page'])) {
// 根据页数计算limit
if(strpos($options['page'],',')) {
list($page,$listRows) = explode(',',$options['page']);
}else{
$page = $options['page'];
}
$page = $page?$page:1;
$listRows= isset($listRows)?$listRows:(is_numeric($options['limit'])?$options['limit']:20);
$offset = $listRows*((int)$page-1);
$options['limit'] = $offset.','.$listRows;
}
$sql = $this->parseSql($this->selectSql,$options);
$sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
return $sql;
}
/**
* 替换SQL语句中表达式
* @access public
* @param array $options 表达式
* @return string
*/
public function parseSql($sql,$options=[]){
$sql = str_replace(
['%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%COMMENT%'],
[
$this->parseTable($options['table']),
$this->parseDistinct(isset($options['distinct'])?$options['distinct']:false),
$this->parseField(!empty($options['field'])?$options['field']:'*'),
$this->parseJoin(!empty($options['join'])?$options['join']:''),
$this->parseWhere(!empty($options['where'])?$options['where']:''),
$this->parseGroup(!empty($options['group'])?$options['group']:''),
$this->parseHaving(!empty($options['having'])?$options['having']:''),
$this->parseOrder(!empty($options['order'])?$options['order']:''),
$this->parseLimit(!empty($options['limit'])?$options['limit']:''),
$this->parseUnion(!empty($options['union'])?$options['union']:''),
$this->parseComment(!empty($options['comment'])?$options['comment']:'')
],$sql);
return $sql;
}
/**
* 获取最近一次查询的sql语句
* @param string $model 模型名
* @access public
* @return string
*/
public function getLastSql($model='') {
return $model?$this->modelSql[$model]:$this->queryStr;
}
/**
* 获取最近插入的ID
* @access public
* @return string
*/
public function getLastInsID() {
return $this->lastInsID;
}
/**
* 获取最近的错误信息
* @access public
* @return string
*/
public function getError() {
return $this->error;
}
/**
* SQL指令安全过滤
* @access public
* @param string $str SQL字符串
* @return string
*/
public function escapeString($str) {
return addslashes($str);
}
/**
* 设置当前操作模型
* @access public
* @param string $model 模型名
* @return void
*/
public function setModel($model){
$this->model = $model;
}
/**
* 数据库调试 记录当前SQL
* @access protected
* @param boolean $start 调试开始标记 true 开始 false 结束
*/
protected function debug($start) {
if($this->config['debug']) {// 开启数据库调试模式
if($start) {
Debug::remark('queryStartTime','time');
}else{
$this->modelSql[$this->model] = $this->queryStr;
//$this->model = '_think_';
// 记录操作结束时间
Debug::remark('queryEndTime','time');
Log::record($this->queryStr.' [ RunTime:'.Debug::getUseTime('queryStartTime','queryEndTime').'s ]','SQL');
}
}
}
/**
* 初始化数据库连接
* @access protected
* @param boolean $master 主服务器
* @return void
*/
protected function initConnect($master=true) {
if(!empty($this->config['deploy']))
// 采用分布式数据库
$this->_linkID = $this->multiConnect($master);
else
// 默认单数据库
if ( !$this->_linkID ) $this->_linkID = $this->connect();
}
/**
* 连接分布式服务器
* @access protected
* @param boolean $master 主服务器
* @return void
*/
protected function multiConnect($master=false) {
static $_config = [];
if(empty($_config)) {
// 缓存分布式数据库配置解析
foreach ($this->config['connection'] as $key=>$val){
$_config[$key] = explode(',',$val);
}
}
// 数据库读写是否分离
if($this->config['rw_separate']){
// 主从式采用读写分离
if($master)
// 主服务器写入
$r = floor(mt_rand(0,$this->config['master_num']-1));
else{
if(is_numeric($this->config['slave_no'])) {// 指定服务器读
$r = $this->config['slave_no'];
}else{
// 读操作连接从服务器
$r = floor(mt_rand($this->config['master_num'],count($_config['hostname'])-1)); // 每次随机连接的数据库
}
}
}else{
// 读写操作不区分服务器
$r = floor(mt_rand(0,count($_config['hostname'])-1)); // 每次随机连接的数据库
}
$db_config = [
'username' => isset($_config['username'][$r])?$_config['username'][$r]:$_config['username'][0],
'password' => isset($_config['password'][$r])?$_config['password'][$r]:$_config['password'][0],
'hostname' => isset($_config['hostname'][$r])?$_config['hostname'][$r]:$_config['hostname'][0],
'hostport' => isset($_config['hostport'][$r])?$_config['hostport'][$r]:$_config['hostport'][0],
'database' => isset($_config['database'][$r])?$_config['database'][$r]:$_config['database'][0],
'dsn' => isset($_config['dsn'][$r])?$_config['dsn'][$r]:$_config['dsn'][0],
];
return $this->connect($db_config,$r);
}
/**
* 析构方法
* @access public
*/
public function __destruct() {
// 释放查询
if ($this->PDOStatement){
$this->free();
}
// 关闭连接
$this->close();
}
}