PHP session有效期问题


Posted in PHP onApril 26, 2009

一个已知管用的方法是,使用session_set_save_handler,接管所有的session管理工作,一般是把session信息存储到数据库,这样可以通过SQL语句来删除所有过期的session,精确地控制session的有效期。这也是基于PHP的大型网站常用的方法。但是,一般的小型网站,似乎没有必要这么劳师动众。
但是一般的Session的生命期有限,如果用户关闭了浏览器,就不能保存Session的变量了!那么怎么样可以实现Session的永久生命期呢?
大家知道,Session储存在服务器端,根据客户端提供的SessionID来得到这个用户的文件,然后读取文件,取得变量的值,SessionID可以使用客户端的Cookie或者Http1.1协议的Query_String(就是访问的URL的“?”后面的部分)来传送给服务器,然后服务器读取Session的目录……
要实现Session的永久生命期,首先需要了解一下php.ini关于Session的相关设置(打开php.ini文件,在“[Session]”部分):
1、session.use_cookies:默认的值是“1”,代表SessionID使用Cookie来传递,反之就是使用Query_String来传递;
2、session.name:这个就是SessionID储存的变量名称,可能是Cookie,也可能是Query_String来传递,默认值是“PHPSESSID”;
3、session.cookie_lifetime:这个代表SessionID在客户端Cookie储存的时间,默认是0,代表浏览器一关闭SessionID就作废……就是因为这个所以Session不能永久使用!
4、session.gc_maxlifetime:这个是Session数据在服务器端储存的时间,如果超过这个时间,那么Session数据就自动删除!
还有很多的设置,不过和本文相关的就是这些了,下面开始讲使用永久Session的原理和步骤。
前面说过,服务器通过SessionID来读取Session的数据,但是一般浏览器传送的SessionID在浏览器关闭后就没有了,那么我们只需要人为的设置SessionID并且保存下来,不就可以……
如果你拥有服务器的操作权限,那么设置这个非常非常的简单,只是需要进行如下的步骤:
1、把“session.use_cookies”设置为1,打开Cookie储存SessionID,不过默认就是1,一般不用修改;
2、把“session.cookie_lifetime”改为正无穷(当然没有正无穷的参数,不过999999999和正无穷也没有什么区别);
3、把“session.gc_maxlifetime”设置为和“session.cookie_lifetime”一样的时间;
在PHP的文档中明确指出,设定session有效期的参数是session.gc_maxlifetime。可以在php.ini文件中,或者通过ini_set()函数来修改这一参数。问题在于,经过多次测试,修改这个参数基本不起作用,session有效期仍然保持24分钟的默认值。
由于PHP的工作机制,它并没有一个daemon线程,来定时地扫描session信息并判断其是否失效。当一个有效请求发生时,PHP会根据全局变量session.gc_probability/session.gc_divisor(同样可以通过php.ini或者ini_set()函数来修改)的值,来决定是否启动一个GC(Garbage Collector)。默认情况下,session.gc_probability = 1,session.gc_divisor =100,也就是说有1%的可能性会启动GC。
GC的工作,就是扫描所有的session信息,用当前时间减去session的最后修改时间(modified date),同session.gc_maxlifetime参数进行比较,如果生存时间已经超过gc_maxlifetime,就把该session删除。
到此为止,工作一切正常。那为什么会发生gc_maxlifetime无效的情况呢?
在默认情况下,session信息会以文本文件的形式,被保存在系统的临时文件目录中。在Linux下,这一路径通常为\tmp,在 Windows下通常为C:\Windows\Temp。当服务器上有多个PHP应用时,它们会把自己的session文件都保存在同一个目录中。同样地,这些PHP应用也会按一定机率启动GC,扫描所有的session文件。
问题在于,GC在工作时,并不会区分不同站点的session。举例言之,站点A的gc_maxlifetime设置为2小时,站点B的 gc_maxlifetime设置为默认的24分钟。当站点B的GC启动时,它会扫描公用的临时文件目录,把所有超过24分钟的session文件全部删除掉,而不管它们来自于站点A或B。这样,站点A的gc_maxlifetime设置就形同虚设了。
找到问题所在,解决起来就很简单了。修改session.save_path参数,或者使用session_save_path()函数,把保存session的目录指向一个专用的目录,gc_maxlifetime参数工作正常了。
严格地来说,这算是PHP的一个bug?
还有一个问题就是,gc_maxlifetime只能保证session生存的最短时间,并不能够保存在超过这一时间之后session信息立即会得到删除。因为GC是按机率启动的,可能在某一个长时间内都没有被启动,那么大量的session在超过gc_maxlifetime以后仍然会有效。解决这个问题的一个方法是,把session.gc_probability/session.gc_divisor的机率提高,如果提到100%,就会彻底解决这个问题,但显然会对性能造成严重的影响。另一个方法是自己在代码中判断当前session的生存时间,如果超出了 gc_maxlifetime,就清空当前session。
但是如果你没有服务器的操作权限,那就比较麻烦了,你需要通过PHP程序改写SessionID来实现永久的Session数据保存。查查php.net的函数手册,可以见到有“session_id”这个函数:如果没有设置参数,那么将返回当前的SessionID,如果设置了参数,就会将当前的SessionID设置为给出的值……
只要利用永久性的Cookie加上“session_id”函数,就可以实现永久Session数据保存了!
但是为了方便,我们需要知道服务器设置的“session.name”,但是一般用户都没有权限查看服务器的php.ini设置,不过PHP提供了一个非常好的函数“phpinfo”,利用这个可以查看几乎所有的PHP信息!
------------------------------------------------------------------------------------
<title>PHP相关信息显示</title>
<?phpinfo()?>
------------------------------------------------------------------------------------
打开编辑器,输入上面的代码,然后在浏览器中运行这个程序,会见到PHP的相关信息(如图1所示)。其中有一项“session.name”的参数,这个就是我们需要的服务器“session.name”,一般是“PHPSESSID”。
记下了SessionID的名称后,我们就可以实现永久的Session数据储存了!

session_start(); 
ini_set('session.save_path','/tmp/'); 
//6个钟头 
ini_set('session.gc_maxlifetime',21600); 
//保存一天 
$lifeTime = 24 * 3600; 
setcookie(session_name(), session_id(), time() + $lifeTime, "/");

后记:
其实真正的永久储存是不可能的,因为Cookie的保存时间有限,而服务器的空间也有限……但是对于一些需要保存时间比较长的站点,以上方法就已经足够了!
把session放入mysql的Example:
数据库里建表:session ( sesskey varchar32 , expiry int11 , value longtext)
code:
代码执行前已经连接数据库了。
define('STORE_SESSIONS','mysql'); if (STORE_SESSIONS == 'mysql') { 
if (!$SESS_LIFE = get_cfg_var('session.gc_maxlifetime')) { 
$SESS_LIFE = 1440; 
} 
function _sess_open($save_path, $session_name) { 
// 如果没有连接数据库,可以在此执行mysql_pconnect,mysql_select_db 
return true; 
} 
function _sess_close() { 
return true; 
} 
function _sess_read($key) { 
$value_query = mysql_query("select value from sessions where sesskey = '" .addslashes($key) . "' and expiry > '" . time() . "'"); 
$value = mysql_fetch_array($value_query); 
if (isset($value['value'])) { 
return $value['value']; 
} 
return false; 
} 
function _sess_write($key, $val) { 
global $SESS_LIFE; 
$expiry = time() + $SESS_LIFE; 
$value = $val; 
$check_query = mysql_query("select count(*) as total from sessions where sesskey = '" . addslashes($key) . "'"); 
$check = mysql_fetch_array($check_query); 
if ($check['total'] > 0) { 
return mysql_query("update sessions set expiry = '" . addslashes($expiry) . "', value = '" . addslashes($value) . "' where sesskey = '" . addslashes($key) . "'"); 
} else { 
return mysql_query("insert into sessions values ('" . addslashes($key) . "', '" . addslashes($expiry) . "', '" . addslashes($value) . "')"); 
} 
} 
function _sess_destroy($key) { 
return mysql_query("delete from sessions where sesskey = '" . addslashes($key) . "'"); 
} 
function _sess_gc($maxlifetime) { 
mysql_query("delete from sessions where expiry < '" . time() . "'"); 
return true; 
} 
session_set_save_handler('_sess_open', '_sess_close', '_sess_read', '_sess_write', '_sess_destroy', '_sess_gc'); 
} 
danoo_session_name( 'dtvSid' ); 
danoo_session_save_path(SESSION_WRITE_DIRECTORY);

还是有点不明白,open,write那些参数哪里来的。
修改php.ini配置的两个常用函数:
get_cfg_var('session.gc_maxlifetime') : 取得session.gc_maxlifetime的值
ini_set('session.cookie_lifetime','0') : 设置session.cookie_lifetime的值为0。
PHP 相关文章推荐
PHP实现域名whois查询的代码(数据源万网、新网)
Feb 22 PHP
如何用php获取程序执行的时间
Jun 09 PHP
Yii操作数据库的3种方法
Mar 11 PHP
Laravel框架表单验证详解
Sep 04 PHP
PHP统计数值数组中出现频率最多的10个数字的方法
Apr 20 PHP
如何使用GDB调试PHP程序
Dec 08 PHP
PHP统计目录中文件以及目录中目录大小的方法
Jan 09 PHP
必须收藏的php实用代码片段
Feb 02 PHP
TP3.2批量上传文件或图片 同名冲突问题的解决方法
Aug 01 PHP
[原创]PHP实现字节数Byte转换为KB、MB、GB、TB的方法
Aug 31 PHP
PHP实现双链表删除与插入节点的方法示例
Nov 11 PHP
PHP实现八皇后算法
May 06 PHP
PHP 判断常量,变量和函数是否存在
Apr 26 #PHP
PHP memcache扩展的三种安装方法
Apr 26 #PHP
PHP mb_convert_encoding 获取字符串编码类型实现代码
Apr 26 #PHP
php mssql 分页SQL语句优化 持续影响
Apr 26 #PHP
配置Apache2.2+PHP5+CakePHP1.2+MySQL5运行环境
Apr 25 #PHP
Uchome1.2 1.5 代码学习 common.php
Apr 24 #PHP
mysql_fetch_row,mysql_fetch_array,mysql_fetch_assoc的区别
Apr 24 #PHP
You might like
php gd2 上传图片/文字水印/图片水印/等比例缩略图/实现代码
2010/05/15 PHP
PHP速成大法
2015/01/30 PHP
PHP+jquery+CSS制作头像登录窗(仿QQ登陆)
2016/10/20 PHP
JavaScript之appendChild、insertBefore和insertAfter使用说明
2010/12/30 Javascript
jQuery 瀑布流 绝对定位布局(二)(延迟AJAX加载图片)
2012/05/23 Javascript
Js动态添加复选框Checkbox的实例方法
2013/04/08 Javascript
firefox浏览器不支持innerText的解决方法
2013/08/07 Javascript
上传图片js判断图片尺寸和格式兼容IE
2014/09/01 Javascript
js简单抽奖代码
2015/01/16 Javascript
js实现图片从左往右渐变切换效果的方法
2015/02/06 Javascript
JS日期加减,日期运算代码
2015/11/05 Javascript
几种二级联动案例(jQuery\Array\Ajax php)
2016/08/13 Javascript
Vue 理解之白话 getter/setter详解
2019/04/16 Javascript
微信小程序 轮播图实现原理及优化详解
2019/09/29 Javascript
vue 授权获取微信openId操作
2020/11/13 Javascript
[03:12]2016完美“圣”典风云人物:单车专访
2016/12/02 DOTA
[44:22]完美世界DOTA2联赛循环赛 FTD vs PXG BO2第一场 11.01
2020/11/02 DOTA
python web自制框架之接受url传递过来的参数实例
2018/12/17 Python
Python 使用Numpy对矩阵进行转置的方法
2019/01/28 Python
Python求离散序列导数的示例
2019/07/10 Python
Django Rest framework三种分页方式详解
2019/07/26 Python
Python绘制三角函数图(sin\cos\tan)并标注特定范围的例子
2019/12/04 Python
python梯度下降算法的实现
2020/02/24 Python
PyCharm永久激活方式(推荐)
2020/09/22 Python
如何利用Python 进行边缘检测
2020/10/14 Python
Python 微信公众号文章爬取的示例代码
2020/11/30 Python
保卫科工作岗位职责
2014/03/01 职场文书
初中作文评语大全
2014/04/23 职场文书
初中学校军训方案
2014/05/09 职场文书
仓管员岗位职责
2015/02/03 职场文书
酒店优秀员工推荐信
2015/03/24 职场文书
2015年仓库工作总结
2015/04/09 职场文书
大学生奖学金获奖感言(范文)
2019/08/15 职场文书
Django migrate报错的解决方案
2021/05/20 Python
解决Tkinter中button按钮未按却主动执行command函数的问题
2021/05/23 Python
Python实现猜拳与猜数字游戏的方法详解
2022/04/06 Python