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()