// +---------------------------------------------------------------------- declare (strict_types = 1); namespace think\db; use Psr\SimpleCache\CacheInterface; use think\DbManager; /** * 数据库连接基础类. */ abstract class Connection implements ConnectionInterface { const PARAM_INT = 1; const PARAM_STR = 2; const PARAM_BOOL = 5; const PARAM_FLOAT = 21; /** * 当前SQL指令. * * @var string */ protected $queryStr = ''; /** * 返回或者影响记录数. * * @var int */ protected $numRows = 0; /** * 事务指令数. * * @var int */ protected $transTimes = 0; /** * 错误信息. * * @var string */ protected $error = ''; /** * 数据库连接ID 支持多个连接. * * @var array */ protected $links = []; /** * 当前连接ID. * * @var object */ protected $linkID; /** * 当前读连接ID. * * @var object */ protected $linkRead; /** * 当前写连接ID. * * @var object */ protected $linkWrite; /** * 数据表信息. * * @var array */ protected $info = []; /** * 查询开始时间. * * @var float */ protected $queryStartTime; /** * Builder对象 * * @var Builder */ protected $builder; /** * Db对象 * * @var DbManager */ protected $db; /** * 是否读取主库. * * @var bool */ protected $readMaster = false; /** * 数据库连接参数配置. * * @var array */ protected $config = []; /** * 缓存对象 * * @var CacheInterface */ protected $cache; /** * 架构函数 读取数据库配置信息. * * @param array $config 数据库配置数组 */ public function __construct(array $config = []) { if (!empty($config)) { $this->config = array_merge($this->config, $config); } // 创建Builder对象 $class = $this->getBuilderClass(); $this->builder = new $class($this); } /** * 获取当前的builder实例对象 * * @return Builder */ public function getBuilder() { return $this->builder; } /** * 创建查询对象 */ public function newQuery() { $class = $this->getQueryClass(); /** @var BaseQuery $query */ $query = new $class($this); $timeRule = $this->db->getConfig('time_query_rule'); if (!empty($timeRule)) { $query->timeRule($timeRule); } return $query; } /** * 指定表名开始查询. * * @param $table * * @return BaseQuery */ public function table($table) { return $this->newQuery()->table($table); } /** * 指定表名开始查询(不带前缀). * * @param $name * * @return BaseQuery */ public function name($name) { return $this->newQuery()->name($name); } /** * 设置当前的数据库Db对象 * * @param DbManager $db * * @return void */ public function setDb(DbManager $db) { $this->db = $db; } /** * 设置当前的缓存对象 * * @param CacheInterface $cache * * @return void */ public function setCache(CacheInterface $cache) { $this->cache = $cache; } /** * 获取当前的缓存对象 * * @return CacheInterface|null */ public function getCache() { return $this->cache; } /** * 获取数据库的配置参数. * * @param string $config 配置名称 * * @return mixed */ public function getConfig(string $config = '') { if ('' === $config) { return $this->config; } return $this->config[$config] ?? null; } /** * 数据库SQL监控. * * @param string $sql 执行的SQL语句 留空自动获取 * @param bool $master 主从标记 * * @return void */ protected function trigger(string $sql = '', bool $master = false): void { $listen = $this->db->getListen(); if (empty($listen)) { $listen[] = function ($sql, $time, $master) { if (str_starts_with($sql, 'CONNECT:')) { $this->db->log($sql); return; } // 记录SQL if (is_bool($master)) { // 分布式记录当前操作的主从 $master = $master ? 'master|' : 'slave|'; } else { $master = ''; } $this->db->log($sql . ' [ ' . $master . 'RunTime:' . $time . 's ]'); }; } $runtime = number_format((microtime(true) - $this->queryStartTime), 6); $sql = $sql ?: $this->getLastsql(); if (empty($this->config['deploy'])) { $master = null; } foreach ($listen as $callback) { if (is_callable($callback)) { $callback($sql, $runtime, $master); } } } /** * 缓存数据. * * @param CacheItem $cacheItem 缓存Item */ protected function cacheData(CacheItem $cacheItem) { if ($cacheItem->getTag() && method_exists($this->cache, 'tag')) { $this->cache->tag($cacheItem->getTag())->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); } else { $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); } } /** * 分析缓存Key. * * @param BaseQuery $query 查询对象 * @param string $method 查询方法 * * @return string */ protected function getCacheKey(BaseQuery $query, string $method = ''): string { if (!empty($query->getOptions('key')) && empty($method)) { $key = 'think_' . $this->getConfig('database') . '.' . var_export($query->getTable(), true) . '|' . $query->getOptions('key'); } else { $key = $query->getQueryGuid(); } return $key; } /** * 分析缓存. * * @param BaseQuery $query 查询对象 * @param array $cache 缓存信息 * @param string $method 查询方法 * * @return CacheItem */ protected function parseCache(BaseQuery $query, array $cache, string $method = ''): CacheItem { [$key, $expire, $tag] = $cache; if ($key instanceof CacheItem) { $cacheItem = $key; } else { if (true === $key) { $key = $this->getCacheKey($query, $method); } $cacheItem = new CacheItem($key); $cacheItem->expire($expire); $cacheItem->tag($tag); } return $cacheItem; } /** * 获取返回或者影响的记录数. * * @return int */ public function getNumRows(): int { return $this->numRows; } /** * 获取最终的SQL语句. * * @param string $sql 带参数绑定的sql语句 * @param array $bind 参数绑定列表 * * @return string */ public function getRealSql(string $sql, array $bind = []): string { foreach ($bind as $key => $val) { $value = strval(is_array($val) ? $val[0] : $val); $type = is_array($val) ? $val[1] : self::PARAM_STR; if (self::PARAM_FLOAT == $type || self::PARAM_STR == $type) { $value = '\'' . addslashes($value) . '\''; } elseif (self::PARAM_INT == $type && '' === $value) { $value = '0'; } // 判断占位符 $sql = is_numeric($key) ? substr_replace($sql, $value, strpos($sql, '?'), 1) : str_replace( [':' . $key . ' ', ':' . $key . ',', ':' . $key . ')'], [$value . ' ', $value . ',', $value . ')'], $sql); } return rtrim($sql); } /** * 析构方法. */ public function __destruct() { // 关闭连接 $this->close(); } }