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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章