thinkphp5 多数据库怎么连接数据库

1162人阅读
代码里原有的注释已经非常完善了。不需要我在做什么了。。。。。
thinkphp5.x之数据库操作相关解析 Db类
thinkphp5.x之Collection(集合)解析 php集合
namespace think\db;
use PDOStatement;
use think\Collection;
use think\Db;
use think\db\exception\BindParamException;
use think\db\Query;
use think\Debug;
use think\Exception;
use think\exception\PDOException;
use think\Log;
* @method Query table(string $table) 指定数据表(含前缀)
* @method Query name(string $name) 指定数据表(不含前缀)
abstract class Connection
PDOStatement PDO操作实例 */
protected $PDOStatement;
string 当前SQL指令 */
protected $queryStr = '';
protected $numRows = 0;
protected $transTimes = 0;
protected $error = '';
PDO[] 数据库连接ID 支持多个连接 */
protected $links = [];
PDO 当前连接ID */
protected $linkID;
protected $linkRead;
protected $linkWrite;
protected $resultSetType = 'array';
protected $fetchType = PDO::FETCH_ASSOC;
protected $attrCase = PDO::CASE_LOWER;
protected static $event = [];
protected $query = [];
protected $config = [
'hostname'
'database'
'username'
'password'
'hostport'
=& 'utf8',
'rw_separate'
'master_num'
'slave_no'
'fields_strict'
'resultset_type' =& 'array',
'auto_timestamp' =& false,
'sql_explain'
=& '\\think\\db\\Query',
protected $params = [
PDO::ATTR_CASE
=& PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE
=& PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS
=& PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES =& false,
PDO::ATTR_EMULATE_PREPARES
* @param array $config 数据库配置数组
public function __construct(array $config = [])
if (!empty($config)) {
$this-&config = array_merge($this-&config, $config);
* @param string $model 模型类名称
* @param string $queryClass 查询对象类名
* @return Query
public function model($model, $queryClass = '')
if (!isset($this-&query[$model])) {
= $queryClass ?: $this-&config['query'];
$this-&query[$model] = new $class($this, $model);
return $this-&query[$model];
* @param string
$method 方法名称
* @param array
$args 调用参数
* @return mixed
public function __call($method, $args)
if (!isset($this-&query['database'])) {
= $this-&config['query'];
$this-&query['database'] = new $class($this);
return call_user_func_array([$this-&query['database'], $method], $args);
* @param array $config 连接信息
* @return string
abstract protected function parseDsn($config);
* @param string $tableName
* @return array
abstract public function getFields($tableName);
* @param string $dbName
* @return array
abstract public function getTables($dbName);
* @param string $sql
* @return array
abstract protected function getExplain($sql);
* @param array $info 字段信息
* @return array
public function fieldCase($info)
switch ($this-&attrCase) {
case PDO::CASE_LOWER:
$info = array_change_key_case($info);
case PDO::CASE_UPPER:
$info = array_change_key_case($info, CASE_UPPER);
case PDO::CASE_NATURAL:
return $info;
* @param string $config 配置名称
* @return mixed
public function getConfig($config = '')
return $config ? $this-&config[$config] : $this-&
* @param string|array
$config 配置名称
* @param mixed
$value 配置值
* @return void
public function setConfig($config, $value = '')
if (is_array($config)) {
$this-&config = array_merge($this-&config, $config);
$this-&config[$config] = $value;
* @param array
$config 连接参数
* @param integer
$linkNum 连接序号
* @param array|bool
$autoConnection 是否自动连接主数据库(用于分布式)
* @return PDO
* @throws Exception
public function connect(array $config = [], $linkNum = 0, $autoConnection = false)
if (!isset($this-&links[$linkNum])) {
if (!$config) {
$config = $this-&
$config = array_merge($this-&config, $config);
if (isset($config['params']) && is_array($config['params'])) {
$params = $config['params'] + $this-&
$params = $this-&
$this-&attrCase = $params[PDO::ATTR_CASE];
if (isset($config['resultset_type'])) {
$this-&resultSetType = $config['resultset_type'];
if (empty($config['dsn'])) {
$config['dsn'] = $this-&parseDsn($config);
if ($config['debug']) {
$startTime = microtime(true);
$this-&links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params);
if ($config['debug']) {
Log::record('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn'], 'sql');
} catch (\PDOException $e) {
if ($autoConnection) {
Log::record($e-&getMessage(), 'error');
return $this-&connect($autoConnection, $linkNum);
return $this-&links[$linkNum];
public function free()
$this-&PDOStatement = null;
* @return \PDO|false
public function getPdo()
if (!$this-&linkID) {
return false;
return $this-&linkID;
* @param string
$sql sql指令
* @param array
$bind 参数绑定
* @param boolean
$master 是否在主服务器读操作
* @param bool|string
$class 指定返回的数据集对象
* @return mixed
* @throws BindParamException
* @throws PDOException
public function query($sql, $bind = [], $master = false, $class = false)
$this-&initConnect($master);
if (!$this-&linkID) {
return false;
$this-&queryStr = $this-&getRealSql($sql, $bind);
if (!empty($this-&PDOStatement)) {
$this-&free();
Db::$queryTimes++;
$this-&debug(true);
$this-&PDOStatement = $this-&linkID-&prepare($sql);
$this-&bindValue($bind);
$result = $this-&PDOStatement-&execute();
$this-&debug(false);
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
return $this-&getResult($class, $procedure);
} catch (\PDOException $e) {
throw new PDOException($e, $this-&config, $this-&queryStr);
* @param string
$sql sql指令
* @param array
$bind 参数绑定
* @return int
* @throws BindParamException
* @throws PDOException
public function execute($sql, $bind = [])
$this-&initConnect(true);
if (!$this-&linkID) {
return false;
$this-&queryStr = $this-&getRealSql($sql, $bind);
if (!empty($this-&PDOStatement)) {
$this-&free();
Db::$executeTimes++;
$this-&debug(true);
$this-&PDOStatement = $this-&linkID-&prepare($sql);
$this-&bindValue($bind);
$result = $this-&PDOStatement-&execute();
$this-&debug(false);
$this-&numRows = $this-&PDOStatement-&rowCount();
return $this-&numR
} catch (\PDOException $e) {
throw new PDOException($e, $this-&config, $this-&queryStr);
* @param string
$sql 带参数绑定的sql语句
* @param array
$bind 参数绑定列表
* @return string
public function getRealSql($sql, array $bind = [])
if ($bind) {
foreach ($bind as $key =& $val) {
$value = is_array($val) ? $val[0] : $val;
= is_array($val) ? $val[1] : PDO::PARAM_STR;
if (PDO::PARAM_STR == $type) {
$value = $this-&quote($value);
$sql = is_numeric($key) ?
substr_replace($sql, $value, strpos($sql, '?'), 1) :
str_replace(
[':' . $key . ')', ':' . $key . ',', ':' . $key . ' '],
[$value . ')', $value . ',', $value . ' '],
$sql . ' ');
return $sql;
* @param array $bind 要绑定的参数列表
* @return void
* @throws \think\Exception
protected function bindValue(array $bind = [])
foreach ($bind as $key =& $val) {
$param = is_numeric($key) ? $key + 1 : ':' . $key;
if (is_array($val)) {
$result = $this-&PDOStatement-&bindValue($param, $val[0], $val[1]);
$result = $this-&PDOStatement-&bindValue($param, $val);
if (!$result) {
throw new BindParamException(
"Error occurred
when binding parameters '{$param}'",
$this-&config,
$this-&queryStr,
* @param bool|string
$class true 返回PDOStatement 字符串用于指定返回的类名
* @param bool
$procedure 是否存储过程
* @return mixed
protected function getResult($class = '', $procedure = false)
if (true === $class) {
return $this-&PDOS
if ($procedure) {
return $this-&procedure($class);
= $this-&PDOStatement-&fetchAll($this-&fetchType);
$this-&numRows = count($result);
if (!empty($class)) {
$result = new $class($result);
} elseif ('collection' == $this-&resultSetType) {
$result = new Collection($result);
return $result;
* @param bool|string $class true 返回PDOStatement 字符串用于指定返回的类名
* @return array
protected function procedure($class)
$item = [];
$result = $this-&getResult($class);
if ($result) {
$item[] = $result;
} while ($this-&PDOStatement-&nextRowset());
$this-&numRows = count($item);
return $item;
* @param callable $callback 数据操作方法回调
* @return mixed
* @throws PDOException
* @throws \Exception
* @throws \Throwable
public function transaction($callback)
$this-&startTrans();
$result = null;
if (is_callable($callback)) {
$result = call_user_func_array($callback, [$this]);
$this-&commit();
return $result;
} catch (\Exception $e) {
$this-&rollback();
} catch (\Throwable $e) {
$this-&rollback();
* @return void
public function startTrans()
$this-&initConnect(true);
if (!$this-&linkID) {
return false;
++$this-&transT
if (1 == $this-&transTimes) {
$this-&linkID-&beginTransaction();
} elseif ($this-&transTimes & 1 && $this-&supportSavepoint()) {
$this-&linkID-&exec(
$this-&parseSavepoint('trans' . $this-&transTimes)
* @return void
* @throws PDOException
public function commit()
$this-&initConnect(true);
if (1 == $this-&transTimes) {
$this-&linkID-&commit();
--$this-&transT
* @return void
* @throws PDOException
public function rollback()
$this-&initConnect(true);
if (1 == $this-&transTimes) {
$this-&linkID-&rollBack();
} elseif ($this-&transTimes & 1 && $this-&supportSavepoint()) {
$this-&linkID-&exec(
$this-&parseSavepointRollBack('trans' . $this-&transTimes)
$this-&transTimes = max(0, $this-&transTimes - 1);
protected function supportSavepoint()
return false;
* @return string
protected function parseSavepoint($name)
return 'SAVEPOINT ' . $name;
* @return string
protected function parseSavepointRollBack($name)
return 'ROLLBACK TO SAVEPOINT ' . $name;
* @param array $sqlArray SQL批处理指令
* @return boolean
public function batchQuery($sqlArray = [])
if (!is_array($sqlArray)) {
return false;
$this-&startTrans();
foreach ($sqlArray as $sql) {
$this-&execute($sql);
$this-&commit();
} catch (\Exception $e) {
$this-&rollback();
return true;
* @param boolean $execute 是否包含所有查询
* @return integer
public function getQueryTimes($execute = false)
return $execute ? Db::$queryTimes + Db::$executeTimes : Db::$queryTimes;
* @return integer
public function getExecuteTimes()
return Db::$executeTimes;
public function close()
$this-&linkID = null;
* @return string
public function getLastSql()
return $this-&queryS
* @param string
自增序列名
* @return string
public function getLastInsID($sequence = null)
return $this-&linkID-&lastInsertId($sequence);
* @return integer
public function getNumRows()
return $this-&numR
* @return string
public function getError()
if ($this-&PDOStatement) {
$error = $this-&PDOStatement-&errorInfo();
$error = $error[1] . ':' . $error[2];
$error = '';
if ('' != $this-&queryStr) {
$error .= "\n [ SQL语句 ] : " . $this-&queryS
return $error;
* @param string $str SQL字符串
* @param bool
$master 是否主库查询
* @return string
public function quote($str, $master = true)
$this-&initConnect($master);
return $this-&linkID ? $this-&linkID-&quote($str) : $str;
* @param boolean $start 调试开始标记 true 开始 false 结束
* @param string
$sql 执行的SQL语句 留空自动获取
* @return void
protected function debug($start, $sql = '')
if (!empty($this-&config['debug'])) {
if ($start) {
Debug::remark('queryStartTime', 'time');
Debug::remark('queryEndTime', 'time');
$runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime');
= $sql ?: $this-&queryS
= $sql . ' [ RunTime:' . $runtime . 's ]';
if ($this-&config['sql_explain'] && 0 === stripos(trim($sql), 'select')) {
$result = $this-&getExplain($sql);
$this-&trigger($sql, $runtime, $result);
* @param callable $callback 回调方法
* @return void
public function listen($callback)
self::$event[] = $callback;
* @param string
$sql SQL语句
* @param float
$runtime SQL运行时间
* @param mixed
$explain SQL分析
* @return bool
protected function trigger($sql, $runtime, $explain = [])
if (!empty(self::$event)) {
foreach (self::$event as $callback) {
if (is_callable($callback)) {
call_user_func_array($callback, [$sql, $runtime, $explain]);
Log::record('[ SQL ] ' . $sql . ' [ RunTime:' . $runtime . 's ]', 'sql');
if (!empty($explain)) {
Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql');
* @param boolean $master 是否主服务器
* @return void
protected function initConnect($master = true)
if (!empty($this-&config['deploy'])) {
if ($master) {
if (!$this-&linkWrite) {
$this-&linkWrite = $this-&multiConnect(true);
$this-&linkID = $this-&linkW
if (!$this-&linkRead) {
$this-&linkRead = $this-&multiConnect(false);
$this-&linkID = $this-&linkR
} elseif (!$this-&linkID) {
$this-&linkID = $this-&connect();
* @param boolean $master 主服务器
* @return PDO
protected function multiConnect($master = false)
$_config = [];
foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
$_config[$name] = explode(',', $this-&config[$name]);
$m = floor(mt_rand(0, $this-&config['master_num'] - 1));
if ($this-&config['rw_separate']) {
if ($master)
} elseif (is_numeric($this-&config['slave_no'])) {
$r = $this-&config['slave_no'];
$r = floor(mt_rand($this-&config['master_num'], count($_config['hostname']) - 1));
$r = floor(mt_rand(0, count($_config['hostname']) - 1));
$dbMaster = false;
if ($m != $r) {
$dbMaster = [];
foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
$dbMaster[$name] = isset($_config[$name][$m]) ? $_config[$name][$m] : $_config[$name][0];
$dbConfig = [];
foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
$dbConfig[$name] = isset($_config[$name][$r]) ? $_config[$name][$r] : $_config[$name][0];
return $this-&connect($dbConfig, $r, $r == $m ? false : $dbMaster);
public function __destruct()
if ($this-&PDOStatement) {
$this-&free();
$this-&close();
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:530917次
积分:6799
积分:6799
排名:第2922名
原创:244篇
转载:48篇
评论:39条
主页: php套件: 邮箱:(#换为@) QQ:
(8)(26)(13)(3)(19)(37)(15)(1)(4)(1)(2)(1)(4)(3)(3)(1)(3)(5)(3)(28)(5)(1)(6)(4)(4)(7)(2)(9)(5)(7)(1)(4)(11)(4)(3)(3)(1)(1)(6)(1)(3)(5)(2)(1)(4)(1)(2)(1)(8)(4)(3)(1)(1)thinkphp5 数据库 链接
代码里原有的注释已经非常完善了。不需要我在做什么了。。。。。
namespace think\
use think\C
use think\Db;
use think\db\exception\BindParamE
use think\db\Q
use think\D
use think\E
use think\exception\PDOE
use think\L
连接 抽象类
* Class Connection
* @package think
* @method Query table(string $table) 指定数据表(含前缀)
* @method Query name(string $name) 指定数据表(不含前缀)
abstract class Connection
/** @var PDOStatement PDO操作实例 */
protected $PDOS
/** @var string 当前SQL指令 */
protected $queryStr = '';
// 返回或者影响记录数
protected $numRows = 0;
// 事务指令数
protected $transTimes = 0;
// 错误信息
protected $error = '';
/** @var PDO[] 数据库连接ID 支持多个连接 */
protected $links = [];
/** @var PDO 当前连接ID */
protected $linkID;
protected $linkR
protected $linkW
// 查询结果类型
protected $resultSetType = 'array';
// 查询结果类型
protected $fetchType = PDO::FETCH_ASSOC;
// 字段属性大小写
protected $attrCase = PDO::CASE_LOWER;
// 监听回调
protected static $event = [];
// 查询对象
protected $query = [];
// 数据库连接参数配置
protected $config = [
// 数据库类型
'type'
=& '',
// 服务器地址
'hostname'
=& '',
// 数据库名
'database'
=& '',
'username'
=& '',
'password'
=& '',
'hostport'
=& '',
// 连接dsn
'dsn'
=& '',
// 数据库连接参数
'params'
// 数据库编码默认采用utf8
'charset'
=& 'utf8',
// 数据库表前缀
'prefix'
=& '',
// 数据库调试模式
'debug'
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy'
// 数据库读写是否分离 主从式有效
'rw_separate'
// 读写分离后 主服务器数量
'master_num'
// 指定从服务器序号
'slave_no'
=& '',
// 是否严格检查字段是否存在
'fields_strict'
// 数据集返回类型
'resultset_type' =& 'array',
// 自动写入时间戳字段
'auto_timestamp' =& false,
// 是否需要进行SQL性能分析
'sql_explain'
// Builder类
'builder'
=& '',
// Query类
'query'
=& '\\think\\db\\Query',
// PDO连接参数
protected $params = [
PDO::ATTR_CASE
=& PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE
=& PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS
=& PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES =& false,
PDO::ATTR_EMULATE_PREPARES
* 架构函数 读取数据库配置信息
* @access public
* @param array $config 数据库配置数组
public function __construct(array $config = [])
if (!empty($config)) {
$this-&config = array_merge($this-&config, $config);
* 创建指定模型的查询对象
* @access public
* @param string $model 模型类名称
* @param string $queryClass 查询对象类名
* @return Query
public function model($model, $queryClass = '')
// 查询对象 query 中 $model 不存在时 执行
if (!isset($this-&query[$model])) {
// 查询类 命名空间地址
= $queryClass ?: $this-&config['query'];
//实例化该类 并赋值
//$this 当前对象,$model 模型类名称
$this-&query[$model] = new $class($this, $model);
return $this-&query[$model];
* 调用Query类的查询方法
* @access public
* @param string
$method 方法名称
* @param array
$args 调用参数
* @return mixed
public function __call($method, $args)
if (!isset($this-&query['database'])) {
// 查询类 命名空间地址
= $this-&config['query'];
// 实例化并赋值
//$this 当前对象
$this-&query['database'] = new $class($this);
return call_user_func_array([$this-&query['database'], $method], $args);
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
abstract protected function parseDsn($config);
* 取得数据表的字段信息
* @access public
* @param string $tableName
* @return array
abstract public function getFields($tableName);
* 取得数据库的表信息
* @access public
* @param string $dbName
* @return array
abstract public function getTables($dbName);
* SQL性能分析
* @access protected
* @param string $sql
* @return array
abstract protected function getExplain($sql);
* 对返数据表字段信息进行大小写转换出来
* @access public
* @param array $info 字段信息
* @return array
public function fieldCase($info)
// 字段大小写转换
switch ($this-&attrCase) {
case PDO::CASE_LOWER:
$info = array_change_key_case($info);
case PDO::CASE_UPPER:
$info = array_change_key_case($info, CASE_UPPER);
case PDO::CASE_NATURAL:
// 不做转换
* 获取数据库的配置参数
* @access public
* @param string $config 配置名称
* @return mixed
public function getConfig($config = '')
return $config ? $this-&config[$config] : $this-&
* 设置数据库的配置参数
* @access public
* @param string|array
$config 配置名称
* @param mixed
$value 配置值
* @return void
public function setConfig($config, $value = '')
if (is_array($config)) {
$this-&config = array_merge($this-&config, $config);
$this-&config[$config] = $
* 连接数据库方法
* @access public
* @param array
$config 连接参数
* @param integer
$linkNum 连接序号
* @param array|bool
$autoConnection 是否自动连接主数据库(用于分布式)
* @return PDO
* @throws Exception
public function connect(array $config = [], $linkNum = 0, $autoConnection = false)
// 数据库链接links中序号为$linkNum是否已存在,如果不存在时执行
if (!isset($this-&links[$linkNum])) {
// $config 连接参数不存在时,自动赋值
if (!$config) {
$config = $this-&
// 合并链接参数
$config = array_merge($this-&config, $config);
// 连接参数
if (isset($config['params']) && is_array($config['params'])) {
$params = $config['params'] + $this-&
$params = $this-&
// 记录当前字段属性大小写设置
$this-&attrCase = $params[PDO::ATTR_CASE];
// 记录数据集返回类型
if (isset($config['resultset_type'])) {
$this-&resultSetType = $config['resultset_type'];
// $config 连接参数 dsn 不存在时
if (empty($config['dsn'])) {
// 解析$config变量,生成 dsn 格式
$config['dsn'] = $this-&parseDsn($config);
if ($config['debug']) {
$startTime = microtime(true);
// PDO 链接实例化
$this-&links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params);
// 数据库配置 是否开启 debug 模式
if ($config['debug']) {
// 记录数据库连接信息
Log::record('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn'], 'sql');
} catch (\PDOException $e) {
// 分布式数据库 是否开启
if ($autoConnection) {
Log::record($e-&getMessage(), 'error');
// 重新链接
return $this-&connect($autoConnection, $linkNum);
return $this-&links[$linkNum];
* 释放查询结果
* @access public
public function free()
$this-&PDOStatement =
* 获取PDO对象
* @access public
* @return \PDO|false
public function getPdo()
if (!$this-&linkID) {
return $this-&linkID;
* 执行查询 返回数据集
* @access public
* @param string
$sql sql指令
* @param array
$bind 参数绑定
* @param boolean
$master 是否在主服务器读操作
* @param bool|string
$class 指定返回的数据集对象
* @return mixed
* @throws BindParamException
* @throws PDOException
public function query($sql, $bind = [], $master = false, $class = false)
$this-&initConnect($master);
if (!$this-&linkID) {
// 根据参数绑定组装最终的SQL语句
$this-&queryStr = $this-&getRealSql($sql, $bind);
//释放前次的查询结果
if (!empty($this-&PDOStatement)) {
$this-&free();
Db::$queryTimes++;
// 调试开始
$this-&debug(true);
$this-&PDOStatement = $this-&linkID-&prepare($sql);
// 参数绑定
$this-&bindValue($bind);
// 执行查询
$result = $this-&PDOStatement-&execute();
// 调试结束
$this-&debug(false);
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
return $this-&getResult($class, $procedure);
} catch (\PDOException $e) {
throw new PDOException($e, $this-&config, $this-&queryStr);
* 执行语句
* @access public
* @param string
$sql sql指令
* @param array
$bind 参数绑定
* @return int
* @throws BindParamException
* @throws PDOException
public function execute($sql, $bind = [])
$this-&initConnect(true);
if (!$this-&linkID) {
// 根据参数绑定组装最终的SQL语句
$this-&queryStr = $this-&getRealSql($sql, $bind);
//释放前次的查询结果
if (!empty($this-&PDOStatement)) {
$this-&free();
Db::$executeTimes++;
// 调试开始
$this-&debug(true);
$this-&PDOStatement = $this-&linkID-&prepare($sql);
// 参数绑定操作
$this-&bindValue($bind);
// 执行语句
$result = $this-&PDOStatement-&execute();
// 调试结束
$this-&debug(false);
$this-&numRows = $this-&PDOStatement-&rowCount();
return $this-&numR
} catch (\PDOException $e) {
throw new PDOException($e, $this-&config, $this-&queryStr);
* 根据参数绑定组装最终的SQL语句 便于调试
* @access public
* @param string
$sql 带参数绑定的sql语句
* @param array
$bind 参数绑定列表
* @return string
public function getRealSql($sql, array $bind = [])
if ($bind) {
foreach ($bind as $key =& $val) {
$value = is_array($val) ? $val[0] : $
= is_array($val) ? $val[1] : PDO::PARAM_STR;
if (PDO::PARAM_STR == $type) {
$value = $this-&quote($value);
// 判断占位符
$sql = is_numeric($key) ?
substr_replace($sql, $value, strpos($sql, '?'), 1) :
str_replace(
[':' . $key . ')', ':' . $key . ',', ':' . $key . ' '],
[$value . ')', $value . ',', $value . ' '],
$sql . ' ');
* 参数绑定
* 支持 ['name'=&'value','id'=&123] 对应命名占位符
* 或者 ['value',123] 对应问号占位符
* @access public
* @param array $bind 要绑定的参数列表
* @return void
* @throws \think\Exception
protected function bindValue(array $bind = [])
foreach ($bind as $key =& $val) {
$param = is_numeric($key) ? $key + 1 : ':' . $
if (is_array($val)) {
$result = $this-&PDOStatement-&bindValue($param, $val[0], $val[1]);
$result = $this-&PDOStatement-&bindValue($param, $val);
if (!$result) {
throw new BindParamException(
&Error occurred
when binding parameters '{$param}'&,
$this-&config,
$this-&queryStr,
* 获得数据集
* @access protected
* @param bool|string
$class true 返回PDOStatement 字符串用于指定返回的类名
* @param bool
$procedure 是否存储过程
* @return mixed
protected function getResult($class = '', $procedure = false)
if (true === $class) {
// 返回PDOStatement对象处理
return $this-&PDOS
if ($procedure) {
// 存储过程返回结果
return $this-&procedure($class);
= $this-&PDOStatement-&fetchAll($this-&fetchType);
$this-&numRows = count($result);
if (!empty($class)) {
// 返回指定数据集对象类
$result = new $class($result);
} elseif ('collection' == $this-&resultSetType) {
// 返回数据集Collection对象
$result = new Collection($result);
* 获得存储过程数据集
* @access protected
* @param bool|string $class true 返回PDOStatement 字符串用于指定返回的类名
* @return array
protected function procedure($class)
$item = [];
$result = $this-&getResult($class);
if ($result) {
$item[] = $
} while ($this-&PDOStatement-&nextRowset());
$this-&numRows = count($item);
* 执行数据库事务
* @access public
* @param callable $callback 数据操作方法回调
* @return mixed
* @throws PDOException
* @throws \Exception
* @throws \Throwable
public function transaction($callback)
$this-&startTrans();
if (is_callable($callback)) {
$result = call_user_func_array($callback, [$this]);
$this-&commit();
} catch (\Exception $e) {
$this-&rollback();
} catch (\Throwable $e) {
$this-&rollback();
* 启动事务
* @access public
* @return void
public function startTrans()
// 初始化数据库连接
$this-&initConnect(true);
if (!$this-&linkID) {
++$this-&transT
if (1 == $this-&transTimes) {
$this-&linkID-&beginTransaction();
} elseif ($this-&transTimes & 1 && $this-&supportSavepoint()) {
$this-&linkID-&exec(
$this-&parseSavepoint('trans' . $this-&transTimes)
* 用于非自动提交状态下面的查询提交
* @access public
* @return void
* @throws PDOException
public function commit()
$this-&initConnect(true);
if (1 == $this-&transTimes) {
$this-&linkID-&commit();
--$this-&transT
* 事务回滚
* @access public
* @return void
* @throws PDOException
public function rollback()
$this-&initConnect(true);
if (1 == $this-&transTimes) {
$this-&linkID-&rollBack();
} elseif ($this-&transTimes & 1 && $this-&supportSavepoint()) {
$this-&linkID-&exec(
$this-&parseSavepointRollBack('trans' . $this-&transTimes)
$this-&transTimes = max(0, $this-&transTimes - 1);
* 是否支持事务嵌套
* @return bool
protected function supportSavepoint()
* 生成定义保存点的SQL
* @param $name
* @return string
protected function parseSavepoint($name)
return 'SAVEPOINT ' . $
* 生成回滚到保存点的SQL
* @param $name
* @return string
protected function parseSavepointRollBack($name)
return 'ROLLBACK TO SAVEPOINT ' . $
* 批处理执行SQL语句
* 批处理的指令都认为是execute操作
* @access public
* @param array $sqlArray SQL批处理指令
* @return boolean
public function batchQuery($sqlArray = [])
if (!is_array($sqlArray)) {
// 自动启动事务支持
$this-&startTrans();
foreach ($sqlArray as $sql) {
$this-&execute($sql);
// 提交事务
$this-&commit();
} catch (\Exception $e) {
$this-&rollback();
* 获得查询次数
* @access public
* @param boolean $execute 是否包含所有查询
* @return integer
public function getQueryTimes($execute = false)
return $execute ? Db::$queryTimes + Db::$executeTimes : Db::$queryT
* 获得执行次数
* @access public
* @return integer
public function getExecuteTimes()
return Db::$executeT
* 关闭数据库
* @access public
public function close()
$this-&linkID =
* 获取最近一次查询的sql语句
* @access public
* @return string
public function getLastSql()
return $this-&queryS
* 获取最近插入的ID
* @access public
* @param string
自增序列名
* @return string
public function getLastInsID($sequence = null)
return $this-&linkID-&lastInsertId($sequence);
* 获取返回或者影响的记录数
* @access public
* @return integer
public function getNumRows()
return $this-&numR
* 获取最近的错误信息
* @access public
* @return string
public function getError()
if ($this-&PDOStatement) {
$error = $this-&PDOStatement-&errorInfo();
$error = $error[1] . ':' . $error[2];
$error = '';
if ('' != $this-&queryStr) {
$error .= &\n [ SQL语句 ] : & . $this-&queryS
* SQL指令安全过滤
* @access public
* @param string $str SQL字符串
* @param bool
$master 是否主库查询
* @return string
public function quote($str, $master = true)
$this-&initConnect($master);
return $this-&linkID ? $this-&linkID-&quote($str) : $
* 数据库调试 记录当前SQL及分析性能
* @access protected
* @param boolean $start 调试开始标记 true 开始 false 结束
* @param string
$sql 执行的SQL语句 留空自动获取
* @return void
protected function debug($start, $sql = '')
if (!empty($this-&config['debug'])) {
// 开启数据库调试模式
if ($start) {
Debug::remark('queryStartTime', 'time');
// 记录操作结束时间
Debug::remark('queryEndTime', 'time');
$runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime');
= $sql ?: $this-&queryS
= $sql . ' [ RunTime:' . $runtime . 's ]';
// SQL性能分析
if ($this-&config['sql_explain'] && 0 === stripos(trim($sql), 'select')) {
$result = $this-&getExplain($sql);
// SQL监听
$this-&trigger($sql, $runtime, $result);
* 监听SQL执行
* @access public
* @param callable $callback 回调方法
* @return void
public function listen($callback)
self::$event[] = $
* 触发SQL事件
* @access protected
* @param string
$sql SQL语句
* @param float
$runtime SQL运行时间
* @param mixed
$explain SQL分析
* @return bool
protected function trigger($sql, $runtime, $explain = [])
if (!empty(self::$event)) {
foreach (self::$event as $callback) {
if (is_callable($callback)) {
call_user_func_array($callback, [$sql, $runtime, $explain]);
// 未注册监听则记录到日志中
Log::record('[ SQL ] ' . $sql . ' [ RunTime:' . $runtime . 's ]', 'sql');
if (!empty($explain)) {
Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql');
* 初始化数据库连接
* @access protected
* @param boolean $master 是否主服务器
* @return void
protected function initConnect($master = true)
if (!empty($this-&config['deploy'])) {
// 采用分布式数据库
if ($master) {
if (!$this-&linkWrite) {
$this-&linkWrite = $this-&multiConnect(true);
$this-&linkID = $this-&linkW
if (!$this-&linkRead) {
$this-&linkRead = $this-&multiConnect(false);
$this-&linkID = $this-&linkR
} elseif (!$this-&linkID) {
// 默认单数据库
$this-&linkID = $this-&connect();
* 连接分布式服务器
* @access protected
* @param boolean $master 主服务器
* @return PDO
protected function multiConnect($master = false)
$_config = [];
// 分布式数据库配置解析
foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
$_config[$name] = explode(',', $this-&config[$name]);
// 主服务器序号
$m = floor(mt_rand(0, $this-&config['master_num'] - 1));
if ($this-&config['rw_separate']) {
// 主从式采用读写分离
if ($master) // 主服务器写入
} elseif (is_numeric($this-&config['slave_no'])) {
// 指定服务器读
$r = $this-&config['slave_no'];
// 读操作连接从服务器 每次随机连接的数据库
$r = floor(mt_rand($this-&config['master_num'], count($_config['hostname']) - 1));
// 读写操作不区分服务器 每次随机连接的数据库
$r = floor(mt_rand(0, count($_config['hostname']) - 1));
$dbMaster =
if ($m != $r) {
$dbMaster = [];
foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
$dbMaster[$name] = isset($_config[$name][$m]) ? $_config[$name][$m] : $_config[$name][0];
$dbConfig = [];
foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
$dbConfig[$name] = isset($_config[$name][$r]) ? $_config[$name][$r] : $_config[$name][0];
return $this-&connect($dbConfig, $r, $r == $m ? false : $dbMaster);
* 析构方法
* @access public
public function __destruct()
// 释放查询
if ($this-&PDOStatement) {
$this-&free();
// 关闭连接
$this-&close();}

我要回帖

更多关于 thinkphp5 数据库操作 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信