session_start()的作用是创建新会话或者重用现有会话。它检查请求中的PHPSESSID,若无则生成新ID,并加载或初始化对应会话存储(如sess_xxx文件),同时反序列化数据填充$_SESSION,且必须在输出任何内容前调用。
调用 session_start() 时,PHP 并不是简单地“打开一个开关”。它会按顺序做三件事:检查请求中是否带有效的 PHPSESSID(默认从 $_COOKIE['PHPSESSID'] 读)、若无则生成新 ID、最后尝试加载对应会话数据文件(或数据库记录/Redis 键)。
关键点在于:这个过程**不依赖 $_SESSION 是否被赋值**。哪怕你只写 session_start() 而不碰 $_SESSION,PHP 仍会读取(或创建)底层存储实体——比如在 session.save_path 下生成一个空的 sess_xxx 文件。
常见错误现象:
session_start() 报 “headers already sent”echo ''),同样触发该错误session.save_path 目录不可写,session_start() 不报错但后续 $_SESSION 写入失败(数据实际没存住)当 session.save_handler = files(PHP 默认),每个会话数据都序列化后存为单独文件,文件名格式固定为 sess_<session_id>,例如 sess_abc123xyz。
立即学习“PHP免费学习笔记(深入)”;
文件存放位置由 session.save_path 决定,但存在两种配置方式:
session.save_path = "/var/lib/php/sessions":所有 sess_* 文件平铺在此目录下(易因文件数过多导致 inode 性能下降)session.save_path = "2;/var/lib/php/sessions":启用两级子目录哈希,ID abc123xyz 会被存到 /var/lib/php/sessions/a/b/sess_abc123xyz(需手动创建 a/b 等子目录,PHP 不自动建)注意:session.gc_probability 和 session.gc_divisor 控制垃圾回收触发概率(如 1/1000),但**仅对平铺模式有效**;分级目录下 GC 机制失效,必须自行定时清理过期文件。
这不是随机故障,而是底层存储生命周期策略叠加的结果:
session.gc_maxlifetime = 1440(默认 24 分钟):服务端会话文件超过此秒数即视为过期,下次 GC 触发时删除session.cookie_lifetime = 0(默认):浏览器关闭即丢弃 Cookie,导致下次访问无法传递 PHPSESSID,服务器只能新建会话session.use_trans_sid = 1:ID 无法传递,每次请求都被当作新用户session.save_path 或未切换为 Redis/DB 存储:用户请求被负载均衡打到不同机器,找不到原会话数据典型表现是登录后刷新就掉线,或同一浏览器开两个标签页,一个登出另一个也失效——本质都是 ID 丢失或存储不一致。
从 files 切到 redis 或 database,不只是改几个配置项的事:
session.serialize_handler 必须保持一致(默认 php),否则旧文件无法被新引擎反序列化sess_id、sess_data、sess_expires),否则 session_write_close() 会静默失败session.save_path = "tcp://127.0.0.1:6379?auth=xxx&prefix=phpsess:",参数间用 &(非 &)转义,否则连接失败且无提示session.name(默认 PHPSESSID)和 session.cookie_domain 必须完全相同,否则 Cookie 无法跨子域共享最常被忽略的是:修改 session.save_handler 后,旧会话文件不会自动迁移。用户首次访问时会新建会话,原有登录态彻底丢失——上线前得清空所有客户端 Cookie 或做灰度过渡。