本文共 5969 字,大约阅读时间需要 19 分钟。
自定义会话管理器
如果需要在数据库中或者以其他方式存储会话数据, 需要使用 函数来创建一系列用户级存储函数。 PHP 5.4.0 之后,你可以使用 类 或者通过继承 类来扩展内置的管理器, 从而达到自定义会话保存机制的目的。
函数 的参数即为在会话生命周期内要调用的一组回调函数: open, read, write 以及 close。 还有一些回调函数被用来完成垃圾清理:destroy 用来删除会话, gc 用来进行周期性的垃圾收集。
因此,会话保存管理器对于 PHP 而言是必需的。 默认情况下会使用内置的文件会话保存管理器。 可以通过 函数来设置自定义会话保存管理器。 一些 PHP 扩展也提供了内置的会话管理器,例如:sqlite, memcache 以及 memcached, 可以通过配置项 来使用它们。
会话开始的时候,PHP 会调用 open 管理器,然后再调用 read 回调函数来读取内容,该回调函数返回已经经过编码的字符串。 然后 PHP 会将这个字符串解码,并且产生一个数组对象,然后保存至 超级全局变量。
当 PHP 关闭的时候(或者调用了 之后), PHP 会对 中的数据进行编码, 然后和会话 ID 一起传送给 write 回调函数。 write 回调函数调用完毕之后,PHP 内部将调用 close 回调函数。
销毁会话时,PHP 会调用 destroy 回调函数。
根据会话生命周期时间的设置,PHP 会不时地调用 gc 回调函数。 该函数会从持久化存储中删除超时的会话数据。 超时是指会话最后一次访问时间距离当前时间超过了 $lifetime 所指定的值。
open 回调函数类似于类的构造函数, 在会话打开的时候会被调用。 这是自动开始会话或者通过调用 手动开始会话 之后第一个被调用的回调函数。 此回调函数操作成功返回 TRUE,反之返回FALSE。
close 回调函数类似于类的析构函数。 在 write 回调函数调用之后调用。 当调用 函数之后,也会调用 close 回调函数。 此回调函数操作成功返回 TRUE,反之返回 FALSE。
如果会话中有数据,read 回调函数必须返回将会话数据编码(序列化)后的字符串。 如果会话中没有数据,read 回调函数返回空字符串。
在自动开始会话或者通过调用 函数手动开始会话之后,PHP 内部调用 read 回调函数来获取会话数据。 在调用 read 之前,PHP 会调用 open 回调函数。
read 回调返回的序列化之后的字符串格式必须与 write 回调函数保存数据时的格式完全一致。 PHP 会自动反序列化返回的字符串并填充 超级全局变量。 虽然数据看起来和 函数很相似, 但是需要提醒的是,它们是不同的。 请参考: 。
在会话保存数据时会调用 write 回调函数。 此回调函数接收当前会话 ID 以及 中数据序列化之后的字符串作为参数。 序列化会话数据的过程由 PHP 根据 设定值来完成。
序列化后的数据将和会话 ID 关联在一起进行保存。 当调用 read 回调函数获取数据时,所返回的数据必须要和 传入 write 回调函数的数据完全保持一致。
PHP 会在脚本执行完毕或调用 函数之后调用此回调函数。 注意,在调用完此回调函数之后,PHP 内部会调用 close 回调函数。
当调用 函数, 或者调用 函数并且设置 destroy 参数为 TRUE 时, 会调用此回调函数。此回调函数操作成功返回 TRUE,反之返回 FALSE。
为了清理会话中的旧数据,PHP 会不时的调用垃圾收集回调函数。 调用周期由 和 参数控制。 传入到此回调函数的 lifetime 参数由 设置。 此回调函数操作成功返回 TRUE,反之返回 FALSE。
SESSION 的数据保存在哪里呢?[session.save_handler = files]
一. session_start()
1. session_start()是SESSION机制的开始,它有一定概率开启垃圾回收,因为SESSION 是存放在文件中,PHP自身的垃圾回收是无效的,SESSION的回收是要删文件的,这个概率是根据php.ini的配置决定的,但是有的系统是 session.gc_probability = 0,这也就是说概率是0,而是通过cron脚本来实现垃圾回收。
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440//过期时间 默认24分钟
//概率是 session.gc_probability/session.gc_divisor 结果 1/1000,
//不建议设置过小,因为SESSION 的垃圾回收,是需要检查每个文件是否过期的。
session.save_path = //好像不同的系统默认不一样,有一种设置是 "N;/path"
//这是随机分级存储,这个样的话,垃圾回收将不起作用,需要自己写脚本。
2. SESSION会判断当前是否有$_COOKIE[session_name()];session_name()返回保存session_id的COOKIE键值,这个值可以从php.ini找到。
session.name = PHPSESSID //默认值PHPSESSID
3. 如果不存在会生成一个session_id,然后把生成的session_id作为COOKIE的值传递到客户端。相当于执行了下面COOKIE 操作,注意的是,这一步执行了setcookie()操作,COOKIE是在header头中发送的,这之前是不能有输出的,PHP有另外一个函数 session_regenerate_id() 如果使用这个函数,这之前也是不能有输出的。
setcookie(session_name(),
session_id(),
session.cookie_lifetime,//默认0
session.cookie_path,//默认'/'当前程序跟目录下都有效
session.cookie_domain,//默认为空
)
4. 如果存在那么session_id = $_COOKIE[session_name];
然后去session.save_path指定的文件夹里去找名字为'SESS_' . session_id()的文件, 读取文件的内容反序列化,然后放到$_SESSION中。
二. 为$_SESSION赋值
比如新添加一个值$_SESSION['test'] = 'blah'; 那么这个$_SESSION只会维护在内存中,当脚本执行结束的时候,用把$_SESSION的值写入到session_id指定的文件夹中,然后关闭相关资源。这个阶段有可能执行更改session_id的操作,比如销毁一个旧的的session_id,生成一个全新的session_id。一般用在自定义 SESSION操作,角色的转换上,比如Drupal.Drupal的匿名用户有一个SESSION的,当它登录后需要换用新的session_id。
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 42000, '/');//旧session cookie过期
}
session_regenerate_id();//这一步会生成新的session_id
//session_id()返回的是新的值
三.写入SESSION操作
在脚本结束的时候会执行SESSION写入操作,把$_SESSION中值写入到session_id命名的文件中,可能已经存在,可能需要创建新的文件。
四. 销毁SESSION
SESSION发出去的COOKIE一般属于即时COOKIE,保存在内存中,当浏览器关闭后,才会过期,假如需要人为强制过期,比如退出登录,而不是关闭浏览器,那么就需要在代码里销毁SESSION,方法有很多。 1. setcookie(session_name(), session_id(), time() - 8000000, ..);//退出登录前执行 2. usset($_SESSION);//这会删除所有的$_SESSION数据,刷新后,有COOKIE传过来,但是没有数据。 3. session_destroy();//这个作用更彻底,删除$_SESSION 删除session文件,和session_id 当不关闭浏览器的情况下,再次刷新,2和3都会有COOKIE传过来,但是找不到数据。
[session.save_handler = USER]
用户自定义SESSION处理机制,更加直观。
session_set_save_handler('open', 'close', 'read', 'write', 'destroy', 'gc');
1.session_start(),
执行open($save_path, $session_name)打开session操作句柄,$save_path 在session.save_handler = files的情况下它就是session.save_path,但是如果用户自定的话,这个两个参数都用不上,直接返回TRUE。 执行read($id)从中读取数据。//这个参数是自动传递的就是session_id(),可以通过这个值进行操作。
2.脚本执行结束
执行write($id, $sess_data) //两个参数,很简单。
3.假如用户需要session_destroy(),先执行destroy,在执行第2步。
一个实际例子:
//SESSION初始化的时候调用
function open($save_path, $session_name)
{
global $sess_save_path;
$sess_save_path = $save_path;
return(true);
}
//关闭的时候调用
function close()
{
return(true);
}
function read($id)
{
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id";
return (string) @file_get_contents($sess_file);
}
//脚本执行结束之前,执行写入操作
function write($id, $sess_data)
{
echo "sdfsf";
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id";
if ($fp = @fopen($sess_file, "w")) {
$return = fwrite($fp, $sess_data);
fclose($fp);
return $return;
} else {
return(false);
}
}
function destroy($id)
{
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id";
return(@unlink($sess_file));
}
function gc($maxlifetime)
{
global $sess_save_path;
foreach (glob("$sess_save_path/sess_*") as $filename) {
if (filemtime($filename) + $maxlifetime < time()) {
@unlink($filename);
}
}
return true;
}