discuz代码解析(一、初始化应用的过程)
discuz是国内著名的论坛系统,今天大象有空看看源码,顺便理下流程,跟哥交流请发邮件到576272452@qq.com
一、首先必须知道的目录结构
(一)uc_client Ucentent客户端程序
(二)uc_server Ucentent服务端程序
(三)static 静态文件
(四)install 安装目录
(五)config 站点配置
(六)api 外部接口
(七)templets 模板目录
(八)source 代码主目录
(九)data 数据缓存及附件
(十) archive 论坛静态化
有图有真相
二、流程结构(随便找个地址来分析下,比如 设置 -> 个人资料 -> 联系方式;地址是:home.php?mod=spacecp&ac=profile&op=contact)
(一)首先打开home.php文件 看到第17行
1 |
require_once "./source/class/class_core.php" ;
|
我同时打开了多个入口的文件都有这么一个引入,因此可以肯定这是个入口配置文件,负责文件引入及初始化需要的组件,下面来看下这个文件
第10行 error_reporting(E_ALL); 报告所有错误
第12-15行四个常量定义
1 2 3 4 |
define( "IN_DISCUZ" , true);
define( "DISCUZ_ROOT" ,
substr (dirname( __FILE__ ), 0, -12));
define( "DISCUZ_CORE_DEBUG" , false);
define( "DISCUZ_TABLE_EXTENDABLE" , TRUE);
|
第17-22行设置异常处理函数
1 2 3 4 5 6 |
set_exception_handler( array ( "core" ,
"handleException" ));
if (DISCUZ_CORE_DEBUG) {
set_error_handler( array ( "core" ,
"handleError" ));
register_shutdown_function( array ( "core" ,
"handleShutdown" ));
}
|
第24-30行设置类自动引入的处理函数(这里看仔细,很重要)
1 2 3 4 5 6 7 |
if (function_exists( "spl_autoload_register" )) {
spl_autoload_register( array ( "core" ,
"autoload" ));
} else {
function
__autoload( $class ) {
return
core::autoload( $class );
}
}
|
第32行 初始化core类
1 |
C::creatapp();
|
这个C是core类的映射,证据是第208行
1 |
class C extends
core {}
|
来看静态方法creatapp()
1 2 3 4 5 6 |
public static
function creatapp() {
if (! is_object (self:: $_app )) {
self:: $_app
= discuz_application::instance();
}
return
self:: $_app ;
}
|
我们看到是返回了一个属性,这个属性_app 是discuz_application::instance()的返回值,我们来看discuz_application类,从上面的自动引入中我们看到这个类的地址是 ./source/class/discdz/discdz_application.php,
来看刚才执行的静态方法 discuz_application::instance() 的返回值是个啥?
1 2 3 4 5 6 7 |
static function
&instance() {
static
$object ;
if ( empty ( $object )) {
$object
= new self();
}
return
$object ;
}
|
原来是按址引用的,并且是实理化自身了,这个类继承自抽象类discuz_base,这个应该是个基类,来看看构造函数。
1 2 3 4 5 6 |
public function
__construct() {
$this ->_init_env();
//274-316行;初始化环境变量。定义了些常量,引入了公共函数库,在./source/function/function_core.php,设置最小内存128M。并设置了允许的全局变量(_GET,_POST,_REQUEST,_COOKIE,_SERVER,_ENV,_FILES),定义了一个全局变量$_G,变将这个全局变量按址传递给了$this->var,看来在模板中可以直接使用这些变量的。
$this ->_init_config();
//274-316行;初始化配置变量。引入了配置,在./config/config_global.php,将配置变量全部压入到$this->var["config"]中,在全局都可以使用配置啦
$this ->_init_input();//224-272行;初始化输入。过滤了下全局变量($_GET["GLOBALS"],$_POST["GLOBALS"]……),如果开启了get_magic_quotes_gpc(),就给$_GET,$_POST,$_COOKIE变量stripslashes一下,设置合法的cookie的键名必须 $this->config["cookie"]["cookiepre"]这个开头的,在配置文件中有。这样做应该是防止非法cookie变量,243行把$_POST的值合并到$_GET变量中去。把rawurlencode($_GET["page"])编码下,过滤了下$_GET["handlekey"],过滤$_GET变量, foreach($_GET
as $k => $v) {$this->var["gp_".$k] = daddslashes($v);}使用的是stripslashes函数。另外设置了几个全局变量 $this->var["XXX"]自己瞅瞅。
$this ->_init_output();//318-345行;初始化输出。过滤URL$this->_xss_check();<"CONTENT-TRANSFER-ENCODING都是非法的。如果控制器为"seccode", "secqaa", "swfupload"则进行这个过滤,有时间看看./source/include/misc/misc_security.php;开启ob_start(),设置charset;
}
|
好了,现在我们明白了,我们现在得到了一个对象C,并且它有一个属性叫_app,这个属性是discuz_application对象,再在回到入口配置文件class_core.php,下面执行到了第209行,我们看到一个映射
1 |
class DB extends
discuz_database {}
|
DB代表了discuz_database对象,我们去看看这个对象。从上面的自动引入类机制中我们得到了这个类位于 ./source/class/discdz/discdz_application.php。打开看看,原来是数据库操作的;静态方法 init($driver, $config)应该是初始化这个类的方法,从字面意思看,传两个参数进来,第一个参数是数据库驱动,第二个参数是数据库连接参数,以后的数据库操作应该都是DB::update() DB::delete这样的方法了。
好了,跟着程序运行顺序走,我们现在返回到入口文件home.php第18行
1 |
require_once "./source/function/function_home.php" ;
|
进去看看,原来是一组函数,这是个函数库,瞄了两眼,现在回到home.php第20行
1 |
$discuz = C::app();
|
原来是把discuz_application对象赋值给变量$discuz。
到第22-24行
1 2 3 |
$cachelist = array ( "magic" , "userapp" , "usergroups" ,
"diytemplatenamehome" );
$discuz ->cachelist =
$cachelist ;
$discuz ->init();
|
哇靠,$discuz->init();这一行才是核心中的核心,具体功能是初始化整个discuz应用。discuz_application类是整个discuz的应用初始化类,相当于织梦的 /include/common.inc.php的功能
1 2 3 4 5 6 7 8 9 10 11 12 |
public
function init() {
if (! $this ->initated)
{
$this ->_init_db();// 初始化数据库
$this ->_init_setting();//系统设置初始化
$this ->_init_user();//用户信息初始化
$this ->_init_session();// session操作初始化
$this ->_init_mobile();//手机功能初始化
$this ->_init_cron();//计划任务初始化
$this ->_init_misc();//其他功能初始化
}
$this ->initated = true;//设置完成标志
}
|
核心代码第一句 $this->_init_db();
1 2 3 4 5 6 7 8 9 |
private function
_init_db() {
if ( $this ->init_db) {
$driver
= "db_driver_mysql" ;
if (getglobal( "config/db/slave" )) {//在设置里修改slave可以修改数据库驱动类,默认为db_dirver_mysql
$driver
= "db_driver_mysql_slave" ;
}
DB::init( $driver ,
$this ->config[ "db" ]);//DB类位于:./source/class/discuz/discuz_database.php
}
}
|
DB::init方法是在discuz_database类中实例化了数据库驱动类db_driver_mysql驱动类,并将其赋值给DB::db属性,根据自动引入规则驱动类的位置在:./source/class/db/db_driver_mysql.php;
核心代码第二句 $this->_init_setting(),
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
private function
_init_setting() {
if ( $this ->init_setting) {
if ( empty ( $this -> var [ "setting" ])) {
$this ->cachelist[] =
"setting" ;
}
if ( empty ( $this -> var [ "style" ])) {
$this ->cachelist[] =
"style_default" ;
}
if (!isset( $this -> var [ "cache" ][ "cronnextrun" ])) {
$this ->cachelist[] =
"cronnextrun" ;
}
}
! empty ( $this ->cachelist) && loadcache( $this ->cachelist);//主要是这句,加载缓存,实现的过程可以看function_core.php中的第687-716行,实际是把缓存赋值给$_G变量($_G["setting"],$_G["cache"],$_G["grouplevels"]),处理缓存的其实是在第700行
C::t("common_syscache")->fetch_all($caches);,这块是重点。可以研究一下,DZ的缓存部分,这里我略讲一下,用C::t("common_syscache")->fetch_all($caches) 实例化了一个对象common_syscache 位置:./source/class/table/table_common_syscache.php,挺绕得,我看了半天代码才找出来,其实这个完整的对象是discuz_container。common_syscache对象只是discuz_container的一个属性obj;在class_core.php的第76-90行可以看出这种关系来,table_common_syscache.php第38行,$data
= memory("get", $cachenames);这个是最后得到的缓存数据,我们来追下memory函数,第一个参数是get,那么会执行function_core.php中的第1687行 case "get": return C::memory()->get($key, $value); 其中C::memory()是实例化了discuz_memory对象,在./source/class/discuz/discuz_memory.php
;看get方法在第70-102行,它有两个参数,get($key, $prefix = "")。get方法第97行$ret = $this->memory->get($this->_key($key));使用了memory_driver_eaccelerator类的get方法,位置./source/class/memory/memory_driver_eaccelerator.php第22行,return
eaccelerator_get($key);,缓存就是依靠eaccelerator_get函数获取的
if (! is_array ( $this -> var [ "setting" ])) {
$this -> var [ "setting" ] =
array ();
}
}
|
核心代码第三句$this->_init_user();discuz_application.php 428-493行
$auth = getglobal("auth", "cookie") 得到cookie中的auth的值,加密过的值
$auth = daddslashes(explode(" ", authcode($auth, "DECODE"))); 利用authcode函数解密,得到一个数组
list($discuz_pw, $discuz_uid) = empty($auth) || count($auth) < 2 ? array("", "") : $auth; 从这句看数组第一个元素为密码,第二个元素是会员登陆的ID。存在common_member表中。
$user = getuserbyuid($discuz_uid, 1);利用getuserbyuid得到会员信息,第二个参数如果是1并且在common_member中没有该会员就去common_member_archive表中去寻找,否则直接去common_member表中查找
1 2 3 4 5 6 7 8 9 |
if (! empty ( $user ) &&
$user [ "password" ] == $discuz_pw ) {如果查出的密码是对的
if (isset( $user [ "_inarchive" ])) {
C::t( "common_member_archive" )->move_to_master( $discuz_uid );如果数组中有_inarchive元素就将当前查到的用户信息插入到用户表common_member表中
}
$this -> var [ "member" ] =
$user ;将用户信息压入模板变量中
} else {
$user =
array ();
$this ->_init_guest();//设置没登陆的访客的信息
}
|
1 2 3 |
if ( $user
&& $user [ "groupexpiry" ] > 0 &&
$user [ "groupexpiry" ] < TIMESTAMP && (getgpc( "mod" ) !=
"spacecp" || CURSCRIPT != "home" )) {
dheader( "location: home.php?mod=spacecp&ac=usergroup&do=expiry" ); //如果登陆时间过期了,或者访问的是home.php下的内容时跳转到登陆页面
}
|
这里作用是检查登陆了没有。
462行;!empty($this->cachelist) && loadcache($this->cachelist);装载缓存, 使用缓存可以参照我另一篇文章中关于缓存的应用
477行;$this->var["member"]["lastvisit"] = TIMESTAMP - 3600;dsetcookie("lastvisit", TIMESTAMP - 3600, 86400 * 30);设置最后浏览的时间
464-490行,设置了cookie、全局变量、增加var属性变量
核心代码第四句 $this->_init_session();discuz_application.php 386-426行
设置session类,更新用户状态
1 2 3 4 5 |
<?php
$sessionclose = ! empty ( $this -> var [ "setting" ][ "sessionclose" ]); //设置中sessionclose是否为真
$this ->session =
$sessionclose ? new discuz_session_close() :
new discuz_session(); //如果为真就实例化discuz_session_close类 应该是关闭session后的解决方案,否则实例化discuz_session类,位置在source/class/discuz/discuz_session.php
if($this->session->get("groupid") == 6)判断session中的groupid合法性 $this->session->set("lastactivity", TIMESTAMP);设置最后访问时间为当前时间 dsetcookie("lip", $this->var["member"]["lastip"].",".$this->var["member"]["lastvisit"]);第一次访问的话设置访问IP的session C::t("common_member_status")->update($this->var["uid"], array("lastip" => $this->var["clientip"], "lastvisit" => TIMESTAMP));//更新用户的状态 ?>
|
核心代码第五句 $this->_init_mobile();discuz_application.php 680-789行
大概看了下,都是设置手机访问的一些变量,全局变量,cookie,session,跳转,常量等,有空细看下
核心代码第六句$this->_init_cron();discuz_application.php 508-515行
计划任务初始化 。配置文件中 $_config["remote"]["cron"] = 1;设置后;远程调用: 开启外部 cron 任务. 系统内部不再执行cron, cron任务由外部程序激活
1 2 3 4 5 6 |
$ext = empty ( $this ->config[ "remote" ][ "on" ]) ||
empty ( $this ->config[ "remote" ][ "cron" ]) || APPTYPEID == 200;
if ( $this ->init_cron &&
$this ->init_setting && $ext ) { //如果远程调用: 开启外部 cron 任务后调用discuz_cron::run();类
if ( $this -> var [ "cache" ][ "cronnextrun" ] <= TIMESTAMP) {
discuz_cron::run();
}
}
|
至此我们看到了一个完整的初始化过程
1 2 3 4 5 6 7 |
require_once "./source/class/class_core.php" ;
//引入核心类文件,作用为:自动引入类规则,错误和异常处理,单例创建discuz_application类实例,引入默认函数库function.core.php
require_once "./source/function/function_home.php" ; //项目函数库
$discuz = C::app(); //实例化desiuz_application类
$cachelist = array ( "magic" , "userapp" , "usergroups" ,
"diytemplatenamehome" );
$discuz ->cachelist =
$cachelist ; //设置缓存列表
$discuz ->init(); //初始化整个应用
|
下一章将讲到discuz控制器和模板
转载自:http://hi.baidu.com/tong_jh/item/9980dd3d0115e583c2cf293a
- 上一篇:没有了
- 下一篇: 使用ThinkPHP扩展,实现Redis的CURD操作。