很多人 只知道用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动作。。这种是可取的。即取即用 省内存空间。设计合理,值得我们学习。。