ThinkPHP5源码分析之Db(5.1)

TP5的数据库相关操作类由 Connection(连接器)、Query(查询器)、Builder(sql生成器)组成。

Db流程如图:

Paste_Image.png

还是以一个简单的Db查询为例,对流程图代码分解展示:

<b>Step:1、2</b>

file [library\think\Db.php] 
namespace think;
use think\Collection;
use think\db\Query;
/*
  Db捕捉到不存在的function会__callStatic(){}执行数据库初始化connect(){} 然后单例化Connector Mysql类
*/
class Db{
  static $instance = [];

  /*
    @param $config 数据库连接参数配置
    @param $name 强制重连接标示
    return Connection对象实例
  */
  static function connect($config = [], $name = false)
  {
    //单例 Connection 类,但如果设置了重连接标示的话就重新实例化
    if (true === $name || !isset(self::$instance[$name])) {

      //parseConfig获取数据库连接相关配置参数,如果$config是空的则直接读取配置文件
      $options = self::parseConfig($config);

      //获取配置参数数据库类型type 然后new对应的connecter $type 类 这个$type类继承connecter抽象类
      $class =  '\\think\\db\\connector\\'  .  $options['type'] ;
    } else {
      if (true === $name) {
        return new $class($options);
      } else {
        self::$instance[$name] = new $class($options);
      }
    }
    return self::$instance[$name];
  }
  /*
    __call(){}魔术。详见php官方文档
  */
  static function __callStatic($method, $params)
  {
    return call_user_func_array([self::connect(), $method], $params);
  }
}

<b>Step:3</b>

file [library\think\db\connector\Mysql.php] 
namespace think\db\connector;
use PDO;
use think\db\Connection;

class Mysql extends Connection
{
  function parseDsn()
  {
    //mysql 驱动的连接pdo dsn
    ……
  }
}


file [library\think\db\Connection.php] 
use PDO;
use PDOStatement;
use think\db\Query;

abstract class Connection
{
  protected $PDOStatement; //pdo
  protected $query = []; //Query对象
  protected $config = []; //数据库连接配置
  protected $params = [……]; //pdo 相关设置

  /*
    读取数据库配置信息
    @param$config 数据库配置数组
  */
  function __construct(array $config = [])
  {
    if (!empty($config)) {
      $this->config = array_merge($this->config, $config);
    }
  }

  function __call($method, $args)
  {
    //实例化查询器(其实就是查询、执行类)
    $this->query['database'] = new Query($this);
    /*
      $method 方法; $args 参数
      例如:Db::name('table')  那么执行Query类name('table')
    */
    return call_user_func_array([$this->query['database'], $method], $args);
  }

   /*
    子类实现 abstract
   */
  abstract protected function parseDsn($config);

  /*
    连接数据库
    @param $config 连接参数
    @param $linkNum 连接序号
    @param $autoConnection 是否自动连接主数据库(用于分布式)
    return PDO
  */
  function connect(array $config = [], $linkNum = 0, $autoConnection = false)
  {
    if (!isset($this->links[$linkNum])) {
      $config = empty($config) ? $this->config : $config;
      //pdo的配置参数
      $params = $this->params;
      //设置默认返回内容类型 array、object、json等
      $this->resultSetType = $config['resultset_type'];

      try {
        $config['dsn'] = $this->parseDsn($config);
        //pdo实例化
        $this->links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params);
      } catch (\PDOException $e) {
        ……
      }
    }
    return $this->links[$linkNum];
  }

  function __destruct()
  {
    // 释放查询
    if ($this->PDOStatement) {
      $this->free();
    }
    // 关闭连接
    $this->close();
  }

  /*
    参照流程图:[other_connection_do]
    @param $sql sql指令
    @param $bind 参数绑定
    @param $master 是否在主服务器读操作
    @param $class 指定返回的数据集对象
    return 数据资源
   */
  function query($sql, $bind = [], $master = false, $class = false)
  {
    //初始化数据库连接
    $this->initConnect($master);

    //sql bind 
    $this->queryStr = $this->getRealSql($sql, $bind);

    try {
      //预处理 也不知道怎么解释  参考pdo官方文档
      $this->PDOStatement = $this->linkID->prepare($sql);

      // 参数绑定操作
      $this->bindValue($bind);

      //执行啊
      $result = $this->PDOStatement->execute();
      
      //获取资源数据
      return $this->getResult($class, $procedure);
    } catch {
      //GG  PDOException 
    }
  }
  
   
}



<b>Step:4</b>

file [library\think\db\builder/Mysql.php] 
namespace think\db\builder;
use think\db\Builder;

class Mysql extends Builder
{
  protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
   /*
    字段表名处理
    */
  function parseKey($key)
  {
    ……
  }
}

file [library\think\db\Builder.php] 
namespace think\db;
use think\Db;
use think\db\Connection;
use think\db\Query;
use PDO;

abstract class Builder
{
  protected $connection; // connection对象实例
  protected $query; // 查询对象实例
  protected $options = []; // 查询参数

  //sql表达式
  protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%';
  protected $insertSql = '……';
  protected $insertAllSql = '……';
  protected $updateSql    = '……';
  protected $deleteSql    = '……';

  /*
    数据库连接对象实例
  */
  function __construct(Connection $connection)
  {
    $this->connection = $connection;
  }

  /*
    Query对象实例
  */
  function setQuery(Query $query)
  {
    $this->query = $query;
  }

  /*
    参照流程图:[other_builder_do]
    生成查询sql
    @param $options 表达式
    return sql string
  */
  
  function select($options = [])
  {
    $sql = str_replace(
    ['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%','%WHERE%'……],
    [    $this->parseTable($options['table']),
         $this->parseDistinct($options['distinct']),
         $this->parseField($options['field']),
         $this->parseJoin($options['join']),
         $this->parseWhere($options['where'], $options),
         ……
    ],
    $this->selectSql
    );
  }

}

<b>Query_do[见流程图]</b>

abstract class Connection
{
  ……
  
  /*
    参照流程图:[other_query_do]
    数据执行、查询动作
    @param array|string|Query|\Closure $data
    return PDOStatement
  */
  function select($data = null)
  {
    $options = $this->parseExpress();
    //Step4 获取sql
    $sql = $this->builder()->select($options);

    /*
      Step5 最终根据驱动绑定合适的query sqlstring
      如果设置了fetch_sql 则直接去连接器解析绑定sqlstring 直接输出 测试用
    */
     if ($options['fetch_sql']) {
      return $this->connection->getRealSql($sql, $this->bind);
    }
    
    /*
      Step5 最终根据驱动绑定合适的query sqlstring
      返回 前面有设置返回类型的 
    */
    $resultSet = $this->connection->query($sql, $this->getBind(), $options['master'], $options['fetch_class']);

    /*
      Step6
      返回 前面有设置pdo返回数据类型 object or array ……
    */
    if ($resultSet instanceof \PDOStatement) {
      return $resultSet;
    }
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容