首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 留言板系統完整教學 (適合初學者)

     編輯:PHP 相關知識

PHP MySQL 留言板系統從零建立

本教學專為 PHP 初學者設計,逐步建立一個功能完整的留言板系統,包含:資料庫設計、留言列表顯示、新增留言、編輯留言、刪除留言、分頁、基本安全防護。程式碼採用程序式寫法,易於理解與修改。

系統功能架構

檔案功能對應操作
common.php資料庫連線共用檔PDO 連線、工具函數
index.php留言列表首頁READ (分頁、倒序)
add.php新增留言表單/處理CREATE
edit.php編輯留言表單/處理UPDATE
delete.php刪除留言處理DELETE
style.css樣式表基本排版美化

1. 資料庫設計

CREATE DATABASE `guestbook` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE `guestbook`;

CREATE TABLE `gb_content` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(50) NOT NULL COMMENT '留言者姓名',
  `email` VARCHAR(100) DEFAULT NULL COMMENT 'Email (可選)',
  `content` TEXT NOT NULL COMMENT '留言內容',
  `ip` VARCHAR(45) DEFAULT NULL COMMENT '留言者 IP',
  `user_agent` VARCHAR(255) DEFAULT NULL COMMENT '瀏覽器資訊',
  `insert_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '留言時間',
  `update_time` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '最後編輯時間',
  `is_deleted` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '軟刪除標記',
  PRIMARY KEY (`id`),
  KEY `idx_insert_time` (`insert_time`),
  KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='留言板內容表';

2. 共用連線檔 (common.php) - 現代化 PDO 版

 PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES " . DB_CHARSET,
        ];
        try {
            $pdo = new PDO($dsn, DB_USER, DB_PASS, $options);
        } catch (PDOException $e) {
            // 正式環境請記錄日誌而非直接輸出
            die("資料庫連線失敗: " . $e->getMessage());
        }
    }
    return $pdo;
}

// 安全輸出函數
function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

// 重導向函數
function redirect($url, $msg = '') {
    if ($msg) {
        session_start();
        $_SESSION['flash_msg'] = $msg;
    }
    header("Location: $url");
    exit;
}

// 取得閃存訊息
function getFlash() {
    session_start();
    $msg = $_SESSION['flash_msg'] ?? '';
    unset($_SESSION['flash_msg']);
    return $msg;
}

// 簡易 CSRF Token
function csrfToken() {
    session_start();
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function verifyCsrf($token) {
    session_start();
    return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}

3. 留言列表 (index.php)

query("SELECT COUNT(*) FROM gb_content WHERE is_deleted = 0");
$total = $stmt->fetchColumn();
$totalPages = ceil($total / $perPage);

// 取得當頁資料
$stmt = $pdo->prepare("
    SELECT id, username, content, insert_time 
    FROM gb_content 
    WHERE is_deleted = 0 
    ORDER BY insert_time DESC 
    LIMIT :offset, :limit
");
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->execute();
$messages = $stmt->fetchAll();

$flash = getFlash();
?>



    
    留言板系統
    


留言板

發表留言

目前沒有留言,快來搶沙發!

4. 新增留言 (add.php)

 50) {
        $errors[] = '姓名長度需 1-50 字元';
    }
    if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = 'Email 格式不正確';
    }
    if (mb_strlen($content) < 1 || mb_strlen($content) > 2000) {
        $errors[] = '留言內容長度需 1-2000 字元';
    }
    
    if (empty($errors)) {
        $stmt = $pdo->prepare("
            INSERT INTO gb_content (username, email, content, ip, user_agent) 
            VALUES (?, ?, ?, ?, ?)
        ");
        $stmt->execute([$username, $email, $content, $ip, $ua]);
        redirect('index.php', '留言發表成功!');
    }
}
?>



    
    發表留言
    


發表留言

返回列表

    " . h($e) . ""; ?>
最多 2000 字元

5. 編輯留言 (edit.php)

prepare("SELECT * FROM gb_content WHERE id = ? AND is_deleted = 0");
$stmt->execute([$id]);
$msg = $stmt->fetch();

if (!$msg) redirect('index.php', '留言不存在或已刪除');

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!verifyCsrf($_POST['csrf_token'] ?? '')) die('CSRF 驗證失敗');
    
    $username = trim($_POST['username'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $content = trim($_POST['content'] ?? '');
    
    $errors = [];
    if (mb_strlen($username) < 1 || mb_strlen($username) > 50) $errors[] = '姓名長度需 1-50 字元';
    if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) $errors[] = 'Email 格式不正確';
    if (mb_strlen($content) < 1 || mb_strlen($content) > 2000) $errors[] = '留言內容長度需 1-2000 字元';
    
    if (empty($errors)) {
        $stmt = $pdo->prepare("UPDATE gb_content SET username=?, email=?, content=? WHERE id=?");
        $stmt->execute([$username, $email, $content, $id]);
        redirect('index.php', '留言更新成功!');
    }
}
?>



    
    編輯留言
    


編輯留言

返回列表

    " . h($e) . ""; ?>

6. 刪除留言 (delete.php)

prepare("SELECT username, content FROM gb_content WHERE id = ? AND is_deleted = 0");
    $stmt->execute([$id]);
    $msg = $stmt->fetch();
    
    if (!$msg) redirect('index.php', '留言不存在');
    
    // 顯示確認頁面
    echo "確認刪除
"; echo "

確認刪除

"; echo "

確定要刪除以下留言嗎?此操作可透過資料庫手動復原。

"; echo "
" . h($msg['username']) . "
" . nl2br(h($msg['content'])) . "
"; echo "
"; echo " "; echo "取消
"; exit; } // POST 執行軟刪除 $stmt = $pdo->prepare("UPDATE gb_content SET is_deleted = 1 WHERE id = ?"); $stmt->execute([$id]); if ($stmt->rowCount() > 0) { redirect('index.php', '留言已刪除'); } else { redirect('index.php', '留言不存在或已刪除'); }

7. 基本樣式 (style.css)

/* style.css */
* { box-sizing: border-box; }
body { font-family: 'Microsoft JhengHei', sans-serif; line-height: 1.6; color: #333; background: #f5f5f5; margin: 0; padding: 20px; }
.container { max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { margin-top: 0; color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
.btn { display: inline-block; padding: 8px 16px; border-radius: 4px; text-decoration: none; font-size: 14px; cursor: pointer; border: none; margin-right: 5px; }
.btn.primary { background: #3498db; color: white; }
.btn.danger { background: #e74c3c; color: white; }
.btn:hover { opacity: 0.9; }
.form-group { margin-bottom: 20px; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
.required { color: #e74c3c; }
input[type=text], input[type=email], textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-family: inherit; font-size: 14px; }
textarea { resize: vertical; }
small { color: #999; font-size: 12px; }
.message { border: 1px solid #eee; border-radius: 4px; padding: 20px; margin-bottom: 20px; background: #fafafa; }
.message-header { margin-bottom: 10px; color: #666; font-size: 14px; }
.message-header strong { color: #2c3e50; font-size: 16px; margin-right: 15px; }
.message-content { white-space: pre-wrap; word-wrap: break-word; }
.message-actions a { margin-right: 15px; color: #3498db; text-decoration: none; }
.message-actions a:hover { text-decoration: underline; }
.pagination { text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; }
.pagination a { padding: 8px 16px; background: #3498db; color: white; border-radius: 4px; text-decoration: none; margin: 0 5px; }
.pagination .page-info { color: #999; margin: 0 15px; }
.alert { padding: 15px; border-radius: 4px; margin-bottom: 20px; }
.alert.success { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; }
.alert.error { background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; }
.empty { text-align: center; color: #999; padding: 40px; }

安全性檢查清單

  • ✅ 使用 PDO Prepared Statements 防止 SQL 注入
  • ✅ 輸出時使用 htmlspecialchars() 防止 XSS
  • ✅ 表單加入 CSRF Token 驗證
  • ✅ 採用軟刪除 (is_deleted) 而非硬刪除
  • ✅ 輸入長度驗證 (前後端雙重)
  • ✅ Email 格式驗證
  • ✅ 記錄留言者 IP 與 User-Agent 供審計
  • ⚠️ 正式環境建議加入:限流、內容過濾、reCAPTCHA、HTTPS 強制

擴充功能建議

  1. 後台管理介面 (審核、批次刪除、匯出)
  2. 留言回覆/巢狀回覆功能
  3. 會員系統整合 (登入才能留言)
  4. 圖片上傳支援
  5. RSS/Atom 訂閱
  6. 搜尋功能 (全文檢索)
  7. 匯出 CSV/Excel 功能
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved