PHP会话(Session)是一种服务端状态保持技术,它允许网站在用户连续访问不同页面时记住其身份与临时数据。与Cookie将信息保存在浏览器端不同,Session把核心数据放在服务器,只向客户端发送一个随机生成的会话标识(通常叫PHPSESSID)。当浏览器带着这个标识再次请求时,PHP就能根据标识找回之前保存的数据。

一、Session的基本工作原理
HTTP协议本身是无状态的,每一次请求对于服务器来说都是陌生的。如果没有额外机制,用户登录后跳转到个人中心页面,服务器根本不知道这个请求来自谁。Session的出现就是为了解决这个问题:当用户第一次访问站点时,PHP调用session_start会检查请求里有没有会话ID,没有就新建一个,并同时通过响应头Set-Cookie把ID交给浏览器。
之后浏览器每次请求都会自动带上这个Cookie,PHP收到后用ID去服务端存储里(默认是文件)找到对应的数据数组,还原成$_SESSION供脚本使用。由于真正敏感的数据没有离开服务器,即使Cookie被截获,攻击者也只是拿到一个编号,无法直接看到内容,这比纯Cookie方案安全不少。
二、如何开启和使用Session
在任意PHP脚本输出内容之前,都必须先调用session_start函数,否则会话无法初始化,也会报头已发送的错误。开启之后,就可以像操作普通数组一样读写$_SESSION。下面是一段最基础的用法示例,演示了记录用户名与读取用户名。
<?php // 必须在输出任何HTML之前开启会话 session_start(); // 写入会话数据 $_SESSION['username'] = 'zhangsan'; $_SESSION['uid'] = 1001; // 读取会话数据 echo '当前登录用户:' . $_SESSION['username']; ?>
需要注意的是,session_start不仅启动会话,还会自动发送Cookie头。因此如果你的代码里先用了echo或者HTML标签,再调用它就会失败。实际项目中通常把session_start放在统一入口文件的最顶部,避免遗漏。
另外,$_SESSION里的键名可以是字符串或数字,值支持字符串、数组、对象(对象需可序列化)等类型。但不要在会话里塞太大的数据,比如几兆的文件内容,这会让每次请求的反序列化都变慢,也占用服务端存储。
三、会话的销毁与退出登录
用户退出登录时,不能只清空$_SESSION数组,因为服务端的会话文件还可能存在,且Cookie也未失效。标准的做法是先清空数组,再调用session_destroy删除服务端文件,并可选地清除客户端的会话Cookie。
<?php
session_start();
// 清空所有会话变量
$_SESSION = array();
// 如果使用了Cookie,顺手删掉客户端的会话ID
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params['path'], $params['domain'],
$params['secure'], $params['httponly']
);
}
// 销毁服务端会话
session_destroy();
echo '您已安全退出';
?>
上面的代码覆盖了大多数注销场景。只写$_SESSION = array()虽然能让当前页面读不到数据,但文件还在,若Cookie没清,用户刷新后又可能恢复,因此destroy这一步不能省。
有些系统还会在用户修改密码或权限变更后调用session_regenerate_id(true),强制换一个新的会话ID并删除旧的,这样能防止会话固定攻击。这个习惯在涉及资金或管理的系统中非常推荐。
四、常见配置与安全防护
php.ini里有一组session相关配置,直接影响行为与安全。例如session.gc_maxlifetime设定了空闲会话多久后被回收,默认是1440秒。session.cookie_httponly设为On可禁止JS读取Cookie,降低XSS偷ID的风险;session.cookie_secure设为On则只允许HTTPS传输。
| 配置项 | 作用 | 建议值 |
|---|---|---|
| session.gc_maxlifetime | 会话文件闲置存活秒数 | 1800 |
| session.cookie_httponly | 禁止脚本访问Cookie | 1(On) |
| session.cookie_secure | 仅HTTPS下发Cookie | 1(On,需全站HTTPS) |
| session.use_strict_mode | 拒绝未初始化ID | 1(On) |
除了配置,开发者还要注意不要在公网明文传输会话ID,也不要把敏感权限长期放在会话里不校验。对于分布式系统,默认的文件存储会导致多台机器间会话不共享,此时可以把save_handler改成redis或数据库,使所有节点访问同一份会话数据。
当站点访问量变大,文件存储的Session会因大量小文件造成IO压力,而Redis这类内存存储不仅速度快,还天然支持过期淘汰,是生产环境更稳的选择。切换方式一般是在php.ini设置session.save_handler=redis和session.save_path=tcp://127.0.0.1:6379,代码层面无需改动。
五、一个小陷阱:session_start重复调用
有些新手会把session_start写进每个被包含的文件,导致同一个请求里多次调用。实际上第二次调用会产生一条警告,说会话已经启动。正确做法是在入口统一开一次,被include的库文件里用session_status判断,避免重复。
<?php
// 仅当会话未开启时才启动
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
?>
这段判断能兼容多数框架和旧代码。如果项目使用了Composer autoload或者MVC骨架,通常框架已经封装了Session类,直接调用对应方法即可,不必自己到处写session_start。
掌握以上几点,你就能够用PHP完成登录保持、购物车临时存储、表单令牌校验等典型会话管理任务。重点记住:早开、轻写、退出要清、配置要收紧,Session就会成为可靠而不是漏洞的来源。