前言
tp5的数据库操作全部通过Db类完成,比较符合国人的习惯,比如简单的Db::query()、Db::execute(),还有复杂的链式操作Db::table('user')->where('id=1')->select(),下面就通过源码来了解其工作流程
看代码之前,先看看涉及到的类都有哪些,tp5的数据库相关的类有以下几个:
- Db(用户接口)
- Connection(连接器)
- Query(查询器)
- Builder(SQL生成器)
Db::query()发生了什么?
假定配置文件设置驱动为Mysql,当执行以下代码时,tp5的数据库类是怎么工作的?
Db::query("select * from user where id=?", [1]);
为了节省篇章以及更好地理解流程,下面只展示核心代码,部分代码被简化或改造,我们来看看Db类:
class Db
{
private static $instance = [];
private static function parseConfig($config)
{
if (empty($config)) {
$config = Config::get('database');
} else {
$config = Config::get($config);
}
return $config;
}
public static function connect($config = [])
{
$name = md5(serialize($config));
if (!isset(self::$instance[$name])) {
$options = self::parseConfig($config);
self::$instance[$name] = new \think\db\connector\Mysql($options);
}
return self::$instance[$name];
}
public static function __callStatic($method, $params)
{
return call_user_func_array([self::connect(), $method], $params);
}
}
因为Db类没有定义query(),所以触发了__callStatic(),__callStatic()又调用自身的connect(),connect()实例化Mysql连接器(传入数据库配置$options),然后保存到$instance(数据库连接实例数组),再来看看Mysql连接器:
namespace think\db\connector;
class Mysql extends Connection
{
protected $builder = '\\think\\db\\builder\\Mysql';
}
Mysql连接器也没有定义query()呀,它继承了Connection,看看Connection有没有:
abstract class Connection
{
protected $PDOStatement;
protected $linkID;
protected $config = [];
public function __construct(array $config = [])
{
if (!empty($config)) {
$this->config = array_merge($this->config, $config);
}
}
protected function getResult()
{
return $this->PDOStatement->fetchAll(PDO::FETCH_ASSOC);
}
protected function bindValue(array $bind = [])
{
foreach ($bind as $key => $val) {
$param = is_numeric($key) ? $key + 1 : ':' . $key;
if (is_array($val)) {
if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
$val[0] = 0;
}
$result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
} else {
$result = $this->PDOStatement->bindValue($param, $val);
}
}
}
public function connect()
{
if (!$this->linkID) {
$config = $this->config;
$this->linkID = new PDO($config['dsn'], $config['username'], $config['password']);
}
return $this->linkID;
}
public function query($sql, $bind = [])
{
$this->connect();
if (empty($this->PDOStatement)) {
$this->PDOStatement = $this->linkID->prepare($sql);
}
$this->bindValue($bind);
$this->PDOStatement->execute();
return $this->getResult();
}
}
结论
Db::query()触发Db::__callStatic(),实例化Mysql连接器并调用Mysql->query(),而Mysql连接器继承了Connection,所以实际上是调用了Connection->query()
Db::table('user')->where('id=1')->select()发生了什么?
Db和Mysql连接器都没有定义table()方法,发现Connection也有个__call():
protected function getQuery()
{
return new \think\db\Query($this);
}
public function __call($method, $args)
{
return call_user_func_array([$this->getQuery(), $method], $args);
}
所以Db::table('user')实际上是触发了__call()魔术方法,然后实例化了一个Query对象(构造函数传入当前Mysql连接器对象),看看Query里面做了什么:
namespace think\db;
class Query
{
protected $connection;
protected $builder;
public function __construct(Connection $connection)
{
$this->connection = $connection;
$this->setBuilder();
}
protected function setBuilder()
{
$this->builder = new \think\db\builder\Mysql($this->connection, $this);
}
public function table($table)
{
$this->options['table'] = $table;
return $this;
}
public function where($where)
{
$this->options['where'] = $where;
return $this;
}
public function query($sql)
{
return $this->connection->query($sql);
}
public function select()
{
$options = $this->options;
$this->options = [];
$sql = $this->builder->select($options);return $this->query($sql);
}
}
首先构造函数保存了当前的Mysql连接器对象,并实例化think\db\builder\Mysql
Query->table()把表名保存到$options数组,然后返回$this(当前实例)从而实现链式操作,where()同样,重点看看select(),它拿到$options之后把它清空以便下次使用,然后调用了Builder->select()拿到拼装好的sql,交由Connection->query()查询数据库获得结果集,整个流程到此结束,那么Builder是怎么拼装sql的呢?
namespace think\db\builder;
class Mysql extends Builder
{
protected function parseRand()
{
return 'rand()';
}
}
think\db\builder\Mysql并没有定义select(),不过它继承了Builder,看看Builder代码:
namespace think\db;
abstract class Builder
{
protected $connection;
protected $query;
protected $selectSql = 'SELECT %FIELD% FROM %TABLE% %WHERE%';
public function select($options = [])
{
$sql = str_replace(
['%TABLE%', '%FIELD%', '%WHERE%'],
[
$options['table'],
$options['field'] ?: '*',
$options['where'] ? 'WHERE'.$options['where'] : '',
], $this->selectSql);
return $sql;
}
}
Builder通过$options替换sql模板拿到sql
结论
Db::table()触发了__callStatic()实例化Connection并调用table(),由于Connection也没有定义table(),又触发了自身的__call()实例化Query并调用table(),table()返回$this实现链式操作DB::table()->where()->select(),而select又调用Builder->select()拿到sql,最终调用Connection->query()获取查询结果,固完整的类图表示如下:
相关推荐
二、ThinkPHP源码结构分析 2.1 源码目录结构:解压"thinkphp完整源码"后,可以看到包括Application、Conf、Controller、Model、View等主要目录,每个目录都有特定的功能和作用。 2.2 主要文件:如`index.php`作为...
1. **数据库设计**:数据库是网站存储数据的核心,ThinkPHP提供了便捷的数据库操作接口。源码中的数据库文件可能包含用户表、预约表等,展示了如何定义数据字段、设置主键、外键,以及如何进行数据的增删改查操作。 ...
《深入解析ThinkPHP5框架源码》 ThinkPHP5(简称TP5)是中国最流行的PHP开发框架之一,它以其简洁的代码结构、丰富的文档和强大的功能吸引了大量的开发者。本篇文章将深入探讨ThinkPHP5的核心设计理念,主要框架...
二、项目源码解析 该项目源码包含了一个完整的商城系统,我们可以从中学习到以下关键知识点: 1. 控制器(Controller):负责接收前端请求,调用模型处理数据,然后返回视图进行展示。例如,商品控制器...
通过这个文件,ThinkPHP会加载核心类库,解析URL,路由请求到相应的控制器和方法,然后执行业务逻辑并返回响应。 五、数据持久化 `think.sql`很可能是数据库的初始脚本文件,用于创建数据库表结构或者填充测试数据...
《深入解析ThinkPHP6.0框架源码》 ThinkPHP6.0是基于PHP语言的轻量级开发框架,以其高效、稳定、易用的特点深受开发者喜爱。它为快速构建Web应用提供了强大的支持,同时也为开发者提供了源码级别的学习资源。在深入...
《深入解析ThinkPHP福娃源码交易网站源码》 ThinkPHP,作为国内广泛应用的开源PHP框架,以其简洁、高效、灵活的特点深受开发者喜爱。在众多的PHP项目中,福娃源码交易网站是一个典型的实例,它展示了ThinkPHP在构建...
本篇文章将详细解析一个基于Thinkphp框架开发的仿素材源码交易商城下载站源码,旨在帮助开发者了解其架构、功能及安装方法。 一、Thinkphp框架简介 Thinkphp是一款国内广泛应用的开源PHP框架,以其简洁的语法、强大...
《深入解析ThinkPHP框架源码交易系统资源网站源码》 在互联网开发领域,ThinkPHP框架因其简洁易用、高效稳定的特点,深受开发者喜爱。本文将深入探讨基于ThinkPHP框架构建的源码交易系统资源网站源码,帮助读者理解...
《基于Thinkphp5的大型程序员交流博客系统源码解析》 在编程世界中,源码是程序员交流思想、分享知识的重要载体。今天我们要探讨的是一个基于Thinkphp5框架构建的大型程序员交流博客系统。Thinkphp5是PHP领域的一款...
《深入剖析ThinkPHP3.0:源码与实践解析》 ThinkPHP,作为一个深受国内开发者喜爱的PHP框架,以其简洁、高效、易学的特点,在Web开发领域占据了一席之地。尤其是ThinkPHP3.0版本,它在前代的基础上进一步优化了架构...
《深入解析ThinkPHP5.0框架源码》 ThinkPHP5.0(简称TP5)是中国最流行的PHP框架之一,以其简洁、高效的特性受到广大开发者喜爱。本篇将围绕"thinkphp5-master"源码进行深入探讨,揭示其背后的运行机制和设计思想。...
2. ThinkPHP 的路由机制将该 URL 解析为一个方法调用,具体来说,是将 `_method` 参数设置为 `__construct`,并将 `filter` 参数设置为 `system` 3. ThinkPHP 的 App.php 文件中的 run 方法将该方法调用传递给 `exec...
五、源码解析 1. 控制器:负责接收用户请求,调用模型进行业务处理,再返回相应的视图展示结果。 2. 模型:处理业务逻辑,如数据验证、数据操作等,与数据库进行交互。 3. 视图:展示数据,通常包含HTML、CSS和...
### ThinkPHP框架核心解析 #### 引言 ThinkPHP是一个基于PHP的开源Web应用程序框架,以其简洁、高效、灵活的特点而著称。本篇旨在深入分析ThinkPHP框架的核心原理及其实现机制,帮助读者理解其架构设计思路,掌握...
《深入解析ThinkPHP框架源码交易系统资源网站源码》 在互联网开发领域,源码交易系统已经成为一种常见的服务模式,它为开发者提供了一个获取、分享和交易代码资源的平台。ThinkPHP作为国内广泛使用的PHP框架,其...
《基于ThinkPHP6的境外商城源码解析与应用》 ThinkPHP6,作为国内流行的开源PHP框架,以其高效、简洁的特性受到了广大开发者们的青睐。本篇将详细解析一款基于ThinkPHP6构建的境外商城源码,探讨其设计思想、核心...
1. **ThinkPHP框架**:ThinkPHP是一个轻量级的PHP框架,其核心特性包括路由系统、模型操作、视图渲染、数据库支持等。在这个项目中,它用于组织代码结构,提供便捷的API接口,帮助开发者高效地处理请求、响应以及...
《基于Thinkphp5的流浪猫狗宠物领养平台H5源码解析与应用拓展》 在当前社会,人们对宠物的关爱日益增长,越来越多的平台致力于为流浪动物提供一个温馨的家。本文将深入探讨一款基于Thinkphp5内核的流浪猫狗宠物领养...
本文将围绕"ThinkPHP5商品进销存管理网站源码案例设计"进行详细解析,旨在帮助开发者理解和运用这一源码。 一、ThinkPHP5框架基础 1. MVC模式:ThinkPHP5采用Model-View-Controller架构,将业务逻辑、数据处理和...