本文目录导读:

- 方案一:基于 Session 和心跳机制(推荐,最简单)
- 方案二:基于 WebSocket 长连接(实时性最强)
- 方案三:使用 Redis 缓存(高性能方案)
- 方案四:基于 Session 文件的时间戳(简易方法)
- 总结与建议
在 PHP 中判断用户是否在线,通常有几种常见的方法,由于 PHP 是无状态协议(HTTP),服务器无法直接知道用户是否在线,只能通过模拟或推断来实现。
以下是几种主流方案,按推荐程度排序:
基于 Session 和心跳机制(推荐,最简单)
这是最常用的方法,核心思想是:用户每次和服务器交互(心跳)时,更新一个“最后活动时间”,如果在一段时间内没有活动,则认为用户离线。
实现步骤:
- 用户登录:将用户 ID 存入
$_SESSION。 - 记录时间:每次用户访问页面(或前端发送 AJAX 心跳请求)时,更新数据库中的
last_activity字段。 - 判断逻辑:查询数据库,
last_activity与当前时间差小于 5 分钟(或 10 分钟),则判定为“在线”。
代码示例(数据库设计 + PHP 逻辑):
-- 用户表(users)增加字段 ALTER TABLE users ADD COLUMN last_activity INT(11) NOT NULL DEFAULT 0; -- 存储时间戳
<?php
session_start();
// 用户登录后(或每个页面加载时)
if (isset($_SESSION['user_id'])) {
$user_id = $_SESSION['user_id'];
// 更新数据库中的最后活动时间(重要:确保这里使用真实的当前时间戳)
$timestamp = time(); // 获取当前时间戳
// 使用 PDO 或 mysqli 执行
$stmt = $pdo->prepare("UPDATE users SET last_activity = ? WHERE id = ?");
$stmt->execute([$timestamp, $user_id]);
}
// 判断某个用户是否在线(例如显示好友列表时)
function isUserOnline($user_id, $pdo) {
$timeout_duration = 300; // 300秒 = 5分钟超时
$current_time = time();
$stmt = $pdo->prepare("SELECT last_activity FROM users WHERE id = ?");
$stmt->execute([$user_id]);
$row = $stmt->fetch();
if ($row) {
$last_activity = $row['last_activity'];
// 如果最后活动时间在超时范围内,则在线
return ($current_time - $last_activity) < $timeout_duration;
}
return false;
}
// 使用示例
if (isUserOnline($some_user_id, $pdo)) {
echo "该用户在线";
} else {
echo "该用户离线";
}
?>
优点:实现简单,逻辑清晰。 缺点:如果用户直接关闭浏览器(不点击退出),需要等待超时时间(如5分钟)才能显示离线。
基于 WebSocket 长连接(实时性最强)
适用于聊天室、在线协作等需要实时状态的应用,用户连接 WebSocket 成功即视为在线,断开连接即视为离线。
实现步骤:
- 客户端发起 WebSocket 连接。
- 服务端维护一个在线用户列表(内存或 Redis)。
- 连接断开(
onClose)时移除用户。
代码思路(伪代码):
// 使用 Workerman 或 Swoole
use Workerman\Worker;
$worker = new Worker('websocket://0.0.0.0:8080');
$worker->onConnect = function($connection) {
// 可以要求客户端发送认证信息,如 token
};
$worker->onMessage = function($connection, $data) {
// 解析 data 中的 user_id
// 将 user_id => connection_id 存入全局数组或 Redis
};
$worker->onClose = function($connection) {
// 1. 找到该 connection 对应的 user_id
// 2. 从在线列表中删除
};
优点:实时性强,没有时间误差。 缺点:需要搭建常驻内存服务,开发和部署复杂度较高。
使用 Redis 缓存(高性能方案)
利用 Redis 的 SETEX 命令设置有效期,每次刷新页面就重置有效期,Key 存在,则在线。
实现思路:
- 用户登录:
$redis->setex('online:'.$user_id, 300, time());。 - 判断在线:
$redis->exists('online:'.$user_id);。
<?php
// 连接 Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 每次活动时(页面加载或 AJAX 心跳):
$user_id = $_SESSION['user_id'];
$redis->setex("online:".$user_id, 300, '1'); // 设置 5 分钟过期
// 判断是否在线:
if ($redis->exists("online:".$user_id)) {
echo "在线";
} else {
echo "离线";
}
?>
优点:性能极好,逻辑简单,且 Redis 的过期机制自动处理了超时问题,不需要手动计算时间差。 缺点:需要安装 Redis 服务。
基于 Session 文件的时间戳(简易方法)
不推荐直接依赖 $_SESSION 的 last_activity,因为 PHP 垃圾回收机制可能延迟,建议方案一,存入数据库或 Redis。
总结与建议
| 需求场景 | 推荐方案 |
|---|---|
| 初学者/简单项目 | 方案一(Session + 数据库) |
| 高并发/性能要求 | 方案三(Redis) |
| 实时通讯(聊天) | 方案二(WebSocket) |
| 最推荐的生产环境方案 | 方案三(Redis),因为性能好且代码简单 |
核心要点:
- 必须用 “心跳” 来刷新最后活动时间。
- 必须设置 “超时时间”(如 5 分钟)来避免把非活跃用户误判为在线。
- 方案一和三是最常用的,可以根据你的技术栈选择。