CI框架Session.php源码分析
CI的Session并不是原生的session,正是我前面所有的cookiebasedsession,另外,CI可以根据用户选择配置是否将session存入数据库中,本人很喜欢这个功能,还有就是“闪出数据”的功能,既闪出数据只是对下次服务器请求可以,之后就会被自动清除。常见使用方法有:
$this->session->set_userdata('some_name','some_value');//设置session数据
$this->session->userdata('item');//获取session数据
$this->session->unset_userdata('some_name');//删除session数据
$this->session->sess_destroy();//销毁session数据
$this->session->set_flashdata('item','value');//设置闪存数据
$this->session->flashdata('item');//获取闪存数据
$this->session->keep_flashdata('item'); //保留闪存数据
/** *CI是sessionbasedcookie */ classCI_Session{ var$sess_encrypt_cookie =FALSE; //是否对session加密 var$sess_use_database =FALSE;//是否将session存入数据库 var$sess_table_name ='';//session存入数据的表名 var$sess_expiration =7200;//session的过期时间 var$sess_expire_on_close =FALSE;//当浏览器窗口关闭时是否自动使session过期 var$sess_match_ip =FALSE;//是否通过用户的IP地址来读取session的数据 var$sess_match_useragent =TRUE;//是否要按照对应的UserAgent来读取session数据。 var$sess_cookie_name ='ci_session';//cookie名称 var$cookie_prefix ='';//cookie前缀 var$cookie_path ='';//cookie路径 var$cookie_domain ='';//cookie作用域 var$cookie_secure =FALSE;//是否在安全的https协议下才有效 var$sess_time_to_update =300;//sessioncookie多久更新一次 var$encryption_key ='';//加密key var$flashdata_key ='flash'; var$time_reference ='time'; var$gc_probability =5; //回收session的能力 var$userdata =array();//用户session数据保存变量 var$CI;//CI超级句柄 var$now; //当前时间 publicfunction__construct($params=array()) { log_message('debug',"SessionClassInitialized"); //获取CI超级类 $this->CI=&get_instance(); //获取config文件中的配置数据 foreach(array('sess_encrypt_cookie','sess_use_database','sess_table_name','sess_expiration','sess_expire_on_close','sess_match_ip','sess_match_useragent','sess_cookie_name','cookie_path','cookie_domain','cookie_secure','sess_time_to_update','time_reference','cookie_prefix','encryption_key')as$key) { $this->$key=(isset($params[$key]))?$params[$key]:$this->CI->config->item($key); } //必须设置encryption_key if($this->encryption_key=='') { show_error('InordertousetheSessionclassyouarerequiredtosetanencryptionkeyinyourconfigfile.'); } //加载stringhelper函数 $this->CI->load->helper('string'); //D如果对cookie进行加密,则引入加密类 if($this->sess_encrypt_cookie==TRUE) { $this->CI->load->library('encrypt'); } //如果session计入数据库,则引入db if($this->sess_use_database===TRUEAND$this->sess_table_name!='') { $this->CI->load->database(); } //获取当前时间 $this->now=$this->_get_time(); //如果没有设置session的有效时间,默认两年 if($this->sess_expiration==0) { $this->sess_expiration=(60*60*24*365*2); } //获取cookie名称 $this->sess_cookie_name=$this->cookie_prefix.$this->sess_cookie_name; //如果session不存在,创建新会话 if(!$this->sess_read()) { $this->sess_create(); } else { $this->sess_update(); } //闪出标记old的闪出数据 $this->_flashdata_sweep(); //将新的闪出数据标记成old标记成old的数据的数据将会在下次请求时闪出 $this->_flashdata_mark(); //回收/删除过期的session $this->_sess_gc(); log_message('debug',"Sessionroutinessuccessfullyrun"); } //-------------------------------------------------------------------- /** *读取session数据 */ functionsess_read() { //获取session $session=$this->CI->input->cookie($this->sess_cookie_name); //没有session 拜拜 if($session===FALSE) { log_message('debug','Asessioncookiewasnotfound.'); returnFALSE; } //如果加密了cookie if($this->sess_encrypt_cookie==TRUE) { $session=$this->CI->encrypt->decode($session); } else { //encryptionwasnotused,soweneedtocheckthemd5hash $hash =substr($session,strlen($session)-32);//getlast32chars $session=substr($session,0,strlen($session)-32); //Doesthemd5hashmatch? Thisistopreventmanipulationofsessiondatainuserspace if($hash!== md5($session.$this->encryption_key)) { log_message('error','Thesessioncookiedatadidnotmatchwhatwasexpected.Thiscouldbeapossiblehackingattempt.'); $this->sess_destroy(); returnFALSE; } } //反序列化存入cookie的session数组 $session=$this->_unserialize($session); //检测session的数据 if(!is_array($session)OR!isset($session['session_id'])OR!isset($session['ip_address'])OR!isset($session['user_agent'])OR!isset($session['last_activity'])) { $this->sess_destroy(); returnFALSE; } //session数据过期了吗 if(($session['last_activity']+$this->sess_expiration)<$this->now) { $this->sess_destroy(); returnFALSE; } //是否是根据用户ip来读取session数据 if($this->sess_match_ip==TRUEAND$session['ip_address']!=$this->CI->input->ip_address()) { $this->sess_destroy(); returnFALSE; } //是否根据ua来匹配session数据 if($this->sess_match_useragent==TRUEANDtrim($session['user_agent'])!=trim(substr($this->CI->input->user_agent(),0,120))) { $this->sess_destroy(); returnFALSE; } //如果session入库了,这种方式应该更安全点,当然负载大时,就不建议了,数据库读写压力大 if($this->sess_use_database===TRUE) { $this->CI->db->where('session_id',$session['session_id']); if($this->sess_match_ip==TRUE) { $this->CI->db->where('ip_address',$session['ip_address']); } if($this->sess_match_useragent==TRUE) { $this->CI->db->where('user_agent',$session['user_agent']); } $query=$this->CI->db->get($this->sess_table_name); //Noresult? Killit! if($query->num_rows()==0) { $this->sess_destroy(); returnFALSE; } //Istherecustomdata? Ifso,addittothemainsessionarray $row=$query->row(); if(isset($row->user_data)AND$row->user_data!='') { $custom_data=$this->_unserialize($row->user_data); if(is_array($custom_data)) { foreach($custom_dataas$key=>$val) { $session[$key]=$val; } } } } //Sessionisvalid! $this->userdata=$session; unset($session); returnTRUE; } //-------------------------------------------------------------------- /** *将读取的session数据写入 */ functionsess_write() { //是否写入db if($this->sess_use_database===FALSE) { $this->_set_cookie(); return; } //setthecustomuserdata,thesessiondatawewillsetinasecond $custom_userdata=$this->userdata; $cookie_userdata=array(); //Beforecontinuing,weneedtodetermineifthereisanycustomdatatodealwith. //Let'sdeterminethisbyremovingthedefaultindexestoseeifthere'sanythingleftinthearray //andsetthesessiondatawhilewe'reatit foreach(array('session_id','ip_address','user_agent','last_activity')as$val) { unset($custom_userdata[$val]); $cookie_userdata[$val]=$this->userdata[$val]; } //Didwefindanycustomdata? Ifnot,weturntheemptyarrayintoastring //sincethere'snoreasontoserializeandstoreanemptyarrayintheDB if(count($custom_userdata)===0) { $custom_userdata=''; } else { //Serializethecustomdataarraysowecanstoreit $custom_userdata=$this->_serialize($custom_userdata); } //更新session记录 $this->CI->db->where('session_id',$this->userdata['session_id']); $this->CI->db->update($this->sess_table_name,array('last_activity'=>$this->userdata['last_activity'],'user_data'=>$custom_userdata)); //Writethecookie. Noticethatwemanuallypassthecookiedataarraytothe //_set_cookie()function.Normallythatfunctionwillstore$this->userdata,but //inthiscasethatarraycontainscustomdata,whichwedonotwantinthecookie. $this->_set_cookie($cookie_userdata); } //-------------------------------------------------------------------- /** *创建一个新的session */ functionsess_create() { //保证sessid安全唯一 $sessid=''; while(strlen($sessid)<32) { $sessid.=mt_rand(0,mt_getrandmax()); } $sessid.=$this->CI->input->ip_address(); $this->userdata=array( 'session_id' =>md5(uniqid($sessid,TRUE)), 'ip_address' =>$this->CI->input->ip_address(), 'user_agent' =>substr($this->CI->input->user_agent(),0,120), 'last_activity' =>$this->now, 'user_data' =>'' ); //SavethedatatotheDBifneeded if($this->sess_use_database===TRUE) { $this->CI->db->query($this->CI->db->insert_string($this->sess_table_name,$this->userdata)); } //Writethecookie $this->_set_cookie(); } //-------------------------------------------------------------------- /** *更新session */ functionsess_update() { //默认五分钟更新 if(($this->userdata['last_activity']+$this->sess_time_to_update)>=$this->now) { return; } //Savetheoldsessionidsoweknowwhichrecordto //updateinthedatabaseifweneedit $old_sessid=$this->userdata['session_id']; $new_sessid=''; while(strlen($new_sessid)<32) { $new_sessid.=mt_rand(0,mt_getrandmax()); } //TomakethesessionIDevenmoresecurewe'llcombineitwiththeuser'sIP $new_sessid.=$this->CI->input->ip_address(); //Turnitintoahash $new_sessid=md5(uniqid($new_sessid,TRUE)); //Updatethesessiondatainthesessiondataarray $this->userdata['session_id']=$new_sessid; $this->userdata['last_activity']=$this->now; //_set_cookie()willhandlethisforusifwearen'tusingdatabasesessions //bypushingalluserdatatothecookie. $cookie_data=NULL; //更新数据库中几率 if($this->sess_use_database===TRUE) { //setcookieexplicitlytoonlyhaveoursessiondata $cookie_data=array(); foreach(array('session_id','ip_address','user_agent','last_activity')as$val) { $cookie_data[$val]=$this->userdata[$val]; } $this->CI->db->query($this->CI->db->update_string($this->sess_table_name,array('last_activity'=>$this->now,'session_id'=>$new_sessid),array('session_id'=>$old_sessid))); } //重新写入session $this->_set_cookie($cookie_data); } //-------------------------------------------------------------------- /** *销毁当前所有session数据 */ functionsess_destroy() { //KillthesessionDBrow if($this->sess_use_database===TRUE&&isset($this->userdata['session_id'])) { $this->CI->db->where('session_id',$this->userdata['session_id']); $this->CI->db->delete($this->sess_table_name); } //Killthecookie setcookie( $this->sess_cookie_name, addslashes(serialize(array())), ($this->now-31500000), $this->cookie_path, $this->cookie_domain, 0 ); //Killsessiondata $this->userdata=array(); } //-------------------------------------------------------------------- /** *获取session数组指定元素的值 */ functionuserdata($item) { return(!isset($this->userdata[$item]))?FALSE:$this->userdata[$item]; } //-------------------------------------------------------------------- /** *获取所有session数据 */ functionall_userdata() { return$this->userdata; } //-------------------------------------------------------------------- /** *添加和修改自定义的session数据 */ functionset_userdata($newdata=array(),$newval='') { if(is_string($newdata)) { $newdata=array($newdata=>$newval); } //支持数组组合方式 if(count($newdata)>0) { foreach($newdataas$key=>$val) { $this->userdata[$key]=$val; } } $this->sess_write(); } //-------------------------------------------------------------------- /** *删除session数组中的元素, */ functionunset_userdata($newdata=array()) { if(is_string($newdata)) { $newdata=array($newdata=>''); } if(count($newdata)>0) { foreach($newdataas$key=>$val) { unset($this->userdata[$key]); } } $this->sess_write(); } //------------------------------------------------------------------------ /** *Addorchangeflashdata,onlyavailable *untilthenextrequest * *@access public *@param mixed *@param string *@return void */ functionset_flashdata($newdata=array(),$newval='') { if(is_string($newdata)) { $newdata=array($newdata=>$newval); } if(count($newdata)>0) { foreach($newdataas$key=>$val) { $flashdata_key=$this->flashdata_key.':new:'.$key; $this->set_userdata($flashdata_key,$val); } } } //------------------------------------------------------------------------ /** *CI支持闪出数据也就是说Session数据只对下次服务器请求可用,有时候如果你还想在下个请求后的请求还有效。。。 *keep_flashdata功能就是讲持续保持闪出数据,使其在下个请求也有效 */ functionkeep_flashdata($key) { //将闪出数据标记成new $old_flashdata_key=$this->flashdata_key.':old:'.$key; $value=$this->userdata($old_flashdata_key); $new_flashdata_key=$this->flashdata_key.':new:'.$key; $this->set_userdata($new_flashdata_key,$value); } //------------------------------------------------------------------------ /** *获取闪出数据 */ functionflashdata($key) { $flashdata_key=$this->flashdata_key.':old:'.$key; return$this->userdata($flashdata_key); } //------------------------------------------------------------------------ /** *将闪出数据标记成old,以便_flashdata_sweep清除数据 */ function_flashdata_mark() { $userdata=$this->all_userdata(); foreach($userdataas$name=>$value) { $parts=explode(':new:',$name); if(is_array($parts)&&count($parts)===2) { $new_name=$this->flashdata_key.':old:'.$parts[1]; $this->set_userdata($new_name,$value); $this->unset_userdata($name); } } } //------------------------------------------------------------------------ /** *将标记成old的闪出数据闪出 */ function_flashdata_sweep() { $userdata=$this->all_userdata(); foreach($userdataas$key=>$value) { if(strpos($key,':old:')) { $this->unset_userdata($key); } } } //获取当前时间 function_get_time() { if(strtolower($this->time_reference)=='gmt') { $now=time(); $time=mktime(gmdate("H",$now),gmdate("i",$now),gmdate("s",$now),gmdate("m",$now),gmdate("d",$now),gmdate("Y",$now)); } else { $time=time(); } return$time; } //-------------------------------------------------------------------- /** *写入sessioncookie * */ function_set_cookie($cookie_data=NULL) { if(is_null($cookie_data)) { $cookie_data=$this->userdata; } //序列化数组 $cookie_data=$this->_serialize($cookie_data); //加密数据 if($this->sess_encrypt_cookie==TRUE) { $cookie_data=$this->CI->encrypt->encode($cookie_data); } else { //ifencryptionisnotused,weprovideanmd5hashtopreventusersidetampering $cookie_data=$cookie_data.md5($cookie_data.$this->encryption_key); } //sess_expire_on_close为TRUE则,浏览器关闭,session失效 $expire=($this->sess_expire_on_close===TRUE)?0:$this->sess_expiration+time(); //Setthecookie setcookie( $this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain, $this->cookie_secure ); } //-------------------------------------------------------------------- /** *序列化数组 */ function_serialize($data) { if(is_array($data)) { foreach($dataas$key=>$val) { if(is_string($val)) { $data[$key]=str_replace('\\','{{slash}}',$val); } } } else { if(is_string($data)) { $data=str_replace('\\','{{slash}}',$data); } } returnserialize($data); } //-------------------------------------------------------------------- /** *反序列化数组 */ function_unserialize($data) { $data=@unserialize(strip_slashes($data)); if(is_array($data)) { foreach($dataas$key=>$val) { if(is_string($val)) { $data[$key]=str_replace('{{slash}}','\\',$val); } } return$data; } return(is_string($data))?str_replace('{{slash}}','\\',$data):$data; } //-------------------------------------------------------------------- /** *回收/删除数据库中失效的session信息 */ function_sess_gc() { if($this->sess_use_database!=TRUE) { return; } srand(time()); if((rand()%100)<$this->gc_probability) { $expire=$this->now-$this->sess_expiration; $this->CI->db->where("last_activity<{$expire}"); $this->CI->db->delete($this->sess_table_name); log_message('debug','Sessiongarbagecollectionperformed.'); } } }