尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

PHP留言板源码实战:数据库增删改查与安全避坑指南

PHP留言板源码实战:数据库增删改查与安全避坑指南 简介这是一套面向PHP与MySQL入门者的留言板完整源码适合刚接触Web开发、想通过一个可运行的小项目理解服务端脚本与数据库协作的读者。包内共131个文件以49个PHP脚本为核心配合16个CSS、16个JS及bootstrap相关样式脚本搭建前端界面另有woff2、ttf、svg等字体图标资源并附带3个SQL数据库文件压缩包约746KB结构紧凑便于本地部署。源码覆盖用户注册登录、留言发布与列表展示等基础互动流程涉及数据库连接配置、表单提交与数据验证、session登录状态管理、mysqli或PDO增删改查操作以及SQL注入与XSS等安全防护思路同时包含错误处理与简单性能优化实践。已有1670人学习下载可作为课程设计、毕业设计或自学练手素材帮助读者快速跑通一套带数据库的留言板系统并对照代码理解各模块职责与调用关系。1. 从一份 PHP 留言板源码说起为什么它仍是练手数据库增删改查的好材料如果你正在找一个能跑通「表单提交 → 数据入库 → 列表渲染 → 后台管理」完整闭环的 PHP 练手项目这份 PHP 留言板源码含数据库文件 MySQL 大概率能满足你。它不是什么框架级工程而是一套原生 PHP MySQL 的经典结构前端几个表单页后端几个处理脚本配一个.sql数据库文件导入就能看到留言列表。对刚接触 PHP 接口数组对象、mysql update语法、数据库增删改查的人来说这种小项目比看视频教程更直接——因为你能改一行代码就立刻看到页面变化。它适合三类人想复习原生 PHP 的在校生、需要快速搭一个内部留言/反馈页的开发者、以及准备数据库课程设计但不想从零写前端的人。下面我按「先跑起来、再拆结构、最后避坑」的顺序把这份源码拆开讲。2. 把源码跑起来环境、导入与第一次访问2.1 环境选型为什么推荐 phpStudy 或 XAMPP 而不是单独装原生 PHP 项目最怕环境版本对不上。这份留言板源码通常按 PHP 5.67.x 写MySQL 用 5.7 左右最稳。如果你单独装 PHP MySQL Apache配置php.ini和my.ini的时间可能比写代码还长。常见做法是用集成环境phpStudyWindows或 XAMPP跨平台一键启动 Apache 和 MySQL把源码丢进www或htdocs目录即可。Linux 用户可以用apt install php mysql-server或宝塔面板但要注意 PHP 8 对老代码的兼容性——很多老留言板用了mysql_*函数PHP 7 以后已移除跑之前先确认源码用的是mysqli还是PDO。判断方法很简单打开源码里的数据库连接文件一般叫conn.php、db.php或config.php看它用的是哪个扩展。如果是mysql_connect()那这套代码在 PHP 7 上会直接报致命错误需要手动替换成mysqli_connect()或 PDO。这一步不做后面全是白费。2.2 导入数据库文件三步确认表结构源码包里一般有一个.sql文件比如message_board.sql或db.sql。导入方式有两种命令行和图形工具。命令行适合服务器环境图形工具适合本地调试。# 登录 MySQL本地默认 root密码按你设置的来 mysql -u root -p # 创建数据库字符集用 utf8mb4 避免中文乱码 CREATE DATABASE message_board DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 切换到该数据库 USE message_board; # 导入 sql 文件路径按实际改 SOURCE /path/to/message_board.sql; # 查看导入了哪些表 SHOW TABLES;逻辑说明先建库再导入是为了避免.sql文件里没有CREATE DATABASE语句时导入失败。字符集选utf8mb4而不是utf8是因为前者支持 emoji 和更多生僻字留言内容里出现特殊符号不会截断。SOURCE命令在 MySQL 客户端里执行路径用绝对路径最稳。参数说明DEFAULT CHARACTER SET决定库的默认字符集建表时如果没单独指定就继承它。COLLATE影响排序和比较规则utf8mb4_general_ci不区分大小写适合留言板这种场景。导入后执行DESC 表名;看字段。典型留言板至少有两张表一张存留言id、username、content、created_at一张存管理员或用户id、username、password。如果只有一张表说明管理员账号可能写死在代码里这种设计后面改密码会很麻烦。2.3 改配置文件并访问首页数据库导入成功后找到连接配置文件把库名、用户名、密码改成你本地的。?php // conn.php 典型写法mysqli 版本 $host 127.0.0.1; // 数据库地址本地一般用 127.0.0.1 或 localhost $user root; // 数据库用户名 $pass your_password; // 数据库密码按实际改 $dbname message_board; // 刚才创建的库名 $port 3306; // MySQL 默认端口 $conn new mysqli($host, $user, $pass, $dbname, $port); // 检查连接失败时直接终止并输出错误 if ($conn-connect_error) { die(连接失败: . $conn-connect_error); } // 设置字符集必须和数据库一致 $conn-set_charset(utf8mb4);逻辑说明new mysqli()是面向对象写法比过程式更清晰。connect_error是 mysqli 的属性连接失败时返回错误描述。set_charset()必须在查询前调用否则中文可能变成问号。参数说明$host用127.0.0.1比localhost更明确因为localhost在某些系统上会走 socket 而不是 TCP。$port默认 3306如果你改过 MySQL 端口要同步改。改完后浏览器访问http://localhost/你的目录/index.php。如果看到留言列表或提交表单说明环境通了。如果报「连接失败」先检查 MySQL 服务是否启动再检查用户名密码最后看端口是否被占用。3. 拆解核心逻辑留言提交、列表渲染与后台管理3.1 留言提交从表单到 INSERT 的完整链路留言板最核心的动作是提交。前端是一个form后端接收$_POST然后执行INSERT。这份源码里通常有一个add.php或save.php。?php // add.php接收表单并写入数据库 require_once conn.php; // 接收表单字段trim 去掉首尾空格 $username trim($_POST[username] ?? ); $content trim($_POST[content] ?? ); // 基础校验不能为空长度限制 if ($username || $content ) { die(用户名和内容不能为空); } if (mb_strlen($username) 20 || mb_strlen($content) 500) { die(用户名不超过20字内容不超过500字); } // 预处理语句防止 SQL 注入 $stmt $conn-prepare(INSERT INTO messages (username, content, created_at) VALUES (?, ?, NOW())); $stmt-bind_param(ss, $username, $content); if ($stmt-execute()) { // 插入成功跳回列表页 header(Location: index.php); exit; } else { echo 写入失败: . $stmt-error; } $stmt-close(); $conn-close();逻辑说明?? 是 PHP 7 的空合并运算符避免字段不存在时报 notice。trim()去掉用户误输入的空格。prepare()bind_param()是防 SQL 注入的标准做法ss表示两个参数都是字符串。NOW()是 MySQL 函数写入当前时间比 PHP 的date()更省事。参数说明bind_param的第一个参数是类型字符串s字符串、i整数、d浮点、b二进制。字段顺序要和 SQL 里的?一一对应。header(Location: ...)前不能有任何输出否则会报「headers already sent」。常见翻车点有些老源码直接拼接 SQL比如INSERT INTO messages VALUES ($username, $content)这种写法遇到单引号就崩还容易被注入。如果你拿到的源码是这样建议手动改成预处理。3.2 列表渲染SELECT 与分页的基本写法列表页负责把数据库里的留言读出来循环输出。简单版本直接SELECT * FROM messages ORDER BY id DESC但留言多了要分页。?php // index.php读取留言并分页 require_once conn.php; $page isset($_GET[page]) ? max(1, intval($_GET[page])) : 1; $pageSize 10; // 每页显示条数 $offset ($page - 1) * $pageSize; // 先查总数用于计算总页数 $totalResult $conn-query(SELECT COUNT(*) AS total FROM messages); $totalRow $totalResult-fetch_assoc(); $total $totalRow[total]; $totalPages ceil($total / $pageSize); // 再查当前页数据LIMIT 后面必须用整数已通过 intval 处理 $sql SELECT id, username, content, created_at FROM messages ORDER BY id DESC LIMIT $offset, $pageSize; $result $conn-query($sql); if ($result $result-num_rows 0) { while ($row $result-fetch_assoc()) { // 输出时用 htmlspecialchars 防止 XSS echo div classmsg; echo strong . htmlspecialchars($row[username]) . /strong; echo p . nl2br(htmlspecialchars($row[content])) . /p; echo small . $row[created_at] . /small; echo /div; } } else { echo 暂无留言; }逻辑说明intval()把 page 强制转成整数避免LIMIT被注入。COUNT(*)查总数ceil()向上取整算总页数。htmlspecialchars()把等字符转义防止用户提交脚本标签。nl2br()把换行符转成br保留留言格式。参数说明LIMIT $offset, $pageSize中第一个是偏移量第二个是条数。第 1 页 offset 为 0第 2 页为 10以此类推。如果数据量很大COUNT(*)会变慢可以缓存总数或用近似值但留言板场景一般不用考虑。3.3 后台管理登录、删除与权限判断后台一般有登录页和删除功能。登录用SELECT比对密码删除用DELETE。这里最容易出问题的是权限判断——有些源码只在前端隐藏按钮后端没校验直接访问删除链接就能删。?php // admin_delete.php删除留言必须校验登录状态 session_start(); // 没有 session 直接拒绝防止绕过前端 if (!isset($_SESSION[admin_id])) { die(无权操作); } require_once conn.php; $id isset($_GET[id]) ? intval($_GET[id]) : 0; if ($id 0) { die(参数错误); } // 预处理删除 $stmt $conn-prepare(DELETE FROM messages WHERE id ?); $stmt-bind_param(i, $id); if ($stmt-execute()) { header(Location: admin.php); exit; } else { echo 删除失败: . $stmt-error; } $stmt-close(); $conn-close();逻辑说明session_start()必须在任何输出前调用。isset($_SESSION[admin_id])判断是否登录这是后端最后一道防线。intval()把 id 转成整数防止?id1 OR 11这种注入。参数说明bind_param(i, $id)中i表示整数。删除操作建议用 POST 而不是 GET但老源码常用 GET改起来涉及前端按实际情况决定。登录逻辑类似查用户表用password_verify()比对哈希密码。如果源码里密码是明文存储建议改成password_hash()加密这是血泪经验——明文密码一旦库被拖走所有账号直接暴露。4. 避坑与排查这份源码最容易翻车的五个地方4.1 中文乱码现象是留言变问号原因是字符集不统一现象提交中文留言后列表显示???或乱码。原因数据库、表、连接三处字符集不一致。常见情况是库用utf8连接没设set_charset或者 HTML 头没声明UTF-8。解决建库时用utf8mb4连接后立即$conn-set_charset(utf8mb4)HTML 里加meta charsetutf-8。三处对齐后基本不会再乱。4.2 SQL 注入现象是单引号导致报错或数据被删现象留言内容里带单引号提交后页面报 SQL 语法错误或者有人用 OR 11绕过登录。原因源码直接拼接 SQL 字符串。解决把所有涉及用户输入的查询改成prepare()bind_param()。如果源码里mysql_*函数太多建议整体替换成 mysqli 或 PDO工作量不大但安全性提升明显。4.3 PHP 版本不兼容现象是白屏或 fatal error现象部署后页面空白日志里写Call to undefined function mysql_connect()。原因PHP 7 移除了mysql_*扩展而源码是 PHP 5 时代写的。解决确认源码用的数据库扩展如果是mysql_*要么降级到 PHP 5.6不推荐要么手动改成mysqli_*。改的时候注意函数名和参数顺序变化比如mysql_query($sql)变成$conn-query($sql)。4.4 删除链接无权限校验现象是未登录也能删现象不登录后台直接访问admin_delete.php?id1就能删除留言。原因后端没判断 session只靠前端隐藏按钮。解决在每个管理操作脚本开头加session_start()和isset($_SESSION[admin_id])判断不通过直接die。这是最容易被忽略的安全漏洞课程设计里也常被扣分。4.5 分页参数被篡改现象是页面报错或显示异常现象把 URL 里的page改成abc或-1页面报 SQL 错误或显示空。原因LIMIT后面直接拼接了未过滤的参数。解决用intval()强制转整数并用max(1, ...)保证最小为 1。LIMIT的参数不能用预处理占位符某些 MySQL 版本不支持所以整数过滤是必须的。5. 进阶用法把留言板改成带验证码和敏感词过滤的版本跑通基础版之后可以在这份源码上做两个实用增强加验证码和敏感词过滤。验证码防止机器人刷留言敏感词过滤避免出现不合适内容。这两个功能都不复杂但能让项目从「练手」变成「能用」。先看验证码。常见做法是用 PHP 的 GD 库生成图片把随机码存 session提交时比对。?php // captcha.php生成验证码图片 session_start(); // 生成 4 位随机数字 $code ; for ($i 0; $i 4; $i) { $code . rand(0, 9); } $_SESSION[captcha] $code; // 创建画布 $width 100; $height 40; $image imagecreatetruecolor($width, $height); // 背景色和文字色 $bg imagecolorallocate($image, 240, 240, 240); $textColor imagecolorallocate($image, 50, 50, 50); imagefill($image, 0, 0, $bg); // 写入文字位置稍微随机化增加识别难度 imagestring($image, 5, 30, 10, $code, $textColor); // 输出图片 header(Content-Type: image/png); imagepng($image); imagedestroy($image);逻辑说明session_start()后把验证码存进$_SESSION[captcha]提交页用img srccaptcha.php显示。imagecreatetruecolor()创建真彩色画布imagestring()写文字。header(Content-Type: image/png)告诉浏览器这是图片而不是 HTML。参数说明imagestring的字体大小用 155 最大。位置参数可以加随机偏移防止 OCR 识别。生产环境建议加干扰线和噪点但练手够用。提交时比对?php // add.php 里增加验证码校验 session_start(); $inputCode trim($_POST[captcha] ?? ); if ($inputCode || $inputCode ! ($_SESSION[captcha] ?? )) { die(验证码错误); } // 验证后立即销毁防止重复使用 unset($_SESSION[captcha]);敏感词过滤更简单维护一个数组提交前替换或拒绝。?php // 敏感词过滤函数 function filterBadWords($content) { $badWords [敏感词1, 敏感词2, 广告词]; // 按实际维护 foreach ($badWords as $word) { if (mb_strpos($content, $word) ! false) { return false; // 命中直接拒绝 } } return true; } // 在 add.php 校验里调用 if (!filterBadWords($content)) { die(内容包含不允许的词汇); }逻辑说明mb_strpos()是支持中文的查找函数返回位置或 false。用! false判断是因为位置 0 也是有效值。命中后直接拒绝比替换成***更简单也避免绕过。参数说明敏感词数组可以放数据库里动态维护但练手阶段写死在文件里就够。注意不要用strpos()它对中文支持不好。验证方法提交一条带敏感词的留言看是否被拦截刷新验证码图片看 session 是否更新用错误验证码提交看是否拒绝。三个都通过说明增强生效。从那以后我每次拿到一份老 PHP 源码都强制先做三件事确认 PHP 版本和数据库扩展、检查所有用户输入是否走预处理、验证后台操作是否有 session 校验。这三步走完后面改功能才踏实。希望帮到你。本文还有配套的精品资源点击获取
返回列表