你是否需要一个关于用PHP实现实时聊天室的项目案例(结合Ajax)

wen PHP项目 45

本文目录导读:

你是否需要一个关于用PHP实现实时聊天室的项目案例(结合Ajax)

  1. 项目结构
  2. 第一步:数据库设计 (MySQL)
  3. 第二步:核心PHP文件
  4. 第三步:前端代码 (index.php)
  5. 第四步:运行测试
  6. 进阶建议(如果你想挑战一下)

这是一个非常经典且实用的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>

第四步:运行测试

  1. 将上述4个文件放在你的Web服务器目录下(如 xampp/htdocs/chatterbox/)。
  2. 通过phpMyAdmin或命令行执行 schema.sql 创建数据库和表。
  3. 打开浏览器访问 http://localhost/chatterbox/index.php
  4. 打开两个不同的浏览器窗口或标签页(使用不同的用户名),开始聊天吧!

概念 在本项目中的体现
短轮询 前端 setInterval(fetchMessages, 1000) 每秒发一次请求。
增量加载 get_messages.php 根据 last_id 只返回新消息,避免重复加载。
无刷新发送 sendMessage() 使用 XMLHttpRequest 异步发送,页面不刷新。
安全问题 后端使用 htmlspecialchars,前端使用 createTextNode 防止XSS。
数据库 使用 PDO 和预处理语句防止SQL注入。

进阶建议(如果你想挑战一下)

  1. 升级为长轮询(Long Polling):修改 get_messages.php,如果没有新消息,不要立刻返回空数组,而是 sleep(1) 循环等待,直到有消息或超时(如30秒)。
  2. 引入WebSocket:使用 Ratchet (PHP WebSocket库) 实现真正的实时推送。
  3. 使用Node.js:如果项目要求高并发实时性,通常Node.js比PHP更适合,但上述Ajax方案在中小流量下完全够用。

这个项目案例涵盖了Web开发中非常核心的异步交互模式,希望对你有所帮助!

抱歉,评论功能暂时关闭!