下面的部分是mSession的实现,它只是模拟了session的存取过程,并对系统session进行了改进。它用了Hash目录。它的缺点是在程序结束部分还要Rewrite一下,把数据更新到session文件里,当然这个很容易被改进。
<?php
classBsmSession
{
var$sid;
var$sess_file;
functionmSession_Start()
{
//SpecialFunction...session_start()
global$cookie_sess_id_varname,$cookie_path,$sess_liftime,$mSession;
$sid=$_COOKIE[$cookie_sess_id_varname]?$_COOKIE[$cookie_sess_id_varname]:$this->_Gen_Sid();
setcookie($cookie_sess_id_varname,$sid,$sess_liftime,$cookie_path);
$sess_file=$this->_Hash_Dir($sid).'sess_'.$sid;
if(file_exists($sess_file)){
if(!@$fp=fopen($sess_file,'rb')){
//DebugInfo...NoLog.
fatal_error('SessionError...');
}
if(0==($fl=filesize($sess_file)))
$sess_content='';
else
$sess_content=fread($fp,$fl);
}
else{
if(!@$fp=fopen($sess_file,'wb')){
//DebugInfo...NoLog.
fatal_error('SessionError...');
}
$sess_content='';
}
fclose($fp);
$this->sid=$sid;
$this->sess_file=$sess_file;
$mSession=unserialize($sess_content)or$mSession=array();
}
functionmSession_Destroy()
{
global$mSession;
$mSession=array();
return@unlink($this->sess_file);
}
functionmSession_Rewrite()
{
//RestoreSessionDataintoSessionFile
global$mSession;
$sess_content=serialize($mSession);
if(!@$fp=fopen($this->sess_file,'wb')){
//DebugInfo...NoLog.
fatal_error('SessionError...');
}
fwrite($fp,$sess_content);
fclose($fp);
return;
}
function_Hash_Dir($sid)
{
//HashtheSessionfileDir
global$user_sess_base_dir;
$sess_dir=$user_sess_base_dir.substr($sid,0,1).'/'.substr($sid,16,1).'/';
return$sess_dir;
}
function_Gen_Sid()
{
//GenanUniqueSessionID
$key_1=rand(32768,65535);
$key_2=microtime();
$key_3=sha1(time());
$sid=md5($key_1.$key_3.$key_2);
return$sid;
}
function_Get_Sid()
{
//GetCurrentSessionID
global$cookie_sess_id_varname;
$sid=$_COOKIE[$cookie_sess_id_varname]?$_COOKIE[$cookie_sess_id_varname]:FALSE;
return$sid;
}
}
?>
Hash目录是一种优化文件存储性能的方法。无论是Windows还是Linux,无论是NTFS还是ext3,每个目录下所能容纳的项目数是有限的。并不是不能保存,而是当项目数量过大的时候,会降低文件索引速度,所以权衡一个目录下应该保存多少文件是很必要的。保存得多了会影响性能,保存得少了会造成目录太多和空间浪费。所以当保存大批文件的时候,需要有一种算法能将文件比较均匀地“打散”在不同的子目录下以提高每一级的索引速度,这种算法就是Hash。通常用的MD5、sha1等都可以用来做Hash目录,我的mSession里也同样使用了MD5,取得sessionID的第一位和第九位,这就构成了两级Hash路径,也就是说,系统把所有的Session文件分散到了16×16=256个子目录下。假设Linux每个目录下保存1000个文件可以获得最好的空间性能比,那么系统在理想情况下可以同时有256000个session文件在被使用。classBsmSession
{
var$sid;
var$sess_file;
functionmSession_Start()
{
//SpecialFunction...session_start()
global$cookie_sess_id_varname,$cookie_path,$sess_liftime,$mSession;
$sid=$_COOKIE[$cookie_sess_id_varname]?$_COOKIE[$cookie_sess_id_varname]:$this->_Gen_Sid();
setcookie($cookie_sess_id_varname,$sid,$sess_liftime,$cookie_path);
$sess_file=$this->_Hash_Dir($sid).'sess_'.$sid;
if(file_exists($sess_file)){
if(!@$fp=fopen($sess_file,'rb')){
//DebugInfo...NoLog.
fatal_error('SessionError...');
}
if(0==($fl=filesize($sess_file)))
$sess_content='';
else
$sess_content=fread($fp,$fl);
}
else{
if(!@$fp=fopen($sess_file,'wb')){
//DebugInfo...NoLog.
fatal_error('SessionError...');
}
$sess_content='';
}
fclose($fp);
$this->sid=$sid;
$this->sess_file=$sess_file;
$mSession=unserialize($sess_content)or$mSession=array();
}
functionmSession_Destroy()
{
global$mSession;
$mSession=array();
return@unlink($this->sess_file);
}
functionmSession_Rewrite()
{
//RestoreSessionDataintoSessionFile
global$mSession;
$sess_content=serialize($mSession);
if(!@$fp=fopen($this->sess_file,'wb')){
//DebugInfo...NoLog.
fatal_error('SessionError...');
}
fwrite($fp,$sess_content);
fclose($fp);
return;
}
function_Hash_Dir($sid)
{
//HashtheSessionfileDir
global$user_sess_base_dir;
$sess_dir=$user_sess_base_dir.substr($sid,0,1).'/'.substr($sid,16,1).'/';
return$sess_dir;
}
function_Gen_Sid()
{
//GenanUniqueSessionID
$key_1=rand(32768,65535);
$key_2=microtime();
$key_3=sha1(time());
$sid=md5($key_1.$key_3.$key_2);
return$sid;
}
function_Get_Sid()
{
//GetCurrentSessionID
global$cookie_sess_id_varname;
$sid=$_COOKIE[$cookie_sess_id_varname]?$_COOKIE[$cookie_sess_id_varname]:FALSE;
return$sid;
}
}
?>
Hash目录还被广泛应用在备份、图库、电子邮件、静态页生成等文件密集型应用上。
再来点一下我的模板类,我很懒地保留了Discuz模板函数的所有标签。一方面是我确实很懒,另一方面是我曾经试图修改Discuz,把它改成一个专用的版本,不过这是一个类,它的使用方法和Discuz函数没什么两样,都是include一个parse结果返回的文件名。
所不同的是在处理{template}标签的时候。Discuz的处理方式是把{template}替换成再次调用模板解析函数去解析另一个模板文件,这样,模板函数可能会被调用多次,编译的结果里也会有很多include另一个模板文件Parse结果的地方。这里涉及另一个优化点——尽量少地include文件。过多地include会带来更多的IO开销和CPU处理开销,所以我把{template}改成直接读入文件内容,然后再parse。这样一个模板文件即使有1000个{template},编译的结果也只有一个文件。
