为什么你的 PHP 会话存活时间比需要的更长?
\n\n你是否曾想过,为什么在 30 分钟无操作后,你的网站仍然记得你已经登录?或者相反,用户抱怨他们被系统“踢出”得太早了?在 PHP 中管理会话生命周期的问题,是初学者(以及不仅仅是初学者)开发者最常见的头痛问题之一。问题 "How do I expire a PHP session after 30 minutes?" 在 Stack Overflow 上获得了数千次浏览,这并非偶然。
\n\n让我们弄清楚如何让 PHP 会话正好在 30 分钟后失效。我们将探讨三种方法:从简单(但狡猾)到专业。你将了解为什么 session.gc_maxlifetime 常常不像你预期的那样工作,以及如何编写不会让你失望的可靠代码。
问题:什么是真正的“会话生命周期”?
\n\n在深入代码之前,让我们先了解基础知识。PHP 会话不仅仅是一个在浏览器中滴答作响的计时器。它是服务器上的一个文件(通常在 /tmp/ 中),用于存储用户数据。“过期”会话意味着该文件被删除或变得无效。
有三个关键因素影响生命周期:
\n\n- \n
- 会话 Cookie: 浏览器存储会话 ID(通常在 cookie
PHPSESSID中)。这个 cookie 有自己的生命周期。 \n - 服务器上的会话文件: 它有最后修改时间(mtime)。 \n
- 垃圾回收器 (GC): PHP 会定期清理旧的会话文件,但这不是按计划进行的,而是随机进行的。 \n
正是由于这种“三层”架构,导致了混乱。只设置一个参数通常不会产生预期的效果。
\n\n方法 1:天真的方法——仅使用 session.gc_maxlifetime
\n\n文档建议的最明显方法是设置 session.gc_maxlifetime。这似乎是问题 "How do I expire a PHP session after 30 minutes?" 的答案。但事情没那么简单。
<?php\n// 开始会话\nsession_start();\n\n// 设置会话生命周期(秒):30 分钟 = 1800 秒\nini_set('session.gc_maxlifetime', 1800);\n\n// 将会话 cookie 的生命周期也设置为 30 分钟\nsession_set_cookie_params(1800);\n\n// 就这样?不。\n?>\n\n为什么这可能不起作用?
\n- \n
- 垃圾回收器 (GC) 很懒。 它以
session.gc_probability/session.gc_divisor(默认 1/100)的概率运行。也就是说,即使会话文件已经存在一小时,如果 GC 没有运行,它也可能不会在下一次请求时被删除。 \n - 其他应用程序。 如果服务器上运行着多个网站,它们可能会清理彼此的会话,或者相反,不清理你的会话。 \n
- 缓存。 在某些主机(尤其是共享主机)上,
ini_set设置可能会被忽略。 \n
结论: 这种方法是基础,但不是万能的。它只在理想条件下有效。对于生产环境,需要更深入的研究。
\n\n方法 2:会话内的手动计时器(推荐)
\n\n这种方法是一个折中方案。我们不是依赖垃圾回收器,而是自己控制用户最后活动的时间。这是 Stack Overflow 上关于 PHP 会话 30 分钟后过期 主题最流行的答案。
\n\n思路很简单:在每次请求时,我们检查用户上次活动的时间。如果超过 30 分钟,则销毁会话。
\n\n<?php\nsession_start();\n\n// 会话生命周期(秒)\n$session_lifetime = 1800; // 30 分钟\n\n// 检查是否设置了最后活动时间戳\nif (isset($_SESSION['LAST_ACTIVITY'])) {\n // 计算与当前时间的差值