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;
    }
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容