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.');
}
}
}