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

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

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

  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,各类缓存接口,模块实例化接口,数据库操做接口等等。

二、实例化过程
经过阅读源码,咱们能够知道: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地址是否被封等等)。

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

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

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

  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 模式或者 静默模式,具体请参考相关数据库知识;

记录跟新操做:app

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

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


取数据操做:xss

  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 等操做。


本节完

相关文章
相关标签/搜索