很多人 只知道用M方法去實現數據庫的curd。今天我跟大家一起去讀源碼,去分析它內部是怎麼一個運行過程的。。
說到Model類 我們肯定先從M方法說起。D方法同理
下面開始分析
$str_dsn = 'mysql://root:root@localhost:3306/sql';
$model = M('teacher','',$str_dsn);
在這裏我們先調用M方法。傳入必要的參數
緊接着 我們進入到這個文件
文件路徑是E:\webroot\tpexcel\ThinkPHP\Library\Think\Model.class.php
new 一個類的時候 通常都是自動調用這個類的構造方法。那我們直接看這個方法
public function __construct($name='',$tablePrefix='',$connection='') {
// 模型初始化
$this->_initialize();
// 獲取模型名稱
if(!empty($name)) {
if(strpos($name,'.')) { // 支持 數據庫名.模型名的 定義
list($this->dbName,$this->name) = explode('.',$name);
}else{
$this->name = $name;
}
}elseif(empty($this->name)){
$this->name = $this->getModelName();
}
//echo $this->name;
// 設置表前綴
if(is_null($tablePrefix)) {// 前綴爲Null表示沒有前綴
$this->tablePrefix = '';
}elseif('' != $tablePrefix) {
$this->tablePrefix = $tablePrefix;
}elseif(!isset($this->tablePrefix)){
$this->tablePrefix = C('DB_PREFIX');
}
// 數據庫初始化操作
// 獲取數據庫操作對象
// 當前模型有獨立的數據庫連接信息
$this->db(0,empty($this->connection)?$connection:$this->connection,true);
}
進過一番分析下來 ,我們覺得 最主要的是db方法。那我們進入
public function db($linkNum='',$config='',$force=false) {
//echo $config;exit;
if('' === $linkNum && $this->db) {
return $this->db;
}
if(!isset($this->_db[$linkNum]) || $force ) {
// 創建一個新的實例
if(!empty($config) && is_string($config) && false === strpos($config,'/')) { // 支持讀取配置參數
//echo $config;exit;
$config = C($config);
}
$this->_db[$linkNum] = Db::getInstance($config);
}elseif(NULL === $config){
$this->_db[$linkNum]->close(); // 關閉數據庫連接
unset($this->_db[$linkNum]);
return ;
}
// 切換數據庫連接
$this->db = $this->_db[$linkNum];
$this->_after_db();
// 字段檢測
if(!empty($this->name) && $this->autoCheckFields) $this->_checkTableInfo();
return $this;
}
在db方法的內部 我們重點關注
Db::getInstance($config);
這個是初始化一個數據庫的連接對象。作爲db類的一個屬性。。並且是單例返回。
進一步進入
static public function getInstance($config=array()) {
$md5 = md5(serialize($config));
if(!isset(self::$instance[$md5])) {
// 解析連接參數 支持數組和字符串
$options = self::parseConfig($config);
//print_r($options);exit;
// 兼容mysqli
if('mysqli' == $options['type']) $options['type'] = 'mysql';
// 如果採用lite方式 僅支持原生SQL 包括query和execute方法
$class = !empty($options['lite'])? 'Think\Db\Lite' : 'Think\\Db\\Driver\\'.ucwords(strtolower($options['type']));
if(class_exists($class)){
self::$instance[$md5] = new $class($options);
}else{
// 類沒有定義
E(L('_NO_DB_DRIVER_').': ' . $class);
}
}
self::$_instance = self::$instance[$md5];
return self::$_instance;
}
看到這裏 我們大概明白了,他是實例化了Think\Db\mysql.class.php這個類。。
我們進入這個類。
發現這個類是一個子類。。也就是這時候 Model把程序的控制權交給了它的父類。我們進入
public function __construct($config=''){
if(!empty($config)) {
$this->config = array_merge($this->config,$config);
if(is_array($this->config['params'])){
$this->options = $this->config['params'] + $this->options;
}
}
}
這裏我們看出 裏面是做一些初始化配置參數的事情。。特別關注 $config 這個是整合配置信息。請注意
此刻並沒有產生真正的數據庫連接對象。
那我們開始分析curd操作。。。我在想肯定是curd的時候纔去做連接。。
我們就挑select來分析吧。。。大家都知道Model類的方法是暴露給程序員去操作數據庫的直接方式。
那我們就去找model類的select方法。
其中關鍵有這麼一段代碼
$resultSet = $this->db->select($options);
這裏調用了db類的select方法。二經過上面的分析 我們知道 $this->db實際上指向driver類的對象
那麼我們直接去這個類定位到對於的selectf方法。
找到下面這條關鍵代碼 在方法裏面
$result = $this->query($sql,!empty($options['fetch_sql']) ? true : false);
發現他調了本類的query方法實現。進去看
那是不是其他操作也是這樣處理呢。。那好我們去找add
按照同樣的代碼運行方式 我們在最後找到驅動類的
public function execute($str,$fetchSql=false) {
$this->initConnect(true);
if ( !$this->_linkID ) return false;
$this->queryStr = $str;
if(!empty($this->bind)){
$that = $this;
$this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return '\''.$that->escapeString($val).'\''; },$this->bind));
}
if($fetchSql){
return $this->queryStr;
}
//釋放前次的查詢結果
if ( !empty($this->PDOStatement) ) $this->free();
$this->executeTimes++;
N('db_write',1); // 兼容代碼
// 記錄開始執行時間
$this->debug(true);
$this->PDOStatement = $this->_linkID->prepare($str);
if(false === $this->PDOStatement) {
$this->error();
return false;
}
foreach ($this->bind as $key => $val) {
if(is_array($val)){
$this->PDOStatement->bindValue($key, $val[0], $val[1]);
}else{
$this->PDOStatement->bindValue($key, $val);
}
}
$this->bind = array();
try{
$result = $this->PDOStatement->execute();
// 調試結束
$this->debug(false);
if ( false === $result) {
$this->error();
return false;
} else {
$this->numRows = $this->PDOStatement->rowCount();
if(preg_match("/^\s*(INSERT\s+INTO|REPLACE\s+INTO)\s+/i", $str)) {
$this->lastInsID = $this->_linkID->lastInsertId();
}
return $this->numRows;
}
}catch (\PDOException $e) {
$this->error();
return false;
}
}
發現依舊是這個處理流程。。。
總結 :thinkphp3.2.3 對於Model的運行過程是這樣的。先M方法實例化驅動類 裝載必要的配置參數。然後等程序真正執行curd操作的時候再去進行真正的數據庫連接操作 執行對於的sql動作。。這種是可取的。即取即用 省內存空間。設計合理,值得我們學習。。