• app: yxcms
  • 参考:

0x01 yxcms model层

此处分析有些长,可跳过…

总结一下yxcms的 数据库操作

  • 从文件结构来看, 从model层下

屏幕快照 2019-11-11 下午4.09.25.png

apps下对应着几个模块,每个模块都有完整的mvc。在看model层,么一个model都继承自baseModel:
protected/base/model/baseModel.php

  1. <?php
  2. class baseModel extends model{
  3. protected $prefix='';
  4. public function __construct( $database= 'DB',$force = false ){
  5. parent::__construct();
  6. $this->prefix=config('DB_PREFIX');
  7. }
  8. }

baseModel 只有一个构造函数, 他构造了上层model方法,model里构造了基本方法。

控制层对于model层的操作语法是这样的

  1. <?php
  2. $var = model('Table')->method($Parameters)
  3. # 如:
  4. $count = model('photo')

model层的入口在core中:
protected/base/model/model

  1. <?php
  2. # $model 就是我们传进来的表名嘛
  3. function model($model){
  4. static $objArray = array();
  5. $className = $model . 'Model';
  6. if(!is_object($objArray[$className]))
  7. {
  8. if(!class_exists($className))
  9. {
  10. throw new Exception(config('_APP_NAME'). '/' . $className . '.php 模型类不存在');
  11. }
  12. 这里new
  13. $objArray[$className] = new $className();
  14. }
  15. return $objArray[$className];
  16. }

再看model层:

  1. <?php
  2. class model{
  3. public $model = NULL;
  4. public $db = NULL;
  5. protected $table = "";
  6. protected $ignoreTablePrefix = false;
  7. # 初始化生成db
  8. public function __construct( $database= 'DB', $force = false){
  9. $this->model = self::connect( config($database), $force);
  10. $this->db = $this->model->db;
  11. }
  12. 连接函数
  13. static public function connect($config, $force=false){
  14. static $model = NULL;
  15. if( $force==true || empty($model) ){
  16. $model = new cpModel($config);
  17. }
  18. return $model;
  19. }
  20. public function query($sql){
  21. return $this->model->query($sql);
  22. }
  23. public function find($condition = '', $field = '', $order = ''){
  24. return $this->model->table($this->table, $this->ignoreTablePrefix)->field($field)->where($condition)->order($order)->find();
  25. }
  26. public function select($condition = '', $field = '', $order = '', $limit = ''){
  27. return $this->model->table($this->table, $this->ignoreTablePrefix)->field($field)->where($condition)->order($order)->limit($limit)->select();
  28. }
  29. public function count($condition = ''){
  30. return $this->model->table($this->table, $this->ignoreTablePrefix)->where($condition)->count();
  31. }
  32. public function insert($data = array() ){
  33. return $this->model->table($this->table, $this->ignoreTablePrefix)->data($data)->insert();
  34. }
  35. public function update($condition, $data = array() ){
  36. $a = $this->model->table($this->table, $this->ignoreTablePrefix)->data($data)->where($condition)->update();
  37. return $this->model->table($this->table, $this->ignoreTablePrefix)->data($data)->where($condition)->update();
  38. }
  39. public function delete($condition){
  40. return $this->model->table($this->table, $this->ignoreTablePrefix)->where($condition)->delete();
  41. }
  42. public function getSql(){
  43. return $this->model->getSql();
  44. }
  45. public function escape($value){
  46. return $this->model->escape($value);
  47. }
  48. public function cache($time = 0){
  49. $this->model->cache($time);
  50. return $this;
  51. }
  52. }
  • 首先从config里连接数据库调用了core里的cpModel,加载了driver。
  1. <?php
  2. //连接数据库
  3. public function connect() {
  4. # 读配置里的数据库驱动
  5. $dbDriver = 'cp' . ucfirst( $this->config['DB_TYPE'] );
  6. # 加载驱动
  7. require_once( dirname(__FILE__) . '/db/' . $dbDriver . '.class.php' );
  8. //实例化数据库驱动类
  9. $this->db = new $dbDriver( $this->config );
  10. }

屏幕快照 2019-11-11 下午4.44.18.png
数据库的读写操作最后还是落到这里面的文件与数据库做对应的api连接, 这里默认配置是第一个cpMysql.class.php。那么如何进入操作的呢?
数据库操作:1. 建立连接 2. 操作 3.释放 在项目中为了做到低耦合模块化总是跳去,但是这些功能都有具体的落实点。比如数据库操作,理清源头,找到哪儿调用了mysql_connect():
屏幕快照 2019-11-11 下午4.56.27.png连接基本操作在这儿了。
这个方法被_getReadLink或者_getWriteLink形成两个数据库操作句柄$this->_readLink$this->_writeLink。分成读操作和写操作:
读操作:
屏幕快照 2019-11-11 下午5.01.47.png

写操作:
屏幕快照 2019-11-11 下午5.02.32.png

瞥到了一个escape被循环调用emmm….在这个方法里我们又看到了这玩意….
屏幕快照 2019-11-11 下午5.09.53.png
思想数组递归,不是数组直接过滤,这里array_map实现递归我觉得比较巧妙。
所以yxcms的model层在底层是用了过滤。

在回头看一个完整的查询,控制层的:$cobiunt=model('news')->count($where); 这个是来自default模块,index控制器下的一个查询功能,条件查询,返回news表查询行数。
$where=”ispass=’1’ AND (title like ‘%”.$keywords.”%’ OR description like ‘%”.$keywords.”%’)”;
$keyword可控。
两个参数,model(‘news’)初始化了model层的news模块(和通过url访问controller很像)。根据上面的分析,所有的model模块都继承了baseModel<-modelmodel中抽象了基本操作。所以这条语句中的->count($where)相当于走到了这里。
这里base/model/model.php:

  1. <?
  2. public function count($condition = ''){
  3. return $this->model->table($this->table, $this->ignoreTablePrefix)->where($condition)->count();
  4. }

这还是一个抽象层,做为接口,连接core里的cpmodel,这一层在核心,形成查询表达式,并翻译给指定的driver的做查询。
我们的$where化身成了$condition传到了where中。这是一串多层调用,现调用 table() ,这个方法就是检查表名有没有前缀,然后赋值给了一个参数,还是返回$this始构建查询表达式。这里面的$this->db就开始调用底层驱动cpMysql里的方法了。
where在哪呢?这里做了一些连贯操作。https://www.aspyc.com/archives/604.html使用了_call方法,当没有发现where就运行call:
include/core/cpModel.class.php

  1. <?php
  2. //回调方法,连贯操作的实现
  3. public function __call($method, $args) {
  4. $method = strtolower($method);
  5. if ( in_array($method, array('field','data','where','group','having','order','limit','cache')) ) {
  6. $this->options[$method] = $args[0]; //接收数据
  7. if( $this->options['field'] =='' ) $this->options['field'] = '*';
  8. return $this; //返回对象,连贯查询
  9. } else{
  10. throw new Exception($method . '方法在cpModel.class.php类中没有定义');
  11. }
  12. }

这里我目前也不是很熟,跳过orm去,打印sql查询语句。屏幕快照 2019-11-11 下午6.58.20.png
真正的sql语句是$this->db->sql;
屏幕快照 2019-11-11 下午7.01.53.png