thinkphp5源码解析(1)数据库
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*fromuserwhereid=?",[1]);
为了节省篇章以及更好地理解流程,下面只展示核心代码,部分代码被简化或改造,我们来看看Db类:
classDb{privatestatic$instance=[];privatestaticfunctionparseConfig($config){if(empty($config)){$config=Config::get('database');}else{$config=Config::get($config);}return$config;}publicstaticfunctionconnect($config=[]){$name=md5(serialize($config));if(!isset(self::$instance[$name])){$options=self::parseConfig($config);self::$instance[$name]=new\think\db\connector\Mysql($options);}returnself::$instance[$name];}publicstaticfunction__callStatic($method,$params){returncall_user_func_array([self::connect(),$method],$params);}}
因为Db类没有定义query(),所以触发了__callStatic(),__callStatic()又调用自身的connect(),connect()实例化Mysql连接器(传入数据库配置$options),然后保存到$instance(数据库连接实例数组),再来看看Mysql连接器:
namespacethink\db\connector;classMysqlextendsConnection{protected$builder='\\think\\db\\builder\\Mysql';}
Mysql连接器也没有定义query()呀,它继承了Connection,看看Connection有没有:
abstractclassConnection{protected$PDOStatement;protected$linkID;protected$config=[];publicfunction__construct(array$config=[]){if(!empty($config)){$this->config=array_merge($this->config,$config);}}protectedfunctiongetResult(){return$this->PDOStatement->fetchAll(PDO::FETCH_ASSOC);}protectedfunctionbindValue(array$bind=[]){foreach($bindas$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);}}}publicfunctionconnect(){if(!$this->linkID){$config=$this->config;$this->linkID=newPDO($config['dsn'],$config['username'],$config['password']);}return$this->linkID;}publicfunctionquery($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():
protectedfunctiongetQuery(){returnnew\think\db\Query($this);}publicfunction__call($method,$args){returncall_user_func_array([$this->getQuery(),$method],$args);}
所以Db::table('user')实际上是触发了__call()魔术方法,然后实例化了一个Query对象(构造函数传入当前Mysql连接器对象),看看Query里面做了什么:
namespacethink\db;classQuery{protected$connection;protected$builder;publicfunction__construct(Connection$connection){$this->connection=$connection;$this->setBuilder();}protectedfunctionsetBuilder(){$this->builder=new\think\db\builder\Mysql($this->connection,$this);}publicfunctiontable($table){$this->options['table']=$table;return$this;}publicfunctionwhere($where){$this->options['where']=$where;return$this;}publicfunctionquery($sql){return$this->connection->query($sql);}publicfunctionselect(){$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的呢?
namespacethink\db\builder;classMysqlextendsBuilder{protectedfunctionparseRand(){return'rand()';}}
think\db\builder\Mysql并没有定义select(),不过它继承了Builder,看看Builder代码:
namespacethink\db;abstractclassBuilder{protected$connection;protected$query;protected$selectSql='SELECT%FIELD%FROM%TABLE%%WHERE%';publicfunctionselect($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()获取查询结果,固完整的类图表示如下:
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。