03
08月
2020
TP5的数据库相关操作类由 Connection(连接器)、Query(查询器)、Builder(sql生成器)组成。
例:
Db::query("select * from user where id=?", [1]);因为Db类没有定义query(),所以触发了__callStatic(),__callStatic()又调用自身的connect(),connect()实例化Mysql连接器,然后保存到$instance。
thinkphp/library/think/Db.php
/**
* 数据库连接参数解析
* @access private
* @param mixed $config
* @return array
*/
private static function parseConfig($config)
{
if (is_string($config) && false === strpos($config, '/')) {
// 支持读取配置参数
$config = isset(self::$config[$config]) ? self::$config[$config] : self::$config;
}
$result = is_string($config) ? self::parseDsnConfig($config) : $config;
if (empty($result['query'])) {
$result['query'] = self::$config['query'];
}
return $result;
}
/**
* 切换数据库连接
* @access public
* @param mixed $config 连接配置
* @param bool|string $name 连接标识 true 强制重新连接
* @param string $query 查询对象类名
* @return mixed 返回查询对象实例
* @throws Exception
*/
public static function connect($config = [], $name = false, $query = '')
{
// 解析配置参数
$options = self::parseConfig($config ?: self::$config);
$query = $query ?: $options['query']; // think\db\Query
// 创建数据库连接对象实例
self::$connection = Connection::instance($options, $name); // 返回 think\db\connector\Mysql 对象
return new $query(self::$connection);
}
public static function __callStatic($method, $args)
{
return call_user_func_array([static::connect(), $method], $args);
}thinkphp/library/think/db/Connection.php
/**
* 取得数据库连接类实例
* @access public
* @param mixed $config 连接配置
* @param bool|string $name 连接标识 true 强制重新连接
* @return Connection
* @throws Exception
*/
public static function instance($config = [], $name = false)
{
if (false === $name) {
$name = md5(serialize($config));
}
if (true === $name || !isset(self::$instance[$name])) {
if (empty($config['type'])) {
throw new InvalidArgumentException('Undefined db type');
}
// 记录初始化信息
Container::get('app')->log('[ DB ] INIT ' . $config['type']);
if (true === $name) {
$name = md5(serialize($config));
}
// 工厂模式
self::$instance[$name] = Loader::factory($config['type'], '\\think\\db\\connector\\', $config);
}
return self::$instance[$name];
}thinkphp/library/think/db/Query.php
/**
* 执行查询 返回数据集
* @access public
* @param string $sql sql指令
* @param array $bind 参数绑定
* @param boolean $master 是否在主服务器读操作
* @param bool $pdo 是否返回PDO对象
* @return mixed
* @throws BindParamException
* @throws PDOException
*/
public function query($sql, $bind = [], $master = false, $pdo = false)
{
return $this->connection->query($sql, $bind, $master, $pdo);
}thinkphp/library/think/db/Connection.php
/**
* 执行查询 返回数据集
* @access public
* @param string $sql sql指令
* @param array $bind 参数绑定
* @param bool $master 是否在主服务器读操作
* @param bool $pdo 是否返回PDO对象
* @return array
* @throws BindParamException
* @throws \PDOException
* @throws \Exception
* @throws \Throwable
*/
public function query($sql, $bind = [], $master = false, $pdo = false)
{
$this->initConnect($master);
if (!$this->linkID) {
return false;
}
// 记录SQL语句
$this->queryStr = $sql;
$this->bind = $bind;
Db::$queryTimes++;
try {
// 调试开始
$this->debug(true);
// 预处理
$this->PDOStatement = $this->linkID->prepare($sql);
// 是否为存储过程调用
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
// 参数绑定
if ($procedure) {
$this->bindParam($bind);
} else {
$this->bindValue($bind);
}
// 执行查询
$this->PDOStatement->execute();
// 调试结束
$this->debug(false, '', $master);
// 返回结果集
return $this->getResult($pdo, $procedure);
} catch (\PDOException $e) {
if ($this->isBreak($e)) {
return $this->close()->query($sql, $bind, $master, $pdo);
}
throw new PDOException($e, $this->config, $this->getLastsql());
} catch (\Throwable $e) {
if ($this->isBreak($e)) {
return $this->close()->query($sql, $bind, $master, $pdo);
}
throw $e;
} catch (\Exception $e) {
if ($this->isBreak($e)) {
return $this->close()->query($sql, $bind, $master, $pdo);
}
throw $e;
}
}执行顺序:
Db::query()触发Db::__callStatic(),实例化 Query 并传入通过工厂模式获取的Mysql实例,而Mysql连接器继承了Connection, 调用 query(),所以实际上是调用了Connection->query()