tp5的数据库操作全体因而Db类落成,tp5的数据库操作全体通过Db类落成皇冠现金app

前言

tp5的数据库操作全体透过Db类完毕,比较符合国人的习惯,比如容易的Db::query()Db::execute(),还有长短不一的链式操作Db::table(‘user’)->where(‘id=1’)->select(),下边就经过源码来打探其工作流程

看代码此前,先看看涉及到的类都有如何,tp5的数据库相关的类有以下多少个:

  • Db(用户接口)
  • Connection(连接器)
  • Query(查询器)
  • Builder(SQL生成器)

前言

tp5的数据库操作全体由此Db类完结,比较符合国人的习惯,比如简单的Db::query()Db::execute(),还有参差不齐的链式操作Db::table(‘user’)->where(‘id=1’)->select(),下边就经过源码来打探其行事流程

看代码以前,先看看涉及到的类都有哪些,tp5的数据库相关的类有以下多少个:

  • Db(用户接口)
  • Connection(连接器)
  • Query(查询器)
  • Builder(SQL生成器)

Db::query()发生了何等? 

Db::query()爆发了如何? 

假若配置文件设置驱动为Mysql,当执行以下代码时,tp5的数码库类是怎么工作的?

Db::query("select * from user where id=?", [1]);

为了节省篇章以及更好地了解流程,上面只呈现宗旨代码,部分代码被简化或改造,大家来看望Db类:

class Db
{
    private static $instance = [];

    private static function parseConfig($config)
    {
        if (empty($config)) {
            $config = Config::get('database');
        } else {
            $config = Config::get($config);
        }
        return $config;
    }

    public static function connect($config = [])
    {
    $name = md5(serialize($config));    
        if (!isset(self::$instance[$name])) {
        $options = self::parseConfig($config);
        self::$instance[$name] = new \think\db\connector\Mysql($options);
        }
        return self::$instance[$name];
    }

    public static function __callStatic($method, $params)
    {
    return call_user_func_array([self::connect(), $method], $params);
    }

}

因为Db类没有定义query(),所以触发了__callStatic(),__callStatic()又调用本身的connect(),connect()实例化Mysql连接器(传入数据库配置$options),然后保留到$instance(数据库连接实例数组),再来看看Mysql连接器:

namespace think\db\connector;
class Mysql extends Connection
{

    protected $builder = '\\think\\db\\builder\\Mysql';

}

Mysql连接器也没有定义query()呀,它一而再了Connection,看看Connection有没有:

abstract class Connection
{

    protected $PDOStatement;
    protected $linkID;
    protected $config = [];

    public function __construct(array $config = [])
    {
        if (!empty($config)) {
            $this->config = array_merge($this->config, $config);
        }
    }

    protected function getResult()
    {
        return $this->PDOStatement->fetchAll(PDO::FETCH_ASSOC);
    }

    protected function bindValue(array $bind = [])
    {
        foreach ($bind as $key => $val) {
            $param = is_numeric($key) ? $key + 1 : ':' . $key;
            if (is_array($val)) {
                if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
                    $val[0] = 0;
                }
                $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
            } else {
                $result = $this->PDOStatement->bindValue($param, $val);
            }

        }
    }

    public function connect()
    {
        if (!$this->linkID) {
            $config = $this->config;
            $this->linkID = new PDO($config['dsn'], $config['username'], $config['password']);
        }
        return $this->linkID;
    }

    public function query($sql, $bind = [])
    {
        $this->connect();

        if (empty($this->PDOStatement)) {
            $this->PDOStatement = $this->linkID->prepare($sql);
        }

        $this->bindValue($bind);
        $this->PDOStatement->execute();
        return $this->getResult();

    }

}

若是配置文件设置驱动为Mysql,当执行以下代码时,tp5的数据库类是怎么工作的?

Db::query("select * from user where id=?", [1]);

为了省去篇章以及更好地领略流程,上面只呈现中央代码,部分代码被简化或改建,我们来看看Db类:

class Db
{
    private static $instance = [];

    private static function parseConfig($config)
    {
        if (empty($config)) {
            $config = Config::get('database');
        } else {
            $config = Config::get($config);
        }
        return $config;
    }

    public static function connect($config = [])
    {
 $name = md5(serialize($config));    
     if (!isset(self::$instance[$name])) {
  $options = self::parseConfig($config);
  self::$instance[$name] = new \think\db\connector\Mysql($options);
     }
        return self::$instance[$name];
    }

    public static function __callStatic($method, $params)
    {
 return call_user_func_array([self::connect(), $method], $params);
    }

}

因为Db类没有定义query(),所以触发了__callStatic(),__callStatic()又调用本人的connect(),connect()实例化Mysql连接器(传入数据库配置$options),然后保留到$instance(数据库连接实例数组),再来看看Mysql连接器:

namespace think\db\connector;
class Mysql extends Connection
{

    protected $builder = '\\think\\db\\builder\\Mysql';

}

Mysql连接器也没有定义query()呀,它继续了Connection,看看Connection有没有:

abstract class Connection
{

    protected $PDOStatement;
    protected $linkID;
    protected $config = [];

    public function __construct(array $config = [])
    {
        if (!empty($config)) {
            $this->config = array_merge($this->config, $config);
        }
    }

    protected function getResult()
    {
        return $this->PDOStatement->fetchAll(PDO::FETCH_ASSOC);
    }

    protected function bindValue(array $bind = [])
    {
        foreach ($bind as $key => $val) {
            $param = is_numeric($key) ? $key + 1 : ':' . $key;
            if (is_array($val)) {
                if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
                    $val[0] = 0;
                }
                $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
            } else {
                $result = $this->PDOStatement->bindValue($param, $val);
            }

        }
    }

    public function connect()
    {
        if (!$this->linkID) {
            $config = $this->config;
            $this->linkID = new PDO($config['dsn'], $config['username'], $config['password']);
        }
        return $this->linkID;
    }

    public function query($sql, $bind = [])
    {
        $this->connect();

        if (empty($this->PDOStatement)) {
            $this->PDOStatement = $this->linkID->prepare($sql);
        }

        $this->bindValue($bind);
        $this->PDOStatement->execute();
        return $this->getResult();

    }

}

结论

Db::query()触发Db::__callStatic(),实例化Mysql连接器并调用Mysql->query(),而Mysql连接器继承了Connection,所以其实是调用了Connection->query()

皇冠现金app 1

结论

Db::query()触发Db::__callStatic(),实例化Mysql连接器并调用Mysql->query(),而Mysql连接器继承了Connection,所以实际是调用了Connection->query()

皇冠现金app 2

Db::table(‘user’)->where(‘id=1’)->select()暴发了何等?

Db和Mysql连接器都不曾定义table()方法,发现Connection也有个__call():

protected function getQuery()
{
    return new \think\db\Query($this);
}

public function __call($method, $args)
{
    return call_user_func_array([$this->getQuery(), $method], $args);
}

因而Db::table(‘user’)实际上是接触了__call()魔术点子,然后实例化了1个Query对象(构造函数传入当前Mysql连接器对象),看看Query里面做了怎么样:

namespace think\db;
class Query
{
    protected $connection;
    protected $builder;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
        $this->setBuilder();
    }

    protected function setBuilder()
    {
        $this->builder = new \think\db\builder\Mysql($this->connection, $this);
    }

    public function table($table)
    {
        $this->options['table'] = $table;
        return $this;
    }

    public function where($where)
    {
        $this->options['where'] = $where;
        return $this;
    }

    public function query($sql)
    {
        return $this->connection->query($sql);
    }

    public function select()
    {
        $options = $this->options;
        $this->options = [];
        $sql = $this->builder->select($options);
        return $this->query($sql);
    }

}

首先构造函数保存了当下的Mysql连接器对象,并实例化think\db\builder\Mysql

Query->table()把表名保存到$options数组,然后重临$this(当前实例)从而达成链式操作,where()同样,重点看看select(),它拿到$options之后把它清空以便下次使用,然后调用了Builder->select()拿到拼装好的sql,交由Connection->query()查询数据库得到结果集,整个工艺流程到此截至,那么Builder是怎么拼装sql的吧?

namespace think\db\builder;
class Mysql extends Builder
{
    protected function parseRand()
    {
        return 'rand()';
    }
}

think\db\builder\Mysql并从未定义select(),但是它连续了Builder,看看Builder代码:

namespace think\db;
abstract class Builder
{
    protected $connection;
    protected $query;
    protected $selectSql    = 'SELECT %FIELD% FROM %TABLE% %WHERE%';

    public function select($options = [])
    {
        $sql = str_replace(
            ['%TABLE%', '%FIELD%', '%WHERE%'],
            [
                $options['table'],
                $options['field'] ?: '*',
                $options['where'] ? 'WHERE'.$options['where'] : '',
            ], $this->selectSql);
        return $sql;
    }

}

Builder通过$options替换sql模板得到sql

Db::table(‘user’)->where(‘id=1’)->select()暴发了如何?

Db和Mysql连接器都不曾定义table()方法,发现Connection也有个__call():

protected function getQuery()
{
    return new \think\db\Query($this);
}

public function __call($method, $args)
{
    return call_user_func_array([$this->getQuery(), $method], $args);
}

为此Db::table(‘user’)实际上是触发了__call()魔术点子,然后实例化了壹个Query对象(构造函数传入当前Mysql连接器对象),看看Query里面做了什么样:

namespace think\db;
class Query
{
    protected $connection;
    protected $builder;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
        $this->setBuilder();
    }

    protected function setBuilder()
    {
        $this->builder = new \think\db\builder\Mysql($this->connection, $this);
    }

    public function table($table)
    {
        $this->options['table'] = $table;
        return $this;
    }

    public function where($where)
    {
        $this->options['where'] = $where;
        return $this;
    }

    public function query($sql)
    {
        return $this->connection->query($sql);
    }

    public function select()
    {
        $options = $this->options;
        $this->options = [];
        $sql = $this->builder->select($options);
        return $this->query($sql);
    }

}

率先构造函数保存了当前的Mysql连接器对象,并实例化think\db\builder\Mysql

Query->table()把表名保存到$options数组,然后重返$this(当前实例)从而完成链式操作,where()同样,重点看看select(),它得到$options之后把它清空以便下次使用,然后调用了Builder->select()得到拼装好的sql,交由Connection->query()查询数据库得到结果集,整个流程到此截至,那么Builder是怎么拼装sql的呢?

namespace think\db\builder;
class Mysql extends Builder
{
    protected function parseRand()
    {
        return 'rand()';
    }
}

think\db\builder\Mysql并没有定义select(),然则它继续了Builder,看看Builder代码:

namespace think\db;
abstract class Builder
{
    protected $connection;
    protected $query;
    protected $selectSql    = 'SELECT %FIELD% FROM %TABLE% %WHERE%';

    public function select($options = [])
    {
        $sql = str_replace(
            ['%TABLE%', '%FIELD%', '%WHERE%'],
            [
                $options['table'],
                $options['field'] ?: '*',
                $options['where'] ? 'WHERE'.$options['where'] : '',
            ], $this->selectSql);
        return $sql;
    }

}

Builder通过$options替换sql模板得到sql

结论

Db::table()触发了__callStatic()实例化Connection并调用table(),由于Connection也从没定义table(),又触及了我的__call()实例化Query并调用table(),table()再次回到$this已毕链式操作DB::table()->where()->select(),而select又调用Builder->select()拿到sql,最后调用Connection->query()获取查询结果,固完整的类图表示如下:

皇冠现金app 3

结论

Db::table()触发了__callStatic()实例化Connection并调用table(),由于Connection也远非定义table(),又触及了自小编的__call()实例化Query并调用table(),table()重临$this已毕链式操作DB::table()->where()->select(),而select又调用Builder->select()得到sql,最后调用Connection->query()获取查询结果,固完整的类图表示如下:

皇冠现金app 4

相关文章