Objective
TP5 database operations are all done through the DB class, more in line with people's habits, such as simple Db::query (), Db::execute (), and complex chain Operation Db::where (' id=1 ')->select (), The following is the source to understand its work flow
Before looking at the code, let's look at what classes are involved, and TP5 database-related classes have the following:
- Db (User interface)
- Connection (connector)
- Query (Finder)
- Builder (SQL Builder)
Db::query () What happened? Assuming that the configuration file settings are driven to MySQL, how does the TP5 database class work when the following code is executed?
Db::query ("SELECT * from user where id=?", [1]);
To save the chapters and better understand the process, just the core code is shown below, some of the code is simplified or modified, let's take a look at the DB class:
classdb{ Private Static $instance= []; Private Static functionParseconfig ($config) { if(Empty($config)) { $config= Config::get (' database '); } Else { $config= Config::get ($config); } return $config; } Public Static functionConnect$config= []) { $name=MD5(Serialize($config)); if(!isset(Self::$instance[$name])) { $options= self::p arseconfig ($config); Self::$instance[$name] =New\think\db\connector\Mysql($options); } returnSelf::$instance[$name]; } Public Static function__callstatic ($method,$params) { return Call_user_func_array([Self::connect (),$method],$params); } }
Because the DB class does not define query (), it triggers __callstatic (), __callstatic () and calls its own connect (), connect () instantiates the MySQL connector (incoming database configuration $options), and then saves to $instance (an array of database connection instances), and then look at the MySQL connector:
namespace Think\db\connector; class Mysql extends connection{ protected$builder = ' \\think\\db\\builder\\Mysql '; }
MySQL connector also does not define query () Yes, it inherits connection and see if connection has:
<?PHPAbstract classconnection{ protected $PDOStatement; protected $linkID; protected $config= []; Public function__construct (Array $config= []) { if(!Empty($config)) { $this->config =Array_merge($this->config,$config); } } protected functionGetResult () {return $this->pdostatement->fetchall (PDO::FETCH_ASSOC); } protected functionBindvalue (Array $bind= []) { foreach($bind as $key=$val) { $param=Is_numeric($key) ?$key+ 1: ': '.$key; if(Is_array($val)) { if(PDO::P aram_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 functionConnect () {if(!$this-LinkID) { $config=$this-config; $this->linkid =NewPDO ($config[' DSN '],$config[' username '],$config[' Password ']); } return $this-LinkID; } Public functionQuery$sql,$bind= []) { $this-Connect (); if(Empty($this-pdostatement)) { $this->pdostatement =$this->linkid->prepare ($sql); } $this->bindvalue ($bind); $this->pdostatement->execute (); return $this-GetResult (); } }
It's clear that connection defines query (), which calls connect () executes the PDO driver connection database, then data binding, and finally gets the query results
Conclusion
DB does not implement query (), it is only responsible for instantiating the corresponding database driver, through the __callstatic () Magic method call Connection query (), UML class diagram representation:
What happened to db::table (' user ')->where (' id=1 ')->select ()?
Both DB and MySQL connectors do not have a table () method defined, and connection also has a __call ():
protected function Getquery () { returnnew \think\db\query ($this);} Public function __call ($method$args) { returncall_user_ Func_array([$this$method$args);}
So db::table (' user ') actually triggers the __call () Magic method, then instantiates a Think\db\query object (the constructor passes in the current Think\db\connector\mysql object), See what the query does:
namespace Think\db;classquery{protected $connection; protected $builder; Public function__construct (Connection$connection) { $this->connection =$connection; $this-Setbuilder (); } protected functionSetbuilder () {$this->builder =New\think\db\builder\Mysql($this->connection,$this); } Public functionTable$table) { $this->options[' table '] =$table; return $this; } Public functionwhere$where) { $this->options[' where '] =$where; return $this; } Public functionQuery$sql) { return $this->connection->query ($sql); } Public functionSelect) { $options=$this-options; $this->options = []; $sql=$this->builder->select ($options);return $this->query ($sql); }}
The constructor first saves the current Think\db\connector\mysql object and instantiates the Think\db\builder\mysql
Query->table () Saves the table name to the $options array, and then returns $this (the current instance) for a chained operation, where () again, focus on select (), it gets $options and empties it for next use, Then call the Think\db\builder\mysql->select () to get the assembled SQL, by the connection->query () query database to obtain the result set, the entire process to this end, then think\db\ How does builder\mysql assemble SQL?
namespace Think\db\builder; class Mysql extends builder{ protectedfunction parserand () { return ' Rand () '; }}
The Select () is not defined, but it inherits the builder and looks at the builder code:
namespace Think\db;Abstract classbuilder{protected $connection; protected $query; protected $SELECTSQL= ' SELECT%field% from%table%%where% '; Public functionSelect$options= []) { $sql=Str_replace( ['%table% ', '%field% ', '%where% ', [ $options[' Table '],$options[' Field ']?: ' * ',$options[' WHERE ']? ' WHERE '.$options[' WHERE ']: ', ],$this-selectsql); return $sql; } }
Builder replaces SQL templates with $options to get SQL
Conclusion
Db::table () triggers the __callstatic () Instantiation connection and invokes table (), because connection also does not define table (), and it triggers its own __call () instantiation of query and calls table () , table () returns $this to implement the chained Operation Db::table ()->where ()->select (), and select calls Builder->select () to get SQL, and finally calls connection- >query () Gets the result of the query, the solid complete class diagram is represented as follows:
THINKPHP5 Source code parsing (1) database