ThinkPhp 源码解读 Model篇
ThinkPhp 源碼解讀 Model篇
本篇主要講解TP下 Model Query Connection DB Builder 類的關(guān)系
最終理解 model(‘xxx’)->startTrans(); model(‘yyy’)->conmmit(); 事物流程的解析
問題:
1:TP 是如何管理不同的模型對(duì)象的,調(diào)用modle()所做的事情
2:TP 是如何管理數(shù)據(jù)庫連接的,為何 不同的model 調(diào)用能保證事物統(tǒng)一
3:TP model 里的鏈?zhǔn)秸{(diào)用時(shí)如何實(shí)現(xiàn)的?
開始 :從 model 方法進(jìn)去:
public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common'){$guid = $name . $layer;if (isset(self::$instance[$guid])) {return self::$instance[$guid];}if (false !== strpos($name, '\\')) {$class = $name;$module = Request::instance()->module();} else {if (strpos($name, '/')) {list($module, $name) = explode('/', $name, 2);} else {$module = Request::instance()->module();}$class = self::parseClass($module, $layer, $name, $appendSuffix);}if (class_exists($class)) {$model = new $class();} else {$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);if (class_exists($class)) {$model = new $class();} else {throw new ClassNotFoundException('class not exists:' . $class, $class);}}self::$instance[$guid] = $model;return $model;}解析: model 方法是 Loader 類的靜態(tài)方法,主要根據(jù)全類名初始化modle類,并且以全類名為key將modle對(duì)象保存到靜態(tài)數(shù)組中,所以TP 中的model對(duì)象都是單例存在
接下來看 Model類
abstract class Model implements \JsonSerializable, \ArrayAccess{//數(shù)據(jù)庫查詢對(duì)象池 /db/Query類對(duì)象 以model類名為key的單例,即每個(gè)model子類都有對(duì)應(yīng)的一個(gè)query類統(tǒng)一保存在 Model 父類里protected static $links = [];// 數(shù)據(jù)庫配置, 這不是connection類對(duì)象,其實(shí)這就是數(shù)據(jù)庫配置數(shù)組,類似配置里的database 數(shù)組!這命名也是坑protected $connection = [];// 數(shù)據(jù)庫查詢對(duì)象, 其實(shí)這是數(shù)據(jù)庫查詢對(duì)象的全類名! 類似 \\think\\db\\Query 不是對(duì)象!protected $query;.....解析: Model是抽象類,跟數(shù)據(jù)庫連接相關(guān)的成員屬性 為上面3個(gè),屬性上面也有解析,接下來看 modle 類做了什么事,主要方法 __construct->getQuery->buildQuery
通過這3方法完成對(duì)當(dāng)前class Query對(duì)象的創(chuàng)建并保存到 links 全局?jǐn)?shù)組中
下面看看 為什么Model 類里沒有我們常用的 where->find 等方法,但在子類里又可以調(diào)用?
調(diào)用沒有的方法首先觸發(fā)的就是_call 魔術(shù)方法 進(jìn)去看看
public function __call($method, $args){$query = $this->db(true, false);if (method_exists($this, 'scope' . $method)) {// 動(dòng)態(tài)調(diào)用命名范圍$method = 'scope' . $method;array_unshift($args, $query);call_user_func_array([$this, $method], $args);return $this;} else {return call_user_func_array([$query, $method], $args);}}很明顯如果類里沒有對(duì)應(yīng)方法 或者沒有對(duì)應(yīng)的scope 方法 則會(huì)調(diào)用query 里對(duì)應(yīng)的方法,(所以 寫一手別人看不懂的代碼是多裝逼的事)這里的query 通過 this->db 方法獲取,其實(shí)就是拿的靜態(tài)數(shù)組links 里面的!
modle 類解決,我們繼續(xù)看Query 類 有什么東西
class Query{// 數(shù)據(jù)庫Connection對(duì)象實(shí)例,這個(gè)才是真的Connection 類的對(duì)象,一般從DB類里直接獲取,后面有簡(jiǎn)單分析DB類protected $connection;// 數(shù)據(jù)庫Builder類對(duì)象實(shí)例, builder類即 '\\think\\db\\builder\\'里面的類,邏輯功能比較單一, 主要解析各種數(shù)組類參數(shù) 生成字符串sql 語句protected $builder;// 當(dāng)前模型類名稱protected $model;....解析:
Query作用 主要封裝數(shù)據(jù)庫查詢實(shí)例connection 以及鏈?zhǔn)秸{(diào)用方法(where->find ..),通過builder 對(duì)參數(shù)進(jìn)行解析獲得sql 并執(zhí)行,
看構(gòu)造方法:
這里的connection 通過 Db::connect 靜態(tài)方法獲得,我們看 DB 類方法
public static function connect($config = [], $name = false){if (false === $name) {$name = md5(serialize($config));}if (true === $name || !isset(self::$instance[$name])) {// 解析連接參數(shù) 支持?jǐn)?shù)組和字符串$options = self::parseConfig($config);if (empty($options['type'])) {throw new \InvalidArgumentException('Undefined db type');}$class = false !== strpos($options['type'], '\\') ? $options['type'] : '\\think\\db\\connector\\' . ucwords($options['type']);// 記錄初始化信息if (App::$debug) {Log::record('[ DB ] INIT ' . $options['type'], 'info');}if (true === $name) {return new $class($options);} else {self::$instance[$name] = new $class($options);}}return self::$instance[$name];}看到這里終于看懂,TP 里所有Query 對(duì)象的connection 都是以數(shù)據(jù)庫配置MD5值為key 的單例,即不改變數(shù)據(jù)庫鏈接配置,不管我們創(chuàng)建多少model,其實(shí)用的都是同一個(gè)connection!!!
繼續(xù)看connection 類
解析: connention 類 主要是封裝保存PDO實(shí)例,邏輯比較簡(jiǎn)單,pdo實(shí)例保存在成員屬性links里,這里如果不是主從多庫的情況一般只會(huì)維護(hù)一個(gè)PDO實(shí)例,多庫情況請(qǐng)看multiConnect() 方法這里不展開
public function connect(array $config = [], $linkNum = 0, $autoConnection = false){if (!isset($this->links[$linkNum])) {if (!$config) {$config = $this->config;} else {$config = array_merge($this->config, $config);}// 連接參數(shù)if (isset($config['params']) && is_array($config['params'])) {$params = $config['params'] + $this->params;} else {$params = $this->params;}// 記錄當(dāng)前字段屬性大小寫設(shè)置$this->attrCase = $params[PDO::ATTR_CASE];// 數(shù)據(jù)返回類型if (isset($config['result_type'])) {$this->fetchType = $config['result_type'];}try {if (empty($config['dsn'])) {$config['dsn'] = $this->parseDsn($config);}if ($config['debug']) {$startTime = microtime(true);}$this->links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params);if ($config['debug']) {// 記錄數(shù)據(jù)庫連接信息Log::record('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn'], 'sql');}} catch (\PDOException $e) {if ($autoConnection) {Log::record($e->getMessage(), 'error');return $this->connect($autoConnection, $linkNum);} else {throw $e;}}}return $this->links[$linkNum];}到這里 TP從 model 方法到 數(shù)據(jù)庫連接一路已經(jīng)走完,下面總結(jié)幾點(diǎn)
1:TP model 子類會(huì)以子類全類名為key 的單例模式保存在 model 抽象父類的靜態(tài)屬性里,即model(xxx) 多次只會(huì)有一個(gè)對(duì)象,且是全局的;
2:每個(gè)model子類會(huì)持有一個(gè)Query對(duì)象,并且所有的Query對(duì)象,以model全類名為key 的單例模式保存在 model 抽象父類的靜態(tài)屬性里,
就是創(chuàng)建2個(gè)modle 對(duì)象,它們會(huì)持有同一個(gè)Query對(duì)象
3:TP 通過 __call 魔術(shù)方法 實(shí)現(xiàn)在modle里調(diào)用Query 類的鏈?zhǔn)椒椒?如 where->select 這些方法均封裝在Query類里
4:每個(gè)query 會(huì)有持有一個(gè)connection 實(shí)例,并且 所有的connection實(shí)例都會(huì)以數(shù)據(jù)庫連接配置MD5值為key 的單例形式保存在DB類的靜態(tài)屬性里,
所以,只要TP 數(shù)據(jù)庫配置沒有修改,不管你怎么創(chuàng)建不同的model 類,最后都會(huì)使用到同一connection實(shí)例,并且此實(shí)例是全局靜態(tài)保存的
總結(jié):TP 對(duì)應(yīng)普通web服務(wù)的應(yīng)用來說是很友好的,對(duì)普通web服務(wù)PHP都是單線程,所以TP全程都是以單例為核心,
整個(gè)框架不存在多次創(chuàng)建對(duì)象,多次鏈接數(shù)據(jù)庫的情況,一次訪問只持有一個(gè)數(shù)據(jù)庫連接;
但 這僅是對(duì)普通web服務(wù)來說是友好的,對(duì)于像多進(jìn)程的應(yīng)用 如swoole 來說,TP 的這些屬性有時(shí)是致命的!后面會(huì)分析TP 跟swoole的一些坑,敬請(qǐng)留意
總結(jié)
以上是生活随笔為你收集整理的ThinkPhp 源码解读 Model篇的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JQuery的可见性选择器与show、h
- 下一篇: 百分点科技位居中国数据治理解决方案市场第