Thinkphp3.2.3关于Model类的运行详解,ThinkPHP3.2是MVC框架,CURD是使用TP框架的基本操作,有使用M方法去实现数据库的curd,有使用模型绑定指定表操作数据表,分析下内部是怎么一个运行过程的。
ThinkPHP3.2是MVC框架,CURD是使用TP框架的基本操作,有使用M方法去实现数据库的curd,有使用模型绑定指定表操作数据表,分析下内部是怎么一个运行过程的。
说到Model类 我们肯定先从M方法说起,D方法同理。
如何使用M方法操作数据表
使用M方法操作数据表需要知道M方法的使用,点击查看M()方法,M($name = '', $tablePrefix = '', $connection = '')方法解释,M方法有三个参数,第一个是表名(@param string $name Model名称 支持指定基础模型 例如 MongoModel:User),第二个是表前缀(@param string $tablePrefix),第三个是(@param mixed $connection 数据库连接信息),如下实例。
$dsn = 'mysql://root:root@localhost:3306/test';
$model = M('demo','',$dsn);
首先调用M方法,传入上面说明的必要参数。
/**
* 实例化一个没有模型文件的Model
* @param string $name Model名称 支持指定基础模型 例如 MongoModel:User
* @param string $tablePrefix 表前缀
* @param mixed $connection 数据库连接信息
* @return Think\Model
*/
function M($name = '', $tablePrefix = '', $connection = '')
{
static $_model = array();
if (strpos($name, ':')) {
list($class, $name) = explode(':', $name);
} else {
$class = 'Think\\Model';
}
$guid = (is_array($connection) ? implode('', $connection) : $connection) . $tablePrefix . $name . '_' . $class;
if (!isset($_model[$guid])) {
$_model[$guid] = new $class($name, $tablePrefix, $connection);
}
return $_model[$guid];
}
首先我们找到:$class = 'Think\\Model';这里指定的Mdel类,查看源码。
文件路径是\ThinkPHP\Library\Think\Model.class.php
实例化,new一个类的时候,通常都是自动调用这个类的构造方法,那我们直接看这个方法。
/**
* 架构函数
* 取得DB类的实例对象 字段检查
* @access public
* @param string $name 模型名称
* @param string $tablePrefix 表前缀
* @param mixed $connection 数据库连接信息
*/
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();
}
// 设置表前缀
if (is_null($tablePrefix)) {
// 前缀为Null表示没有前缀
$this->tablePrefix = '';
} elseif ('' != $tablePrefix) {
$this->tablePrefix = $tablePrefix;
} elseif (!isset($this->tablePrefix)) {
$this->tablePrefix = !empty($this->connection) && !is_null(C($this->connection . '.DB_PREFIX')) ? C($this->connection . '.DB_PREFIX') : C('DB_PREFIX');
}
// 数据库初始化操作
// 获取数据库操作对象
// 当前模型有独立的数据库连接信息
$this->db(0, empty($this->connection) ? $connection : $this->connection, true);
}
通过查看上面的构造函数,最后一句,最主要的是db方法。
/**
* 切换当前的数据库连接
* @access public
* @param integer $linkNum 连接序号
* @param mixed $config 数据库连接信息
* @param boolean $force 强制重新连接
* @return Model
*/
public function db($linkNum = '', $config = '', $force = false)
{
if ('' === $linkNum && $this->db) {
return $this->db;
}
if (!isset($this->_db[$linkNum]) || $force) {
// 创建一个新的实例
if (!empty($config) && is_string($config) && false === strpos($config, '/')) {
// 支持读取配置参数
$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::getInstance($config);
这个是初始化一个数据库的连接对象,作为db类的一个属性,并且是单例返回。
在找到这个Db类,在Think目录下Db.class.php,打开找到getInstance方法。
/**
* 取得数据库类实例
* @static
* @access public
* @param mixed $config 连接配置
* @return Object 返回数据库驱动类
*/
public static function getInstance($config = array())
{
$md5 = md5(serialize($config));
if (!isset(self::$instance[$md5])) {
// 解析连接参数 支持数组和字符串
$options = self::parseConfig($config);
// 兼容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这个类。
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think\Db\Driver;
use Think\Db\Driver;
/**
* mysql数据库驱动
*/
class Mysql extends Driver
{
........//Mysql类内部代码省略,请自行查看
}
原来这个类是一个子类,也就是这时候Model把程序的控制权交给了它的父类Driver,看下父类里面的构造函数。
/**
* 架构函数 读取数据库配置信息
* @access public
* @param array $config 数据库配置数组
*/
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方法实现。
/**
* 执行查询 返回数据集
* @access public
* @param string $str sql指令
* @param boolean $fetchSql 不执行只是获取SQL
* @param boolean $master 是否在主服务器读操作
* @return mixed
*/
public function query($str, $fetchSql = false, $master = false)
{
$this->initConnect($master);
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->queryTimes++;
N('db_query', 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 {
return $this->getResult();
}
} catch (\PDOException $e) {
$this->error();
return false;
}
}
那是不是其他操作也是这样处理呢?那好去找add。
按照同样的代码运行方式,在最后找到驱动类的。
/**
* 执行语句
* @access public
* @param string $str sql指令
* @param boolean $fetchSql 不执行只是获取SQL
* @return mixed
*/
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;
}
}
/**
* 执行语句
* @access public
* @param string $str sql指令
* @param boolean $fetchSql 不执行只是获取SQL
* @return mixed
*/
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动作,这种是可取的,即取即用,省内存空间。
转载注明:
感谢博主,喝杯咖啡~
感谢博主,喝杯咖啡~
还没有人发表评论