本文目录导读:

这是一个非常经典且实用的PHP项目案例,实时聊天室是学习 PHP + Ajax + 数据库 + 长轮询/短轮询 之间协同工作的绝佳场景。
下面我为你提供一个轻量级、易于理解的聊天室实现方案,它基于短轮询(每隔一秒请求一次服务器),这是用纯Ajax实现“实时”感的最简单方式,适合初学者和中高级开发者重构。
项目结构
chatterbox/
├── index.php # 主页面 (HTML + CSS + JS)
├── send_message.php # 处理发送消息 (POST)
├── get_messages.php # 获取最新消息 (GET)
├── db.php # 数据库连接与初始化
└── schema.sql # 数据库建表语句
第一步:数据库设计 (MySQL)
创建一个简单的表来存储消息。
schema.sql
CREATE DATABASE IF NOT EXISTS chat_db;
USE chat_db;
CREATE TABLE IF NOT EXISTS messages (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
第二步:核心PHP文件
db.php (数据库连接)
<?php
$host = 'localhost';
$dbname = 'chat_db';
$username = 'root';
$password = '';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("连接失败: " . $e->getMessage());
}
?>
send_message.php (接收Ajax请求,写入数据库)
<?php
require_once 'db.php';
header('Content-Type: application/json');
// 获取POST数据
$username = trim($_POST['username'] ?? '');
$message = trim($_POST['message'] ?? '');
// 简单验证
if (empty($username) || empty($message)) {
echo json_encode(['success' => false, 'error' => '用户名或消息不能为空']);
exit;
}
// 防止XSS (将在前端进行,但后端也要做)
$username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
$message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
// 插入数据库
$stmt = $pdo->prepare("INSERT INTO messages (username, message) VALUES (?, ?)");
$success = $stmt->execute([$username, $message]);
echo json_encode(['success' => $success]);
?>
get_messages.php (获取新消息,Ajax轮询的目标)
核心逻辑: 它接收一个 last_id 参数(上一次请求拿到的最后一条消息ID),然后只返回ID大于该值的新消息。
<?php
require_once 'db.php';
header('Content-Type: application/json');
// 获取客户端传来的最后一条消息ID,若没有则从0开始
$last_id = isset($_GET['last_id']) ? (int)$_GET['last_id'] : 0;
// 获取比 last_id 更新的消息,限制20条防止数据爆炸
$stmt = $pdo->prepare("SELECT id, username, message, created_at FROM messages WHERE id > ? ORDER BY id ASC LIMIT 20");
$stmt->execute([$last_id]);
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['messages' => $messages, 'last_id' => $last_id]);
?>
第三步:前端代码 (index.php)
这是核心,包含HTML结构、CSS样式和Ajax逻辑。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">PHP Ajax 实时聊天室</title>
<style>
#chat-box {
width: 400px; height: 350px; border: 1px solid #ccc;
overflow-y: auto; padding: 10px; margin-bottom: 10px;
background-color: #f9f9f9;
}
.msg { margin-bottom: 8px; }
.msg .user { font-weight: bold; color: #007bff; }
.msg .time { font-size: 0.8em; color: #888; margin-left: 10px; }
#username-input, #message-input { padding: 5px; width: 100%; box-sizing: border-box; margin-bottom: 5px; }
#send-btn { padding: 7px 20px; background-color: #007bff; color: white; border: none; cursor: pointer; }
#send-btn:hover { background-color: #0056b3; }
</style>
</head>
<body>
<h1>PHP + Ajax 实时聊天</h1>
<div id="chat-box"></div>
<input type="text" id="username-input" placeholder="输入你的名字" value="User<?php echo rand(100,999); ?>" />
<input type="text" id="message-input" placeholder="输入消息..." onkeydown="if(event.key === 'Enter') sendMessage();" />
<button id="send-btn" onclick="sendMessage()">发送</button>
<script>
let lastMessageId = 0; // 记录最后一条消息ID
const chatBox = document.getElementById('chat-box');
// ---------- 1. 发送消息 (Ajax POST) ----------
function sendMessage() {
const username = document.getElementById('username-input').value.trim();
const message = document.getElementById('message-input').value.trim();
if (!message || !username) {
alert('用户名和消息不能为空');
return;
}
const xhr = new XMLHttpRequest();
xhr.open('POST', 'send_message.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
if (response.success) {
document.getElementById('message-input').value = ''; // 清空输入框
// 立即请求新消息,加速显示
fetchMessages();
} else {
alert('发送失败: ' + response.error);
}
}
};
xhr.send(`username=${encodeURIComponent(username)}&message=${encodeURIComponent(message)}`);
}
// ---------- 2. 获取新消息 (Ajax GET 轮询) ----------
function fetchMessages() {
const xhr = new XMLHttpRequest();
// 关键:把 lastMessageId 传给服务器
xhr.open('GET', `get_messages.php?last_id=${lastMessageId}`, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
if (response.messages && response.messages.length > 0) {
// 更新last_id
const lastMsg = response.messages[response.messages.length - 1];
lastMessageId = lastMsg.id;
// 渲染新消息
for (const msg of response.messages) {
appendMessage(msg);
}
}
}
};
xhr.send();
}
// ---------- 3. 将消息追加到聊天框 ----------
function appendMessage(msg) {
const div = document.createElement('div');
div.className = 'msg';
div.innerHTML = `
<span class="user">${escapeHtml(msg.username)}</span>
<span class="time">${msg.created_at}</span>
<br/>
<span class="content">${escapeHtml(msg.message)}</span>
`;
chatBox.appendChild(div);
// 自动滚动到底部
chatBox.scrollTop = chatBox.scrollHeight;
}
// ---------- 4. 简单的XSS防护 (前端) ----------
function escapeHtml(text) {
const div = document.createElement('div');
div.appendChild(document.createTextNode(text));
return div.innerHTML;
}
// ---------- 5. 启动轮询 (每1秒请求一次) ----------
// 页面加载后立即请求一次,并开始定时器
setInterval(fetchMessages, 1000); // 1秒一次
// 可选:页面加载时立即拉取历史消息(last_id=0)
window.onload = function() {
fetchMessages();
};
</script>
</body>
</html>
第四步:运行测试
- 将上述4个文件放在你的Web服务器目录下(如
xampp/htdocs/chatterbox/)。 - 通过phpMyAdmin或命令行执行
schema.sql创建数据库和表。 - 打开浏览器访问
http://localhost/chatterbox/index.php。 - 打开两个不同的浏览器窗口或标签页(使用不同的用户名),开始聊天吧!
| 概念 | 在本项目中的体现 |
|---|---|
| 短轮询 | 前端 setInterval(fetchMessages, 1000) 每秒发一次请求。 |
| 增量加载 | get_messages.php 根据 last_id 只返回新消息,避免重复加载。 |
| 无刷新发送 | sendMessage() 使用 XMLHttpRequest 异步发送,页面不刷新。 |
| 安全问题 | 后端使用 htmlspecialchars,前端使用 createTextNode 防止XSS。 |
| 数据库 | 使用 PDO 和预处理语句防止SQL注入。 |
进阶建议(如果你想挑战一下)
- 升级为长轮询(Long Polling):修改
get_messages.php,如果没有新消息,不要立刻返回空数组,而是sleep(1)循环等待,直到有消息或超时(如30秒)。 - 引入WebSocket:使用
Ratchet(PHP WebSocket库) 实现真正的实时推送。 - 使用Node.js:如果项目要求高并发实时性,通常Node.js比PHP更适合,但上述Ajax方案在中小流量下完全够用。
这个项目案例涵盖了Web开发中非常核心的异步交互模式,希望对你有所帮助!