Discuz!X中SESSION机制实例详解
本文实例讲述了Discuz!X中SESSION机制。分享给大家供大家参考。具体如下:
在Discuz!X中一如继往的,SESSION并没有使用PHP自带的SESSION机制,而是系统的一套自带的机制。
在数据库中可以看到有两个SESSION表:
一个是pre_common_adminsession,是管理员登录后台的SESSION表;
另一个是pre_common_session表,是所有用户在前台浏览页面时的SESSION表。
这两个表都是内存表(内存表的读写速度远高于MYISAM表及文本文件)。
在Discuz!X中SESSION与COOKIE是分不开的,因为SESSION就是从客户端读取的COOKIE,
然后由浏览页面时触发相关的函数执行,再写入数据库SESSION表。
我以登录流程为例来讲解程序具体是如何执行的。
在前台首页,点击登录后,弹出一个登录窗口,填写好数据后,提交。form表单提交的URL是:
<ahref="http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1">http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1</a>
数据提交到了member.php文件中,在程序中可看到下面的代码:
$mod=!in_array($discuz->var['mod'],$modarray)?'logging':$discuz->var['mod'];//mod的值即是接下来加载的php页面
define('CURMODULE',$mod);
$modcachelist=array('register'=>array('modreasons','stamptypeid','fields_required','fields_optional','ipctrl'));
$cachelist=array();
if(isset($modcachelist[CURMODULE])){
$cachelist=$modcachelist[CURMODULE];
}
$discuz->cachelist=$cachelist;
$discuz->init();
runhooks();
requireDISCUZ_ROOT.'./source/module/member/member_'.$mod.'.php';//完成程序的包含操作
打开source/module/member/member_logging.php文件,是一个类,在类的前面可看到下面三句代码:
$ctl_obj=newlogging_ctl(); $method='on_'.$_G['gp_action'];//$_G['gp_action']等于action的值即login $ctl_obj->$method();//$ctl_obj->on_login();
在类中可找到login方法,在方法中,大约56行有下面一个判断语句:
if(!submitcheck('loginsubmit',1,$seccodecheck)){
//判断语句是当游客浏览时,submitcheck函数的返回值是假,取反,为真。
//当用户登录时,程序走的是else部分,在里面可看到下面五句代码:
}else{
$_G['uid']=$_G['member']['uid']=0;
$_G['username']=$_G['member']['username']=$_G['member']['password']='';//变量赋值
$result=userlogin($_G['gp_username'],$_G['gp_password'],$_G['gp_questionid'],$_G['gp_answer'],$_G['setting']['autoidselect']?'auto':$_G['gp_loginfield']);//从数据库查询用户数据,并返回相应的信息
if($result['status']>0){//状态值大于0,说明有此用户,可以登录
setloginstatus($result['member'],$_G['gp_cookietime']?2592000:0);//设置登录状态,即是写COOKIE操作,COOKIE中的数据即是SESSION中相应的数据,但此函数并不负责写SESSION的操作
我们来看一下source/function/function_login.php中的setloginstatus函数,是普通的写COOKIE操作,不再具体讲解:
functionsetloginstatus($member,$cookietime){
global$_G;
$_G['uid']=$member['uid'];
$_G['username']=$member['username'];
$_G['adminid']=$member['adminid'];
$_G['groupid']=$member['groupid'];
$_G['formhash']=formhash();
$_G['session']['invisible']=getuserprofile('invisible');
$_G['member']=$member;
$_G['core']->session->isnew=1;
dsetcookie('auth',authcode("{$member['password']}\t{$member['uid']}",'ENCODE'),$cookietime,1,true);//authcode加密
dsetcookie('loginuser');
dsetcookie('activationauth');
dsetcookie('pmnum');
}
到这里可以说是登录流程大部分已经走完,但是COOKIE不清除时,会一直存在于客户端,如果超时,程序中会在判断弃用此COOKIE,并重新写入。
下面我们来看一下DZX中SESSION操作的类,在source/class/calss_core.php文件中:
程序中每次请求都会加载SESSION,这是由核心类discuz_core中的_init_session方法来执行的,此方法被置于类的init方法中,说明每次加载类,会自动将SESSION写入。
function_init_session(){
$this->session=newdiscuz_session();//创建SESSION类
if($this->init_session){
//从COOKIE中读取数据
$this->session->init($this->var['cookie']['sid'],$this->var['clientip'],$this->var['uid']);
$this->var['sid']=$this->session->sid;
$this->var['session']=$this->session->var;
//判断SID是否相等,不等,说明是多个用户在同一主机上登录网站,需要重新写COOKIE
if($this->var['sid']!=$this->var['cookie']['sid']){
dsetcookie('sid',$this->var['sid'],86400);
}
if($this->session->isnew){
if(ipbanned($this->var['clientip'])){
$this->session->set('groupid',6);
}
}
if($this->session->get('groupid')==6){
$this->var['member']['groupid']=6;
sysmessage('user_banned');
}
//UID不为空,且需要更新SESSION或是SESSION超时,更改用户状态,需要用户重新登录
if($this->var['uid']&&($this->session->isnew||($this->session->get('lastactivity')+600)<TIMESTAMP)){
$this->session->set('lastactivity',TIMESTAMP);
$update=array('lastip'=>$this->var['clientip'],'lastactivity'=>TIMESTAMP);
if($this->session->isnew){
$update['lastvisit']=TIMESTAMP;
}
DB::update('common_member_status',$update,"uid='".$this->var['uid']."'");
}
}
}
操作SESSION的类是discuz_session,我们看这个类里面的两个方法:
//此函数负责产生新的SESSION,但并不负责写入数据库
functioncreate($ip,$uid){
//创建SESSION,执行插入数据,由随机函数产生一个六位随机数即是session的唯一值时间为当前时间,sid为cookie中的sid
$this->isnew=true;
$this->var=$this->newguest;
$this->set('sid',random(6));
$this->set('uid',$uid);
$this->set('ip',$ip);
$this->set('lastactivity',time());
$this->sid=$this->var['sid'];
return$this->var;
}
//此函数负责更新SESSION
functionupdate(){
if($this->sid!==null){
$data=daddslashes($this->var);
if($this->isnew){
$this->delete();
DB::insert('common_session',$data,false,false,true);
}else{
DB::update('common_session',$data,"sid='$data[sid]'");
}
dsetcookie('sid',$this->sid,86400);
}
}
至此我们知道了SESSION插入数据库的具体函数,与COOKIE的联系,但还不清楚是如何触发此操作的。
打开source/function/function_core.php文件,找到函数,updatesession,此函数负责更新SESSION:
functionupdatesession($force=false){
global$_G;
static$updated=false;
if(!$updated){
$discuz=&discuz_core::instance();
foreach($discuz->session->varas$k=>$v){
if(isset($_G['member'][$k])&&$k!='lastactivity'){
$discuz->session->set($k,$_G['member'][$k]);
}
}
foreach($_G['action']as$k=>$v){
$discuz->session->set($k,$v);
}
$discuz->session->update();
$updated=true;
}
return$updated;
}
我们在程序源码中搜索此函数,可以看到在很多的模板中都有下面一句代码:
{evalupdatesession();}浏览页面时将触发此函数,并将SESSION写入数据库。
整理一下思绪:
第一步:用户登录,程序将COOKIE写入客户端,这些COOKIE即是SESSION的部分数据,如SID、IP、TIME,不包含用户名、密码等关键信息。
第二步,登录成功后,程序会自动刷新页面,向服务器再次发送请求,服务器加载discuz_core核心类,并从COOKIE中读取到SESSION的相关信息,但还没有写入数据库。
第三步,核心类加载完成,程序继续执行,最后加载模板,触发updatesession函数,SESSION被写入数据库。
希望本文所述对大家的php程序设计有所帮助。