TP5的数据库相关操作类由 Connection(连接器)、Query(查询器)、Builder(sql生成器)组成。
Db流程如图:
还是以一个简单的Db查询为例,对流程图代码分解展示:
<b>Step:1、2</b>
file [library\think\Db.php]
namespace think;
use think\Collection;
use think\db\Query;
/*
Db捕捉到不存在的function会__callStatic(){}执行数据库初始化connect(){} 然后单例化Connector Mysql类
*/
class Db{
static $instance = [];
/*
@param $config 数据库连接参数配置
@param $name 强制重连接标示
return Connection对象实例
*/
static function connect($config = [], $name = false)
{
//单例 Connection 类,但如果设置了重连接标示的话就重新实例化
if (true === $name || !isset(self::$instance[$name])) {
//parseConfig获取数据库连接相关配置参数,如果$config是空的则直接读取配置文件
$options = self::parseConfig($config);
//获取配置参数数据库类型type 然后new对应的connecter $type 类 这个$type类继承connecter抽象类
$class = '\\think\\db\\connector\\' . $options['type'] ;
} else {
if (true === $name) {
return new $class($options);
} else {
self::$instance[$name] = new $class($options);
}
}
return self::$instance[$name];
}
/*
__call(){}魔术。详见php官方文档
*/
static function __callStatic($method, $params)
{
return call_user_func_array([self::connect(), $method], $params);
}
}
<b>Step:3</b>
file [library\think\db\connector\Mysql.php]
namespace think\db\connector;
use PDO;
use think\db\Connection;
class Mysql extends Connection
{
function parseDsn()
{
//mysql 驱动的连接pdo dsn
……
}
}
file [library\think\db\Connection.php]
use PDO;
use PDOStatement;
use think\db\Query;
abstract class Connection
{
protected $PDOStatement; //pdo
protected $query = []; //Query对象
protected $config = []; //数据库连接配置
protected $params = [……]; //pdo 相关设置
/*
读取数据库配置信息
@param$config 数据库配置数组
*/
function __construct(array $config = [])
{
if (!empty($config)) {
$this->config = array_merge($this->config, $config);
}
}
function __call($method, $args)
{
//实例化查询器(其实就是查询、执行类)
$this->query['database'] = new Query($this);
/*
$method 方法; $args 参数
例如:Db::name('table') 那么执行Query类name('table')
*/
return call_user_func_array([$this->query['database'], $method], $args);
}
/*
子类实现 abstract
*/
abstract protected function parseDsn($config);
/*
连接数据库
@param $config 连接参数
@param $linkNum 连接序号
@param $autoConnection 是否自动连接主数据库(用于分布式)
return PDO
*/
function connect(array $config = [], $linkNum = 0, $autoConnection = false)
{
if (!isset($this->links[$linkNum])) {
$config = empty($config) ? $this->config : $config;
//pdo的配置参数
$params = $this->params;
//设置默认返回内容类型 array、object、json等
$this->resultSetType = $config['resultset_type'];
try {
$config['dsn'] = $this->parseDsn($config);
//pdo实例化
$this->links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params);
} catch (\PDOException $e) {
……
}
}
return $this->links[$linkNum];
}
function __destruct()
{
// 释放查询
if ($this->PDOStatement) {
$this->free();
}
// 关闭连接
$this->close();
}
/*
参照流程图:[other_connection_do]
@param $sql sql指令
@param $bind 参数绑定
@param $master 是否在主服务器读操作
@param $class 指定返回的数据集对象
return 数据资源
*/
function query($sql, $bind = [], $master = false, $class = false)
{
//初始化数据库连接
$this->initConnect($master);
//sql bind
$this->queryStr = $this->getRealSql($sql, $bind);
try {
//预处理 也不知道怎么解释 参考pdo官方文档
$this->PDOStatement = $this->linkID->prepare($sql);
// 参数绑定操作
$this->bindValue($bind);
//执行啊
$result = $this->PDOStatement->execute();
//获取资源数据
return $this->getResult($class, $procedure);
} catch {
//GG PDOException
}
}
}
<b>Step:4</b>
file [library\think\db\builder/Mysql.php]
namespace think\db\builder;
use think\db\Builder;
class Mysql extends Builder
{
protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
/*
字段表名处理
*/
function parseKey($key)
{
……
}
}
file [library\think\db\Builder.php]
namespace think\db;
use think\Db;
use think\db\Connection;
use think\db\Query;
use PDO;
abstract class Builder
{
protected $connection; // connection对象实例
protected $query; // 查询对象实例
protected $options = []; // 查询参数
//sql表达式
protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%';
protected $insertSql = '……';
protected $insertAllSql = '……';
protected $updateSql = '……';
protected $deleteSql = '……';
/*
数据库连接对象实例
*/
function __construct(Connection $connection)
{
$this->connection = $connection;
}
/*
Query对象实例
*/
function setQuery(Query $query)
{
$this->query = $query;
}
/*
参照流程图:[other_builder_do]
生成查询sql
@param $options 表达式
return sql string
*/
function select($options = [])
{
$sql = str_replace(
['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%','%WHERE%'……],
[ $this->parseTable($options['table']),
$this->parseDistinct($options['distinct']),
$this->parseField($options['field']),
$this->parseJoin($options['join']),
$this->parseWhere($options['where'], $options),
……
],
$this->selectSql
);
}
}
<b>Query_do[见流程图]</b>
abstract class Connection
{
……
/*
参照流程图:[other_query_do]
数据执行、查询动作
@param array|string|Query|\Closure $data
return PDOStatement
*/
function select($data = null)
{
$options = $this->parseExpress();
//Step4 获取sql
$sql = $this->builder()->select($options);
/*
Step5 最终根据驱动绑定合适的query sqlstring
如果设置了fetch_sql 则直接去连接器解析绑定sqlstring 直接输出 测试用
*/
if ($options['fetch_sql']) {
return $this->connection->getRealSql($sql, $this->bind);
}
/*
Step5 最终根据驱动绑定合适的query sqlstring
返回 前面有设置返回类型的
*/
$resultSet = $this->connection->query($sql, $this->getBind(), $options['master'], $options['fetch_class']);
/*
Step6
返回 前面有设置pdo返回数据类型 object or array ……
*/
if ($resultSet instanceof \PDOStatement) {
return $resultSet;
}
}
}