Thinkphp3.2.3關於Model類的運行詳解

很多人 只知道用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動作。。這種是可取的。即取即用  省內存空間。設計合理,值得我們學習。。




發佈了76 篇原創文章 · 獲贊 25 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章