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程序设计有所帮助。