PHP Session生存周期相关知识:从创建开始到结束的过程
本文主要分享关于 php session 生存周期的一些相关知识。

首先说一下session的创建的开始到结束的过程。
当程序要为某个客户端的请求创建 session 时,服务器会先检查该客户端是否已包含 session 标识,即我们所说的 session id(获取方法为 session_id())。若已包含 session id,就表明此客户端之前已创建过 session,服务器会按 session id 检索此 session 中的值。若客户端不包含 session id,意味着此客户端是第一次请求服务器或手动清除过缓存文件,那么服务器会为此客户端创建一个 session 并生成一个与此 session 相关联的 session id。通常,session id 的值是不会重复的,它是加密的字符串,且这个 session id 将在本次响应中被返回给客户端保存。
session 在何时被创建 ?
通常是在浏览器向服务器端首次请求时被创建,它会占用一定的内存空间,所以在没有必要的情况下,要尽量关闭 session。
session 何时被删除 ?
通常情况下,session 在会在这几种情况下被删除:
一是通过使用 session_destroy() 这个重置函数来进行手动删除操作;
一是 session 的上次活动时间与当前时间的间隔超出了 session 的超时设置的时间;二是服务器进程处于停止状态。
怎么在浏览器关闭时删除 session ?
理论上无法做到这一点。因为 http 是无状态协议,所以服务器不知道客户端何时关闭浏览器,且 PHP 也没有相关函数来获取此信息。不过,这个问题仍可解决,可通过使用网页特效代码 window.oncolose 来监视浏览器的关闭动作,然后用 Ajax 向服务器端发送请求以删除 session 。但此办法也不能完全解决问题,原因在于在某些情况下,如浏览器崩溃、突然断电、用户死机等,无法作出反应。
如何设置使session在一段时间过后自动失效(删除)?
session_start()标志着 session 机制的起始。它有可能开启垃圾回收。因为 session 存放在文件中,PHP 自身的垃圾回收不起作用,SESSION 的回收(删除)需要删除文件。这个概率由 php.ini 的配置决定。然而,有的系统中 session.gc_probability = 0,这意味着概率为 0,而是通过 cron 脚本来实现垃圾回收(即删除 session)。
PHP 中的 session 有效期通常是 1440 秒,也就是 24 分钟。需要注意的是,在 php5 中默认是 180 分钟。这意味着如果客户端超过 24 分钟没有刷新,当前的 session 就会失效。很显然,这样的设置不能满足需求。
一个管用的方法是,运用 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]”这一部分进行查看。
session.use_cookies 的默认值为“1”,这意味着 SessionID 通过 Cookie 来传递;若不是默认值,即代表使用 Query_String 来传递。
Session.name 是 SessionID 储存的变量名称,它既可能通过 Cookie 传递,也可能通过 Query_String 传递,其默认值是“PHPSESSID”。
Session.cookie_lifetime 代表 SessionID 在客户端 Cookie 中储存的时间。其默认值为 0,这意味着当浏览器关闭时,SessionID 就会作废。正是由于这个原因,Session 不能被永久使用。
Session.gc_maxlifetime 指的是 Session 数据在服务器端储存的时间,倘若超过了这个时间,Session 数据就会自动被删除。
有很多设置,然而与本文相关的就只有这些了。接下来阐述一下如何使用永久 Session 的原理和步骤。
前面提到,服务器借助 SessionID 来读取 Session 的数据。然而,通常情况下,浏览器传送的 SessionID 在浏览器关闭后就不复存在了。这样一来,我们只要人为地设置 SessionID 并将其保存下来,不就可以实现目标了吗?倘若你拥有服务器的操作权限,那么设置这个过程是非常非常简单的,只需按照如下步骤进行操作:
将“session.use_cookies”设定为 1 ,这样就会开启 Cookie 来储存 SessionID 。而默认情况下它就是 1 ,通常是不需要进行修改的。
将“session.cookie_lifetime”修改为 999999999,因为 999999999 和正无穷在实际效果上没有区别。
将“session.gc_maxlifetime”设置为与“session.cookie_lifetime”相同的时间;
PHP 的文档明确表明,设定 session 有效期的参数为 session.gc_maxlifetime。能够在 php.ini 文件里,亦或是借助 ini_set()函数去修改这一参数。然而,经过多次测试后发现,修改这个参数基本上没什么作用,session 有效期依然维持着 24 分钟的默认值。
PHP 的工作机制决定了它没有一个 daemon 线程。这个 daemon 线程原本的作用是定时去扫描 session 信息,并且判断这些 session 信息是否失效。当一个有效请求出现时,PHP 会依据全局变量 session.gc_probability 与 session.gc_divisor 的值(这两个值同样能够通过 php.ini 或者 ini_set()函数进行修改),去决定是否开启一个 GC(垃圾收集器)。
在默认的情况下,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 文件全部删除掉。无论这些 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 信息。
phpinfo();
打开编辑器,接着输入上面的代码,之后在浏览器中运行该程序,就能见到 PHP 的相关信息。在这些信息当中,有一项参数为“session.name”,而这就是我们所需要的服务器“session.name”,通常它是“PHPSESSID”。
我们记下了 SessionID 的名称,之后就能够实现永久的 Session 数据储存了。
session_start();
设置会话保存路径为 /tmp/ ,通过 ini_set 函数来实现 ;将会话保存路径设定为 /tmp/ ,这是通过 ini_set 函数完成的操作 ;利用 ini_set 函数把会话保存路径设置为 /tmp/ 。
//6个钟头
设置 session 的 gc_maxlifetime 为 21600 ;将 session 的 gc_maxlifetime 进行设置为 21600 ;对 session 的 gc_maxlifetime 进行设置,设置的值为 21600 ;把 session 的 gc_maxlifetime 设定为 21600 ;设定 session 的 gc_maxlifetime 为 21600 ;让 session 的 gc_maxlifetime 为 21600 ;使 session 的 gc_maxlifetime 等于 21600
//保存一天
$lifeTime = 24 * 3600;
设置名为 session_name() 的 cookie,其值为 session_id(),过期时间为当前时间加上 $lifeTime,作用域为 "/" 。
另外,关于设置 php session 的生存时间这件事,在网上我们看到有网友写出了一个非常不错的方法,在此,将相关代码分享给大家:
如果设置了过期时间$expire$,则启动会话;如果没有设置过期时间$expire$,则也启动会话。
if($expire==0){
$expire 等于 ini_get('session.gc_maxlifetime');
}else{
设置 session 的 gc_maxlifetime 为 $expire 。
如果($_COOKIE['PHPSESSID']为空){
设置会话的 cookie 参数为 $expire 。
session_start();
}else{
session_start();
设置名为“PHPSESSID”的 cookie,其值为当前的 session_id(),过期时间为当前时间加上$expire。
使用方法也很简单,例如:
后记:实际上真正意义上的永久储存是难以实现的。原因在于 Cookie 的保存时间存在限制,并且服务器的空间也是有限的……然而,对于那些需要较长保存时间的站点来说,上述方法已经能够满足需求了!