Discuz 整体架构及内核浅析二:内核功能(For DzX3.2)

Discuz!X 系列的内核是使用面向对象模式开发的,因此每一次页面访问其实都有一个内核实例化的过程,下文将简单介绍一下内核的实现,以及提供的相关功能。

1、实例化操作
通常只有通过 Discuz 入口文件对站点进行访问才会正常进行内核实例化,这些入口文件位于 Dz 根目录下,如:forum.php、home.php、plugin.php 等等。想绕过这些入口文件而直接访问 Dz 目录中的文件则会被拒绝,通常我们会看到 “Access Denied” 的提示。原因是,除了入口文件和一些专用 API 接口文件外所有源码文件的开头都有如下代码:

  1. if(!defined('IN_DISCUZ')) {
  2.         exit('Access Denied');
  3. }
复制代码

而 IN_DISCUZ 这个常量只有实例化的时候才会被定义,任何绕过入口文件而直接执行代码因为 IN_DISCUZ 未被赋值而显示 “Access Denied” 提示。
要实例化内核对象只需要三条语句,代码如下:

  1. require_once './source/class/class_core.php';
  2. $discuz = C::app();
  3. $discuz->init();
复制代码

初始化完成以后,Dz 内核所集成的功能就可以使用了,比如包含整个内核信息的全局变量 $_G,各种缓存接口,模块实例化接口,数据库操作接口等等。

2、实例化过程
通过阅读源码,我们可以知道:class_core.php 中首先创建实例

  1. C::creatapp();
复制代码

大写 C 其实只是内核对象的一个简写形式

  1. class C extends core {}
复制代码

因此 C 就代表内核,同时内核还封装了一个很重要的成员函数,即:t(),在 Dz 源码中随处可见类似 C::t('forum_post')->fetch 之类的调用,这其实是数据库操作函数,相关的数据库操作,后文再详述。

静态成员函数 creatapp 用于创建内核实例,而内核的核心其实是定义在 discuz_application 类中,参考代码如下:

  1. public static function creatapp() {
  2.                 if(!is_object(self::$_app)) {
  3.                         self::$_app = discuz_application::instance();
  4.                 }
  5.                 return self::$_app;
  6.         }
复制代码

实例化首先会调用构造函数,构造函数会对基本的运行环境、系统配置文件、输入输出做基本的初始化,参考代码如下:

  1. public function __construct() {
  2.                 $this->_init_env();
  3.                 $this->_init_config();
  4.                 $this->_init_input();
  5.                 $this->_init_output();
  6.         }
复制代码

_init_env() 函数主要用于检测服务器软件环境,并做相应设置;加载 function_core.php,Dz 内置的常用函数都在此文件中;判断来访者是否是机器人,通过 checkrobot() 实现;初始化全局变量 $_G;
_init_config() 函数主要用于读取 Dz 配置文件,Dz 的配置文件位于根目录下的 config 文件夹中,config_global.php 用于 Dz 系统,config_ucenter.php 用于 UCenter 系统;Dz 的配置文件包含了比较多的内容,主要有这么几个部分:数据库访问参数、内存缓存接口参数、页面输出参数、Cookie参数、安全相关参数等等;通过配置文件的合理配置可以实现 Dz 的分布式部署以及对 MySQL 读写分离功能的支持。
_init_input() 函数主要用于对输入内核的数据进行相关处理,比如对 $_GET、$_POST、$_COOKIE 中的数据进行相关处理,将 $_GET 与 $_POST 合并,因此在 Dz 源码中,可以统一使用 $_GET 来处理用户的输入;
_init_output() 则是用于页面输出,即展示给浏览器的部分,根据配置文件的设定,可以实现页面编码,比如 UTF-8 或者 GBK,以及页面是否使用 Gzip 压缩后传输;

构造函数执行完毕后,还需要继续业务系统的初始化,这些工作则是通过 $discuz->init() 来完成, 其中的 init 成员函数其实是 discuz_application 中提供的,代码如下:

  1. public function init() {
  2.                 if(!$this->initated) {
  3.                         $this->_init_db();
  4.                         $this->_init_setting();
  5.                         $this->_init_user();
  6.                         $this->_init_session();
  7.                         $this->_init_mobile();
  8.                         $this->_init_cron();
  9.                         $this->_init_misc();
  10.                 }
  11.                 $this->initated = true;
  12.         }
复制代码

我们可以看到,初始化内核一共调用了 7 个子模块,分别用于:数据库、系统设置、用户数据、SESSION数据、移动端适配、计划任务、其他附加功能。
_init_db() 用于初始化数据库,Dz 系统会自动检测适合的数据库驱动,也就是接口函数,最终的初始化通过 DB::inti() 完成;
_init_setting() 用于加载系统设置、默认风格参数、自动化任务等等数据;
_init_user() 与 _init_session() 一起完成了用户登录相关的一些初始化工作,比如如果用户之前已经登录,那么在 session 过期前可以直接访问网站而无需再次登录;
_init_mobile() 用于识别用户使用的浏览器是否是移动端,如果是则还要进一步分析是新型触摸屏移动设备还是老式移动设备,并通过设置常量 IN_MOBILE 来标示用户的浏览器属性;
_init_cron() 用于自动任务的初始化,Dz 的自动化任务可以在后台控制面板中看到,比如每天定时清理论坛数据(用户数、帖子访问量等等),需要指出的是 Dz 的自动化任务执行不同于操作系统提供的自动化任务,比如 Linux 系统的 crontab,区别在于:操作系统的自动化任务严格按照时间设定执行,而 Dz 的自动化任务需要用户访问来触发,也就是说,只有在每次内核实例化并且执行 _init_cron() 函数的时候才会执行,这时 Dz 内置的相关模块会逐个检测设定好的自动化任务,如果满足执行条件,就会自动调用;
_init_misc() 用于一些杂项的设置,包括一些安全设置,比如进行 xss 攻击检测、表单 FORMHASH 常量设置用于防止非法访问、用户状态检测(比如用户id,ip地址是否被封等等)。

到此,整个实例化过程完成,每个部分的具体细节,有兴趣的同学就需要仔细去研究源码才能得到答案。

3、内核数据库操作接口
前面讲过 Dz 内核封装了对数据库的操作,通过内核接口,我们可以非常方便的操作对应的数据表,同时因为内核的面向对象特性,使得我们可以很容易的扩展接口的功能。在 Dz source/class/table 目录下有很多以 table_ 前缀开头的 php 源码文件,这些文件就是 C::t() 函数加载的对象,例如:C::t('forum_post') 会自动加载 source/class/table/talbe_forum_post.php 文件中的同名类,而这些类均继承自相同的父类 discuz_table;

对数据表常用的操作比如 插入、更新、读取等等 都在 discuz_table 类中进行了封装,这里简单介绍下这些函数的使用。
插入操作:

  1. public function insert($data, $return_insert_id = false, $replace = false, $silent = false)
复制代码

$data:要插入的数据,必须是 key => value 结构的数组,且 key 应该和对应数据表的相关字段名对应;
$return_insert_id:是否返回插入记录的ID号,如果设置为 true,则插入成功后会返回新记录的主键值,比如用户表就是 uid;
后面两个参数用于 REPLACE INTO 模式或者 静默模式,具体请参考相关数据库知识;

记录跟新操作:

  1. public function update($val, $data, $unbuffered = false, $low_priority = false)
复制代码

$val:用于更新数据表的主键值,如果是用户表, $val = 2 表示更新 uid = 2 的用户数据;
$data:要更新的数据字段的值,必须是 key => value 结构的数组,且 key 应该和对应数据表的相关字段名对应;
后面两个参数我也很少用,有兴趣的同学可以自行研究:)


取数据操作:

  1. public function fetch($id, $force_from_db = false)
复制代码

$id:要取的数据的主键,比如要获取用户2的数据,那么就是 C::t('common_member')->fetch(2);
$force_from_db:用于设置是否强制从数据库中获取数据,默认允许直接从缓存取数据;

其他还有很多内置接口,同学们可以自行研究源码,如果内置接口无法满足要求,则完全可以自行对以上接口进行重新封装。

插件数据表的操作方法:插件根目录下创建 table 目录,对应的表名必须是 table_ + 表名的结构,需要注意的是在数据库中手动创建插件数据表时,需要与系统数据表保持相同的前缀,Dz 默认安装时的表前缀都是 pre_ ,因此如果是默认安装,则需要先在数据库中创建表名为 pre_yourtable 的数据表,然后在插件 table 目录下创建对应的数据库操作类,文件名为 table_yourtable.php,最后在插件代码中就可以通过 C::t("#插件ID#yourtable") 加在插件数据表操作类,并同时继承了父类提供的 insert、update、fetch 等操作。


本节完

posted on 2018-01-27 23:07  alleyonine  阅读(395)  评论(0编辑  收藏  举报

导航