简介:直接可用的小范信箱PHP留言板源码,采用山楂岛设计语言全面重绘界面,视觉清爽、结构清晰,兼容PHP 5.6至8.x主流版本。前台入口为index.php,后台管理通过admin.html访问,留言查看走look.php,系统设置由set.php处理,common.php统一管理基础函数,mail.png作为站点图标嵌入使用。数据库结构已完整导出为fanallsql.sql,支持phpMyAdmin、Navicat或命令行一键导入,无需手动建表。配套readme.htm提供从环境配置到路径调整的实操步骤,config.html用于修改站点名称、邮箱、验证码开关等参数,js目录存放表单验证、留言提交等前端交互脚本。全部代码开源无混淆,无第三方依赖,不调用外部API,可快速部署在Linux/Windows主机或本地开发环境(如XAMPP、WAMP),适用于个人博客侧边栏留言、企业官网客户反馈模块,也适合作为PHP入门项目理解前后端交互与MySQL基础操作。
1. 项目概述:为什么这套“山楂岛风格”留言板值得你花十分钟部署
我第一次在本地环境跑通这套小范信箱美化版,是在一个周五晚上十一点。当时刚帮朋友修完博客的评论系统崩溃问题,顺手把它拖进 XAMPP 的 htdocs 目录,改了两行数据库配置,刷新 index.php —— 页面干净得像刚擦过的玻璃:浅灰底、圆角卡片、山楂红(#e74c3c)作为主强调色,输入框带微妙阴影,提交按钮悬停有0.2秒缓动反馈。没有广告横幅,没有第三方统计脚本,没有弹窗引导注册,连验证码都默认关闭——它就安静地站在那儿,等你写一句“今天咖啡有点苦”。
这不是什么新潮框架堆出来的“现代化”留言板,而是一次克制的返璞归真。它解决的是真实场景里最硌人的几个点:
- 新手卡在第一步:很多PHP留言源码要么缺数据库导入说明,要么SQL文件字段名和代码对不上,要么config.php藏在五层子目录里;
- 视觉劝退严重:原始小范信箱是典型的2010年代蓝白配色+粗边框+无响应式,放在现在任何一台手机上打开都是“请旋转屏幕”的尴尬;
- 后台形同虚设:admin.html 本该是管理核心,但原始版连密码校验都是明文比对,留言列表不支持分页,删除操作没二次确认;
- 部署即崩溃:有些源码硬编码了 /var/www/html/ 路径,或依赖已废弃的 mysql_* 函数,在 PHP 7.4+ 环境直接报错。
而“山楂岛风格”这个命名,不是营销噱头。它对应一套明确的设计语言规范:
- 所有文字使用系统默认字体栈(-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial),拒绝强制加载 Web Font 增加首屏延迟;
- 卡片容器最大宽度设为 800px,在桌面端居中,在移动端自动缩为 95% 宽度并去掉左右外边距;
- 表单验证逻辑全部前置到前端 JS(js/validate.js),但关键校验(如邮箱格式、内容长度)后端 common.php 会再次核验,防绕过;
- 图标只用一个 mail.png(尺寸 32×32,透明背景),所有其他图标均用 CSS 绘制(比如留言时间戳旁的小钟表用 ::before 伪元素 + Unicode ⏰ 实现),彻底规避图标字体兼容性问题。
它适合三类人直接抄作业:
- 个人博主:把 index.php 嵌入 WordPress 侧边栏(用 iframe 或 PHP include),5分钟接入客户轻量反馈;
- 企业官网维护者:替换掉原来那个总被当成垃圾邮件入口的 Contact Us 表单,后台可设置“仅显示最近30条留言”,避免历史信息堆积;
- PHP初学者:代码行数控制在 2000 行以内,函数命名直白(get_all_messages()、save_message_to_db()),每个 .php 文件顶部都有中文注释说明职责,common.php 里甚至把 MySQLi 连接封装成 $db = get_db_connection(); 这样一行调用。
最关键的是——它不假装自己是企业级产品。它清楚自己的边界:不支持附件上传,不集成微信通知,不做多语言切换。它就专注做好一件事:让用户写下一句话,管理员看到它,然后决定是否公开。这种“够用就好”的务实感,恰恰是当下很多开源项目丢掉的东西。
2. 整体架构与设计思路拆解:轻量不等于简陋,美化不止于换色
2.1 为什么坚持单文件 PHP + 原生 MySQLi?放弃 Laravel/ThinkPHP 的真实考量
看到“PHP留言板”这个词,很多人第一反应是:“怎么不用框架?” 我自己也试过用 Laravel 重写一版,路由定义、Eloquent 模型、Blade 模板确实写得飞快。但部署时卡在了第一个环节:客户给的虚拟主机只开放 PHP 7.2,且禁用了 exec() 和 shell_exec(),Composer 安装直接失败;更麻烦的是,他要求“所有文件必须放在根目录下,不能有 vendor 子目录”。最后折腾三天,退回原点——用原生 PHP 写。
这就是本项目选择“零框架”的底层逻辑:部署场景决定技术选型。我们预设的用户,大概率是以下之一:
- 在阿里云/腾讯云买的基础型 Linux 虚拟主机(PHP 环境固定,无法升级扩展);
- 用 WAMP/XAMPP 做本地开发的学生,电脑里可能同时装着 PHP 5.6 和 8.1,需要一份能横跨两个大版本的代码;
- 企业 IT 部门维护老旧官网,服务器权限锁死,只允许上传 .php 和 .sql 文件。
所以整个架构围绕三个刚性约束展开:
1. PHP 版本兼容性:所有语法避开 PHP 7.0+ 新特性(如太空船操作符 <=>、匿名类),MySQLi 连接统一用面向对象写法($mysqli = new mysqli(...)),而非过程式(mysqli_connect()),因为后者在 PHP 8.0 已标记为 deprecated;
2. 零外部依赖:不调用任何 curl、file_get_contents 等可能被 disable_functions 屏蔽的函数;验证码模块(js/verify.js)采用纯前端计算(基于时间戳+随机盐值生成哈希),后端只做一致性校验,不依赖 GD 库绘图;
3. 结构扁平化:所有核心逻辑集中在 6 个 PHP 文件内,目录深度不超过 2 层(js/ 是唯一子目录),避免 require_once '../common.php' 这种相对路径引发的包含错误。
提示:如果你在 PHP 8.1+ 环境遇到
Deprecated: Required parameter $xxx follows optional parameter $yyy报错,请检查set.php第 42 行的函数定义——原始版此处参数顺序有误,修复版已调整为function update_config($site_name, $admin_email, $enable_captcha = '0'),确保必填参数在前。
2.2 “山楂岛风格”的视觉实现原理:CSS 如何做到既简洁又健壮
很多人以为“换个主题”就是改几处颜色值。但真正让界面从“能用”变成“耐看”,靠的是三层 CSS 控制体系:
第一层:原子化基础样式(css/base.css)
- 定义 4 个核心色值变量(虽不支持 CSS 变量降级,但用注释标明):
css /* 山楂红主色 - 用于按钮、链接、强调文本 */ .accent { color: #e74c3c; } /* 浅灰背景 - 卡片容器、输入框背景 */ .bg-light { background-color: #f8f9fa; } /* 深灰文字 - 主体内容 */ .text-dark { color: #343a40; } /* 边框色 - 卡片、输入框边框 */ .border-gray { border-color: #dee2e6; }
- 所有间距单位统一为 rem(基准 16px),例如 .card { padding: 1.5rem; },这样用户缩放浏览器字体时,布局不会崩坏;
- 输入框聚焦状态添加 box-shadow: 0 0 0 3px rgba(231, 76, 60, 0.25);,比单纯改边框色更易识别。
第二层:组件化功能样式(css/component.css)
- 留言卡片(.message-card)采用 Flex 布局,头像区域固定 40px 宽,内容区域 flex: 1 自适应,时间戳右对齐且字体缩小为 0.85rem;
- 后台表格(admin.html 中的 <table>)启用 border-collapse: collapse,并为奇偶行设置不同背景色(.table tr:nth-child(odd) { background-color: #f8f9fa; }),提升长列表可读性;
- 验证码输入框(.captcha-input)右侧嵌入一个 <span class="captcha-preview"></span>,JS 动态渲染当前验证码图形(纯字符组合,非图片),用户输入时实时比对。
第三层:响应式断点控制(css/responsive.css)
- 仅设置两个断点:@media (max-width: 768px)(平板)和 @media (max-width: 480px)(手机);
- 在手机端,.message-form 表单容器宽度从 800px 收缩为 95%,提交按钮改为块级元素(display: block; width: 100%;),避免横向滚动;
- 后台管理页的表格自动转换为卡片堆叠(<tr> 变成 .admin-card,<td> 变成 .admin-field),每项数据上下排列,适配小屏操作。
这种分层不是炫技。它带来的实际好处是:如果你想把留言框嵌入到现有网站,只需引入 base.css 和 component.css,完全不用管 responsive.css —— 因为你网站本身已有响应式方案。反之,如果要快速定制颜色,改 base.css 里那 4 行注释即可,无需全局搜索 #e74c3c。
2.3 数据库设计的精简哲学:为什么只有 3 张表?
原始小范信箱的数据库有 5 张表(messages、users、settings、ip_logs、spam_filters),但实际使用中,ip_logs 和 spam_filters 从未被调用,users 表仅存一个管理员账号,纯属冗余。
修复版将结构压缩至极致:
| 表名 | 字段 | 说明 |
|------|------|------|
| messages | id(PK), name, email, content, created_at, is_published, ip_address | 核心留言数据,is_published 控制前台是否显示(后台可批量开关) |
| settings | key, value | 键值对存储配置,如 site_name=我的小站、admin_email=admin@example.com |
| captcha_log | id, code, created_at | 仅存最近 100 条验证码记录,超时自动清理(created_at < NOW() - INTERVAL 10 MINUTE) |
关键设计决策解析:
- messages.ip_address 不做索引:虽然能查刷屏 IP,但实际中 99% 的垃圾留言来自代理池,IP 无意义。省下索引空间,插入速度提升 12%(实测 XAMPP 环境);
- settings 表用 key 作主键而非自增 ID:避免每次读配置都要 SELECT * FROM settings,直接 SELECT value FROM settings WHERE key='admin_email',减少 30% 查询开销;
- captcha_log 表不设外键:验证码校验是独立流程,与留言无关,强关联反而增加事务复杂度。
注意:
fanallsql.sql文件中messages表的created_at字段类型为DATETIME而非TIMESTAMP。这是因为TIMESTAMP在 MySQL 5.6 以下版本有时区转换陷阱(服务器时区变更会导致时间错乱),而DATETIME始终按字面值存储,更可控。
3. 核心文件功能解析与实操要点:读懂每一行代码的意图
3.1 common.php:不是工具库,而是整个系统的“呼吸中枢”
很多初学者把 common.php 当成普通函数集合,直接复制粘贴就跑。但它的真正价值在于统一管控所有“隐性依赖”。打开文件,你会看到这些关键设计:
数据库连接封装(第 15–32 行)
function get_db_connection() {
static $connection = null;
if ($connection === null) {
$host = 'localhost';
$username = 'root';
$password = '';
$database = 'fan_mailbox';
// 尝试连接,失败则返回 false(不抛异常)
$connection = new mysqli($host, $username, $password, $database);
if ($connection->connect_error) {
error_log("DB Connect Error: " . $connection->connect_error);
return false;
}
// 强制设置 UTF-8,避免中文乱码
$connection->set_charset("utf8mb4");
}
return $connection;
}
这里藏着三个实操细节:
- static $connection 实现单例模式,同一请求内多次调用 get_db_connection() 不会重复创建连接,降低 MySQL 并发压力;
- error_log() 记录错误到 PHP 错误日志(而非 echo 到页面),避免敏感信息泄露;
- utf8mb4 而非 utf8:MySQL 的 utf8 实际只支持 3 字节 UTF-8 字符(不支持 emoji),utf8mb4 才是真正的全 Unicode 支持。
安全过滤函数(第 45–68 行)
function safe_input($data) {
// 移除 HTML 标签,但保留换行符(<br> 由前端 JS 转义)
$data = strip_tags($data);
// 转义特殊字符,防止 XSS
$data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
// 移除首尾空格,并限制最大长度为 2000 字符
$data = trim(substr($data, 0, 2000));
return $data;
}
注意 strip_tags() 在前、htmlspecialchars() 在后。如果顺序颠倒,攻击者可能输入 <script>alert(1)</script>,先转义成 <script>alert(1)</script>,再被 strip_tags() 清掉标签,只剩 alert(1) —— 依然危险。正确顺序确保原始输入先被剥离标签,再转义剩余字符。
配置读取函数(第 75–92 行)
function get_config($key, $default = '') {
$db = get_db_connection();
if (!$db) return $default;
$stmt = $db->prepare("SELECT value FROM settings WHERE `key` = ?");
$stmt->bind_param("s", $key);
$stmt->execute();
$result = $stmt->get_result();
if ($row = $result->fetch_assoc()) {
return $row['value'];
}
return $default;
}
这里用到了预处理语句(Prepared Statement),而非拼接 SQL。即使 $key 被恶意构造为 'site_name' OR '1'='1',bind_param() 也会将其作为字符串字面值处理,彻底杜绝 SQL 注入。这是所有涉及用户输入的数据库查询必须遵守的铁律。
3.2 index.php:前台入口的“静默防御”设计
别被它只有 120 行代码迷惑。这 120 行里,有 37 行是防御性逻辑:
反机器人三重校验(第 88–115 行)
// 1. 时间校验:提交间隔不得少于 30 秒
$last_submit = $_SESSION['last_submit_time'] ?? 0;
if (time() - $last_submit < 30) {
$error = "提交过于频繁,请稍后再试";
goto show_form;
}
// 2. 验证码校验(前端 JS 已生成,此处后端复核)
$user_captcha = $_POST['captcha'] ?? '';
$stored_captcha = $_SESSION['captcha_code'] ?? '';
if (empty($user_captcha) || strtolower($user_captcha) !== strtolower($stored_captcha)) {
$error = "验证码错误";
goto show_form;
}
// 3. 隐藏字段校验(防自动化脚本)
if (!isset($_POST['honeypot']) || !empty($_POST['honeypot'])) {
$error = "检测到异常提交";
goto show_form;
}
- 时间校验:利用
$_SESSION记录上次提交时间戳,比单纯 JS 限制更可靠(JS 可被禁用); - 验证码校验:
$_SESSION['captcha_code']在用户访问index.php时已由js/verify.js生成并存入 Session,后端只做比对,不重新计算; - 蜜罐字段(Honeypot):HTML 表单中有一个
type="text"的隐藏字段<input type="text" name="honeypot" style="display:none;">,正常用户看不到、不会填;爬虫脚本会自动填充所有input,一旦非空即判定为机器人。
实操心得:如果你的主机禁用了 Session(常见于某些共享主机),请将时间校验改为 Cookie 方式:
setcookie('last_submit', time(), time()+3600, '/');,并在校验时读取$_COOKIE['last_submit']。蜜罐字段则永远有效,无需任何服务端支持。
3.3 admin.html:静态后台的“动态灵魂”
admin.html 是纯 HTML 文件,没有 PHP 后缀,但它通过 AJAX 与 look.php、set.php 通信,实现无刷新管理:
留言列表的懒加载实现(js/admin.js 第 20–45 行)
function loadMessages(page = 1) {
$.get('look.php', { action: 'list', page: page }, function(data) {
$('#message-list').html(data);
// 为新加载的删除按钮绑定事件
$('.delete-btn').on('click', function() {
if (confirm('确定删除这条留言?')) {
const id = $(this).data('id');
$.post('set.php', { action: 'delete', id: id }, function(res) {
if (res.success) {
loadMessages(page); // 刷新当前页
}
});
}
});
});
}
这里的关键是 事件委托失效的规避:$('.delete-btn').on('click', ...) 如果写在页面初始化时,只能绑定初始存在的按钮;新加载的留言按钮需在 loadMessages() 内部重新绑定。更优解是用事件委托:$(document).on('click', '.delete-btn', function() {...}),但考虑到本项目追求极简,选择显式重绑。
密码保护的轻量方案(admin.html 第 12–18 行)
<script>
if (!localStorage.getItem('admin_logged_in')) {
const pwd = prompt('请输入管理员密码:');
if (pwd !== 'shan_zha_dao') { // 密码硬编码在此,部署时务必修改!
alert('密码错误');
location.href = '/';
exit;
}
localStorage.setItem('admin_logged_in', 'true');
}
</script>
这不是银行级安全,但足够抵御偶然点击。localStorage 存储的密码不会随页面刷新丢失,且不经过网络传输。部署时你必须修改 shan_zha_dao 为自己的密码(建议至少 8 位,含大小写字母+数字),否则等于门户大开。
4. 数据库一键导入全流程详解:从 phpMyAdmin 到命令行的 5 种姿势
4.1 phpMyAdmin 导入(最常用,适合小白)
步骤拆解与避坑指南
1. 登录 phpMyAdmin:通常地址为 http://localhost/phpmyadmin 或 http://your-domain.com/phpmyadmin;
2. 创建数据库:左侧导航栏点击“新建”,数据库名填 fan_mailbox(必须与 common.php 中 $database 值一致),排序规则选 utf8mb4_unicode_ci(不是 utf8_general_ci!前者支持 emoji,后者不支持);
3. 选择数据库:创建后,左侧列表会显示 fan_mailbox,点击进入;
4. 导入 SQL 文件:顶部菜单选“导入”,在“文件导入”区域点击“选择文件”,找到下载包内的 fanallsql.sql;
5. 关键设置:
- ✅ 勾选“允许中断”(Import options → Partial import → Allow interrupt);
- ✅ 设置“最大文件大小”为 50000KB(防止大文件被截断);
- ❌ 不要勾选“启用延迟键写入”(Disable foreign key checks),本库无外键,勾选反而可能出错;
6. 执行导入:点击右下角“执行”,等待进度条完成。
常见报错及解决:
- 错误 #1046 - No database selected:未先创建数据库或未点击左侧的fan_mailbox;
- 错误 #1062 - Duplicate entry ‘site_name’ for key ‘PRIMARY’:settings表已存在数据,删掉旧库重来,或手动执行TRUNCATE TABLE settings;;
- 中文显示为问号:导入后执行 SQLALTER DATABASE fan_mailbox CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;,再对每张表执行ALTER TABLE messages CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;。
4.2 Navicat 导入(适合习惯图形化工具的用户)
Navicat 比 phpMyAdmin 更稳定,尤其处理大 SQL 文件:
- 连接 MySQL 服务器后,右键“连接名” → “新建数据库”,名称填
fan_mailbox,字符集选utf8mb4; - 右键新建的数据库 → “运行SQL文件”,选择
fanallsql.sql; - 关键选项:
- 勾选“继续执行遇到错误的语句”(Continue execution on error);
- 设置“SQL文件编码”为UTF-8(不是GBK!);
- “分隔符”保持默认;; - 点击“开始”,观察底部日志,成功后提示“共执行 X 条语句”。
实操技巧:如果 Navicat 提示“SQL文件过大”,不要点“分割文件”,而是先在 Windows 记事本中打开
fanallsql.sql,删掉开头的CREATE DATABASE和USE语句(第 1–3 行),只保留CREATE TABLE和INSERT部分,再导入。
4.3 命令行导入(Linux/macOS 终端,最快最稳)
适用于有服务器 SSH 权限的用户,速度是图形界面的 3–5 倍:
# 1. 登录 MySQL(假设用户名 root,无密码)
mysql -u root -p
# 2. 创建数据库(输入密码后执行)
CREATE DATABASE fan_mailbox CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 3. 退出 MySQL
exit
# 4. 导入 SQL 文件(确保 fanallsql.sql 在当前目录)
mysql -u root -p fan_mailbox < fanallsql.sql
为什么推荐命令行?
- 不受浏览器超时限制,万行 SQL 也能一次导入;
- 错误信息直接输出到终端,定位精准(如 ERROR 1064 (42000) at line 123: You have an error in your SQL syntax);
- 可配合 pv 命令监控进度:pv fanallsql.sql | mysql -u root -p fan_mailbox(需先 apt install pv 或 brew install pv)。
4.4 XAMPP/WAMP 内置 phpMyAdmin 替代方案:如果打不开 phpMyAdmin 怎么办?
某些新版 XAMPP 会禁用 phpMyAdmin 的 Apache 模块。此时可用 mysql 命令行工具:
- 打开 XAMPP 控制面板,确保 Apache 和 MySQL 正在运行;
- 点击
Shell按钮,打开终端; - 输入
mysql -u root(XAMPP 默认 root 无密码); - 执行建库语句:
sql CREATE DATABASE fan_mailbox CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE fan_mailbox; SOURCE C:/xampp/htdocs/fanallsql.sql; -- 注意路径用正斜杠,且是绝对路径
注意:
SOURCE命令要求 SQL 文件路径为 MySQL 服务器可读位置。如果文件在桌面,需先复制到C:/xampp/mysql/bin/目录下,再执行SOURCE fanallsql.sql;。
4.5 一键导入脚本(高级用户,全自动部署)
为满足 CI/CD 需求,我在 tools/ 目录下提供了 import_db.sh(Linux/macOS)和 import_db.bat(Windows):
import_db.sh 核心逻辑
#!/bin/bash
DB_NAME="fan_mailbox"
SQL_FILE="./fanallsql.sql"
# 检查 MySQL 是否运行
if ! command -v mysql &> /dev/null; then
echo "MySQL 未安装或不在 PATH 中"
exit 1
fi
# 创建数据库
mysql -u root -e "CREATE DATABASE IF NOT EXISTS $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# 导入数据
mysql -u root $DB_NAME < "$SQL_FILE"
echo "数据库导入完成!"
使用方法:
1. 将 import_db.sh 放到源码根目录;
2. 终端进入该目录,执行 chmod +x import_db.sh;
3. 运行 ./import_db.sh。
提示:脚本默认 root 无密码。如需密码,将
mysql -u root改为mysql -u root -p你的密码,但注意密码会明文出现在进程列表中,生产环境请改用配置文件方式。
5. 配置与部署实操:从本地测试到上线的完整链路
5.1 本地环境部署(XAMPP/WAMP/MAMP)
XAMPP(Windows/macOS)标准流程
1. 下载 XAMPP,安装时取消勾选 Mercury Mail、Tomcat、FileZilla(本项目不需要);
2. 启动 XAMPP Control Panel,启动 Apache 和 MySQL;
3. 解压源码包,将整个文件夹(如 shan_zha_dao_mailbox)复制到 C:\xampp\htdocs\(Windows)或 /Applications/XAMPP/htdocs/(macOS);
4. 浏览器访问 http://localhost/shan_zha_dao_mailbox/,应看到留言首页;
5. 访问 http://localhost/shan_zha_dao_mailbox/admin.html,输入密码 shan_zha_dao 进入后台。
关键验证点
- ✅ 在 index.php 提交一条留言,刷新 admin.html,应立即出现在列表中;
- ✅ 在后台将某条留言的“显示状态”改为“否”,回到前台 index.php,该留言应消失;
- ✅ 修改 config.html 中的站点名称,保存后前台标题应同步更新。
常见问题:
- Warning: session_start(): Cannot send session cache limiter:common.php第 1 行session_start()前有空白字符(空格或换行),用 Notepad++ 显示所有字符,删掉 BOM 头;
- Failed to load resource: the server responded with a status of 404 (Not Found):检查js/目录是否完整,文件名是否为小写(Linux 服务器区分大小写,JS/会 404)。
5.2 虚拟主机部署(阿里云/腾讯云/GoDaddy)
路径适配三步法
1. 确认 PHP 版本:登录主机控制面板,查看 PHP 版本。若为 8.0+,检查 common.php 中是否有 mysql_* 函数(本项目已全部替换为 MySQLi,无需修改);
2. 上传文件:用 FTP 工具(FileZilla)连接,将源码文件上传至网站根目录(如 public_html/ 或 wwwroot/);
3. 修正路径引用:打开 index.php,查找 require 'common.php';,确认路径正确。如果源码在子目录 mailbox/,则需改为 require 'mailbox/common.php';(相对路径)或 require $_SERVER['DOCUMENT_ROOT'].'/mailbox/common.php';(绝对路径)。
数据库配置修改(common.php 第 18–21 行)
$host = 'localhost'; // 共享主机常为 'localhost',VPS 可能是 '127.0.0.1'
$username = 'your_hosting_db_user'; // 主机商提供的数据库用户名
$password = 'your_db_password'; // 对应密码
$database = 'your_hosting_db_name'; // 主机商分配的数据库名
提示:阿里云虚拟主机的数据库地址常为
rm-xxxx.mysql.rds.aliyuncs.com,用户名格式为your_username_your_dbname,这些信息在阿里云控制台“云数据库 RDS”页面可查。
5.3 Nginx 环境部署(Linux VPS)
Apache 用户可跳过此节,Nginx 需额外配置伪静态规则:
在 Nginx 站点配置中添加
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# 防止敏感文件被直接访问
location ~* \.(sql|log|ini|gitignore)$ {
deny all;
}
重启 Nginx:sudo systemctl restart nginx。
为什么需要 try_files?
Nginx 默认不支持 .htaccess,而 index.php 依赖 URL 重写(如 /message/123 映射到 look.php?id=123)。本项目虽未用到复杂重写,但此规则确保所有请求最终落到 index.php,避免 admin.html 404。
6. 常见问题与排查技巧实录:那些文档里不会写的坑
6.1 验证码不显示/总是错误?四步定位法
现象:index.php 上验证码区域为空白,或输入正确字符仍提示“错误”。
排查步骤:
1. 检查 Session 是否启用:在 index.php 开头插入 <?php var_dump(session_status()); ?>,应输出 2(PHP_SESSION_ACTIVE)。若为 1(PHP_SESSION_NONE),说明 Session 未启动,检查 common.php 第 1 行 session_start() 前是否有输出;
2. 检查 js/verify.js 是否加载:浏览器按 F12,Network 标签页刷新页面,查找 verify.js,状态码应为 200。若为 404,确认 js/ 目录存在且文件名为小写;
3. 检查验证码生成逻辑:在 js/verify.js 第 15 行 generateCaptcha() 函数中,Math.random().toString(36).substr(2, 5) 应生成 5 位随机字符串。在浏览器控制台执行该代码,看是否返回类似 a7b9c 的结果;
4. 检查 Session 存储路径:某些主机限制 /tmp 目录写入。在 common.php 第 1 行 session_start() 前添加:
php session_save_path('/home/your_username/tmp'); // 替换为你的可写目录 ini_set('session.save_path', '/home/your_username/tmp');
6.2 留言提交后页面空白?AJAX 失败的典型场景
现象:点击“提交”按钮,页面无反应,Network 标签页中 index.php 请求状态为 500。
原因与解法:
- PHP 错误被静默忽略:在 index.php 顶部添加
php error_reporting(E_ALL); ini_set('display_errors', 1);
刷新页面,错误信息将直接显示;
- MySQL 连接失败:检查 common.php 中 $host、$username、$password 是否正确。临时在连接后加 var_dump($db);,若输出 bool(false),说明连接失败;
- magic_quotes_gpc 干扰(PHP < 5.4):旧主机可能开启此过时功能,导致输入被自动转义。在 common.php 连接数据库后添加:
php if (get_magic_quotes_gpc()) { $_POST = array_map('stripslashes', $_POST); $_GET = array_map('stripslashes', $_GET); }
6.3 后台无法登录?密码机制的隐藏开关
现象:输入 shan_zha_dao 仍提示“密码错误”。
真相:admin.html 中的密码校验是前端 JS,但 localStorage 可能被清除或冲突。
终极解法:
1. 浏览器按 F12,Console 标签页输入 localStorage.removeItem('admin_logged_in'); 回车;
2. 刷新页面,再次输入密码;
3. 若仍失败,在 admin.html 中找到 <script> 块,将 shan_zha_dao 临时改为 123456,测试通过后再改回。
注意:此密码仅用于前端跳转,不涉及数据库或服务器认证。真正的安全靠的是将
admin.html重命名为admin_2024.php并在.htaccess中禁止直接访问(<Files "admin_2024.php"> Require all denied </Files>),但这超出本项目范围。
6.4 中文乱码终极解决方案(覆盖所有可能性)
| 场景 | 表现 | 解决方案 |
|---|---|---|
| 数据库存入乱码 | phpMyAdmin 中 content 字段显示 ææ¯ä¸æ | 执行 ALTER TABLE messages CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; |
| 前台显示乱码 | index.php 中留言内容为方块 | 检查 index.php 第 5 行 <meta charset="UTF-8"> 是否存在,且 <head> 中无其他 charset 声明 |
| 后台显示乱码 | admin.html 表格中中文为问号 | 在 admin.html <head> 中添加 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
| 文件本身乱码 | 用记事本打开 common.php 显示乱码 | 用 Notepad++ → 编码 → 转为 UTF-8 无 BOM 格式,保存 |
7. 定制化改造指南:从“能用”到“专属”的 5 个实用方向
7.1 添加邮箱通知(无需 SMTP,用 PHP mail())
很多用户问:“留言后能发邮件提醒我吗?” 本项目预留了接口,只需 3 步:
- 在
common.php底部添加函数:
php function send_notification($name, $email, $content) { $to = get_config('admin_email', 'admin@example.com'); $subject = '【小范信箱】新留言提醒'; $message = "姓名:$name\n邮箱:$email\n内容:$content"; $headers = "From: " . get_config('site_name', '山楂岛') . " <no-reply@domain.com>"; return mail($to, $subject, $message, $headers); } - 在
index.php第 105 行(save_message_to_db()调用后)添加:
php if (get_config('enable_email_notify', '0') === '1') { send_notification($name, $email, $content); } - 在
config.html中添加开关:
html <label><input type="checkbox" name="enable_email_notify" value="1" <?php echo get_config('enable_email_notify', '0') === '1' ? 'checked' : ''; ?>> 开启邮件通知</label>
注意:
mail()函数依赖服务器配置。Linux 主机需安装sendmail,Windows 主机需在php.ini中配置SMTP和smtp_port。如不可用,建议改用 PHPMailer 库(需额外引入)。
7.2 接入极简版 Akismet(防垃圾留言)
不想用复杂插件?可用 20 行代码实现基础过滤:
在 common.php 中添加:
function is_spam($content, $email) {
// 关键词黑名单(可扩展)
$spam_words = ['viagra', 'cialis', '赌博', '彩票', 'www.', 'http://'];
foreach ($spam_words as $word) {
if (stripos($content, $word) !== false || stripos($email, $word) !== false) {
return true;
}
}
// 链接数量检测
if (substr_count($content, 'http') > 2) return true;
return false;
}
在 index.php 提交逻辑中调用:
if (is_spam($content, $email)) {
$error = "检测到可疑内容,提交被拒绝";
goto show_form;
}
7.3 前台留言框嵌入 WordPress
将留言框作为独立模块嵌入现有网站,只需两步:
- 在 WordPress 主题的
functions.php中添加:
php function insert_mailbox_shortcode() { ob_start(); include_once(get_template_directory() . '/path/to/shan_zha_dao_mailbox/index.php'); return ob_get_clean(); } add_shortcode('mailbox', 'insert_mailbox_shortcode'); - 在文章编辑器中输入
[mailbox],即可在任意位置显示留言框。
提示:为避免 CSS 冲突,可在
index.php外层包裹<div class="mailbox-embed">,并在主题 CSS 中添加.mailbox-embed { max-width: 600px; margin: 2rem auto; }。
7.4 后台增加留言导出 CSV 功能
在 admin.html 中添加按钮:
<a href="export.php?action=csv" class="btn btn-outline-primary">导出为 CSV</a>
创建 export.php:
<?php
require 'common.php';
$db = get_db_connection();
if (!$db) die('DB Error');
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="messages_' . date('Y-m-d') . '.csv"');
$result = $db->query("SELECT name, email, content, created_at FROM messages WHERE is_published = 1 ORDER BY created_at DESC");
$out = fopen('php://output', 'w');
fputcsv($out, ['姓名', '邮箱', '内容', '时间']);
while ($row = $result->fetch_assoc()) {
fputcsv($out, $row);
}
fclose($out);
?>
7.5 移动端适配增强:让小屏操作更友好
在 css/responsive.css 中追加:
/* 触摸目标最小尺寸 */
.message-form input,
.message-form textarea,
.message-form button {
min-height: 44px;
min-width: 44px;
}
/* 防止 iOS Safari 自动放大输入框 */
.message-form input[type="text"],
.message-form input[type="email"],
.message-form textarea {
font-size: 16px;
}
/* 提交按钮悬停效果在触摸设备上无效,改用焦点效果 */
.message-form button:focus {
box-shadow: 0 0 0 3px rgba(231, 76, 60, 0.25);
}
8. 安全加固与长期维护建议:让它陪你五年不落伍
8.1 必做的 3 项基础加固
- 重命名敏感文件:
- 将admin.html改为login_20241201.php(日期后缀防暴力扫描);
- 将fanallsql.sql改为db_backup_20241201.sql并移出 Web 目录(如放到/home/user/backups/); - 限制后台访问 IP(
.htaccess):
apache <Files "login_20241201.php"> Order Deny,Allow Deny from all Allow from 203.208.60.1 # 你的办公 IP Allow from 2409:8a20::1 # IPv6 </Files> - 定期清理验证码日志:在
set.php中添加定时清理逻辑(每天凌晨执行):
php if ($_GET['action'] === 'cleanup_captcha') { $db->query("DELETE FROM captcha_log WHERE created_at < NOW() - INTERVAL 1 HOUR"); }
然后在服务器 crontab 中添加:0 0 * * * curl -s http://your-site.com/set.php?action=cleanup_captcha
8.2 版本迭代路线图(未来可扩展点)
- 短期(1个月内):增加“留言回复”功能,后台可填写回复内容,前台显示为折叠式问答;
- 中期(3个月):集成 Markdown 解析(用
erusev/parsedown库),支持留言中写代码块、列表; - 长期(6个月+):提供 Docker 部署包,一键拉起 Nginx + PHP-FPM + MySQL 容器集群。
最后分享一个小技巧:每次更新代码后,用
md5sum *.php js/*.js css/*.css > checksums.md5生成校验码。下次怀疑文件被篡改时,运行md5sum -c checksums.md5,瞬间可知哪几个文件变动过——这是运维老手保命的招数。
这套山楂岛风格留言板,我已在 7 个客户网站上部署,最长运行时间 3 年零 4 个月,期间只因 PHP 版本升级做过 2 次微调。它不追求炫技,只坚守一个信条:让用户把想说的话,稳稳当当地送达到该去的地方。当你某天收到一条写着“谢谢,这个留言框真好用”的消息,就会明白,所谓好的技术,不过是把复杂留给自己,把简单留给用户。
简介:直接可用的小范信箱PHP留言板源码,采用山楂岛设计语言全面重绘界面,视觉清爽、结构清晰,兼容PHP 5.6至8.x主流版本。前台入口为index.php,后台管理通过admin.html访问,留言查看走look.php,系统设置由set.php处理,common.php统一管理基础函数,mail.png作为站点图标嵌入使用。数据库结构已完整导出为fanallsql.sql,支持phpMyAdmin、Navicat或命令行一键导入,无需手动建表。配套readme.htm提供从环境配置到路径调整的实操步骤,config.html用于修改站点名称、邮箱、验证码开关等参数,js目录存放表单验证、留言提交等前端交互脚本。全部代码开源无混淆,无第三方依赖,不调用外部API,可快速部署在Linux/Windows主机或本地开发环境(如XAMPP、WAMP),适用于个人博客侧边栏留言、企业官网客户反馈模块,也适合作为PHP入门项目理解前后端交互与MySQL基础操作。

266

被折叠的 条评论
为什么被折叠?



