资讯动态

手写PHP表白网站:从口令验证到部署避坑全攻略

发布时间:2026/10/13 14:01:08 来源:尧图企业网站定制
简介一套基于PHP开发的情感表白类网页应用源码定位清晰面向PHP初学者、个人开发者以及想快速搭建个性化表白或感谢页面的普通用户用互动网页分享心意免去从零开发成本。资源压缩包仅25KB共19个文件涵盖12个PHP业务文件、JS交互脚本、CSS样式、SQL建表脚本和Apache配置文件类型齐全又轻量配合目录结构可快速定位入口、页面、公共函数与静态资源。代码完整覆盖了PHP后端开发常见环节入口脚本负责初始化与页面渲染异步脚本配合AJAX实现不刷新页面更新内容URL重写规则和SQL建表脚本也都包含在内附带的说明文档详解安装部署步骤有效降低环境配置门槛。已有390人学习下载对想通过实际项目掌握PHP语法、数据库操作、前后端交互及基础安全处理的新手特别友好。1. 手写一个 PHP 表白网站真的还有人这么做吗先说结论有而且不少。每年情人节前后总有人搜“PHP写的表白网站源码”这种关键词。他们不是不会前端而是想在某个特定日子给某个特定的人一个能拿得出手、能长期打开的页面。用 PHP 做表白网站表面上看是技术选型实际上是选了一条“不需要复杂前端工程、一台普通虚拟主机就能跑、后期还能动态加内容”的路。这类源码的核心价值从来不是 PHP 本身而是三个点第一页面能不能在手机和电脑上都好看第二表白内容能不能藏在 URL 参数或后台里给对方一个“打开即惊喜”的仪式感第三能不能动态记录对方的回应比如“接受”或“再看看”这些数据存下来是后期可以做点小文章的地方。适合的人群也很明确懂一点 PHP 语法、不想碰 Node 或 Python 部署、手头只有一台虚拟主机的开发者或者想拿这个当 PHP 入门练手项目的新手。这篇文章会把一个常见表白网站从设计思路、目录结构、核心代码到部署踩坑完整拆开。你不用去网上找一个来路不明的压缩包直接照着我下面给出的方案自己就能从零写出来并且能在真实服务器上跑通。2. 表白网站应该有什么需求拆解与页面架构在写代码之前先把需求想清楚。一个合格的 PHP 表白网站至少要包含四个模块展示页、交互页、后台数据入口、部署配置。很多人拿到源码第一件事是改文字结果发现改完样式乱了就是因为没搞清楚每个文件是干什么的。2.1 从单纯的页面展示到“互动回应”的升级最早的所谓表白网站就是单页 HTML加上一段 CSS 动画弄点飘落花瓣、跳动爱心就算完事。但 PHP 版本如果只做到这个程度那和静态页没有区别用 PHP 的意义就消失了。我一般会建议做成这样第一个页面是“邀请页”输入对方名字或特定口令才能进入这样 URL 本身不会直接暴露内容第二个页面是“表白主页面”包含文字、图片、背景音乐和动画第三个页面是“回应页”放一个“我接受”和“我再想想”的按钮。重点来了——点击按钮之后PHP 脚本会把回应时间、IP、按钮选项写入一个文本文件或 SQLite 数据库然后跳转到不同的结果页。这一步是从“展示”升级到“交互”的关键。对方接受后可以展示一张带日期和名字的纪念卡片拒绝或犹豫时展示一个俏皮又不尴尬的页面。整个过程都是 PHP 在服务端完成的逻辑非常清晰非常适合作为 PHP 入门项目来理解表单提交、会话管理和文件读写。2.2 文件结构与页面流转设计一个清晰的文件结构决定了后期改代码时会不会骂娘。常见做法如下love-site/ ├── index.php // 入口页口令验证 ├── main.php // 表白主页面 ├── response.php // 处理回应表单 ├── accept.php // 接受后的结果页 ├── reject.php // 未接受的结果页 ├── admin.php // 查看回应记录可选 ├── assets/ │ ├── css/style.css // 样式 │ ├── js/main.js // 动画与交互 │ └── music/bgm.mp3 // 背景音乐 └── data/ └── responses.log // 回应记录文件页面流转是index.php→ 口令正确 →main.php→ 点击按钮 →response.php处理并重定向 →accept.php或reject.php。整体非常简单但有几个细节要提前想好。口令校验放在服务端不放在 JavaScript 里因为前端校验只是体验服务端才是真正拦截。response.php处理完之后必须用header(Location: ...)重定向不能直接输出结果页内容否则刷新页面会重复提交数据。data/目录需要提前建好并设置写入权限很多源码跑不起来就是死在权限上。2.3 部署环境PHP 版本与服务器选择这套代码对 PHP 版本要求很低PHP 5.6 及以上都能跑。不建议用特别新的语法特性比如属性类型声明、构造器属性提升因为目标环境可能是老旧的虚拟主机PHP 版本还在 7.4 甚至更低。服务器方面常见选择是 Apache 或 Nginx PHP-FPM 的组合。用 Apache 时注意.htaccess文件是否被启用因为有些虚拟主机默认关闭AllowOverride导致 URL 重写规则不生效。用 Nginx 时要确保index index.php配置正确否则访问根目录会直接列出文件或者报 403。开发环境我通常直接用php -S本地起服务测试命令如下cd love-site php -S 127.0.0.1:8080这段命令会启动 PHP 内置开发服务器不需要额外装 Apache 或 Nginx非常适合前期调试。注意内置服务器是单进程阻塞模式只适合开发测试不能用于生产环境。3. 从零写出核心代码口令、主页面与回应处理现在开始写实际代码。我一个文件一个文件拆开讲每个文件都给出完整代码和关键说明。你照着抄就能跑通改改文字就能用。3.1 入口页用 PHP 校验口令并跳转index.php的职责很简单显示一个输入框比对用户输入的值正确就跳转到主页面错误则提示重新输入。关键点是口令不能写在 JavaScript 里必须由 PHP 在服务端比对。?php // index.php session_start(); // 预设口令可以改成你想要的内容 $correct_password love2024; // 处理表单提交 if ($_SERVER[REQUEST_METHOD] POST) { $input isset($_POST[password]) ? trim($_POST[password]) : ; if ($input $correct_password) { $_SESSION[verified] true; header(Location: main.php); exit; } else { $error 口令不对哦再想想; } } ? !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title一个秘密页面/title link relstylesheet hrefassets/css/style.css /head body div classentry-box h1这是一个秘密页面/h1 form methodpost input typetext namepassword placeholder输入口令 required button typesubmit进入/button /form ?php if (isset($error)): ? p classerror-msg?php echo htmlspecialchars($error, ENT_QUOTES, UTF-8); ?/p ?php endif; ? /div /body /html代码逻辑不难先开 session再用 POST 方式接收表单数据。htmlspecialchars对错误信息做转义是防止反射型 XSS 的最小手段。header(Location: main.php)后面必须加exit否则脚本会继续往下执行可能出现页面内容被输出后再跳转的警告。口令本身写死在代码里够用吗够也不够。如果只是给一个人看写死没问题但如果对方懂一点代码查看源代码就能看到 PHP 在哪段被请求不过看不到 PHP 解析后的结果所以写死口令在 PHP 页面里其实是安全的——因为最终返回给浏览器的 HTML 里不会包含服务端代码。如果想更安全一点可以用password_hash()存哈希值然后用password_verify()比对。但为了减少初学者的负担上面这个方案对低版本 PHP 更友好。3.2 主页面把视觉与音乐做进去main.php是重头戏。页面上要有一句直达内心的话、一张合适的背景图或渐变背景、一段轻柔的音乐、一个“接受”和“一个再看看”的按钮。?php // main.php session_start(); // 未通过口令验证直接拦截 if (empty($_SESSION[verified])) { header(Location: index.php); exit; } ? !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title写给你的话/title link relstylesheet hrefassets/css/style.css /head body div classlove-container h1 classmain-title有些话想当面告诉你/h1 p classmain-text 认识你之后我发现日子开始有了期待。br 不知道你愿不愿意和我一起把这份期待变成以后 /p audio autoplay loop controls source srcassets/music/bgm.mp3 typeaudio/mpeg /audio div classaction-buttons form actionresponse.php methodpost styledisplay:inline; input typehidden namechoice valueaccept button typesubmit classbtn-accept我愿意/button /form form actionresponse.php methodpost styledisplay:inline; input typehidden namechoice valuereject button typesubmit classbtn-reject我再想想/button /form /div /div script srcassets/js/main.js/script /body /html这个页面的关键有两点。第一开头那几行 session 校验不能省不然别人直接访问main.php就能跳过口令查看内容。第二按钮不是普通的a链接而是包在form里的提交按钮这样response.php才能用 POST 接收到choice参数。audio autoplay在移动端 Safari 上经常不生效这是浏览器限制不是代码问题。想兼容移动端就得在main.js里监听用户的第一次触碰再触发播放。这个我后面会补上。3.3 回应处理文件写入与页面跳转response.php是整个交互逻辑的核心。它做什么接收choice参数把回应记录追加到data/responses.log然后按选择跳转到不同结果页。?php // response.php session_start(); if (empty($_SESSION[verified])) { header(Location: index.php); exit; } $choice isset($_POST[choice]) ? $_POST[choice] : ; $log_file __DIR__ . /data/responses.log; if (!in_array($choice, [accept, reject], true)) { die(非法参数); } $ip $_SERVER[REMOTE_ADDR]; $time date(Y-m-d H:i:s); $entry [$time] IP:$ip choice:$choice\n; file_put_contents($log_file, $entry, FILE_APPEND | LOCK_EX); if ($choice accept) { header(Location: accept.php); } else { header(Location: reject.php); } exit; ?__DIR__拿到的是当前文件所在目录的绝对路径这样无论脚本从哪里被调用都能正确找到data目录。file_put_contents第三个参数FILE_APPEND | LOCK_EX表示追加写入并加锁避免两个人同时点按钮时写入内容交错一行。in_array用严格模式true防止传入非预期字符串。注意header(Location: ...)后面没有直接接exit而是放在 if 分支结束后统一exit这是可以的但新手容易在header()之后忘了exit导致后续代码继续执行。保险写法是在每次header(Location:)之后紧跟exit;。3.4 接受页与结果页展示纪念内容accept.php是对方点下“我愿意”之后看到的页面值得好好设计。我建议放一张纪念卡片上面写清楚“某年某月某日我们在一起了”再放一段祝福的话。reject.php不要放沮丧的内容放一句“没关系我可以继续等”就足够体面。?php // accept.php session_start(); if (empty($_SESSION[verified])) { header(Location: index.php); exit; } // 获取当前日期用于展示 $today date(Y年m月d日); ? !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title最好的答案/title link relstylesheet hrefassets/css/style.css /head body div classresult-box accepted h1终于等到你/h1 p你说“我愿意”的时候是我这阵子最开心的瞬间。/p p classdate-mark这一天被记下来了?php echo $today; ?/p /div /body /html这里 PHP 只用来输出一个日期为了保持页面结构干净我把日期变量在顶部准备好在 HTML 中合适位置输出。如果你想记录更精确的时间比如精确到秒就把date(Y年m月d日 H:i:s)传给页面。到这里一个最小完整版的 PHP 表白网站已经能跑了。接下来我会把样式、动画和数据可视化这些锦上添花的部分补齐并把部署过程中最常遇到的坑一个个拆开。4. 让页面好看样式、动画与移动端适配技术上的表白网站成败往往不在 PHP 代码而在视觉呈现。一个配色凌乱、动画卡顿的页面再好的内容也大打折扣。这一章讲 CSS 设计、JavaScript 动画和移动端适配的常见做法。4.1 主风格与配色方案表白网站的配色通常以粉色、红色、暖白为主尽量不要引入第三种色系。我常用这样一套/* assets/css/style.css 核心片段 */ body { margin: 0; min-height: 100vh; background: linear-gradient(135deg, #ffdde1 0%, #ee9ca7 100%); font-family: -apple-system, PingFang SC, Microsoft YaHei, sans-serif; display: flex; align-items: center; justify-content: center; } .love-container { max-width: 720px; margin: 20px; padding: 40px; background: rgba(255, 255, 255, 0.8); border-radius: 24px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); text-align: center; }渐变背景linear-gradient(135deg, ...)让页面不生硬配合半透明白色卡片形成层次感。max-width加margin: auto保证在手机上不会溢出屏幕。font-family里先放系统自带字体再放中文常见字体是防止不同设备字体渲染差异的常见写法。卡片圆角24px和阴影0 8px 32px rgba(0,0,0,0.1)是现代 UI 风格中很典型的“软阴影”做法视觉上比直角卡片舒适很多。4.2 JavaScript 动画心跳、飘花与触碰播放音乐main.js里加两类效果一类是心率动画直接操作 CSS 类切换另一类是点击屏幕触发音符或花瓣飘落。同时解决音频的移动端自动播放问题。// assets/js/main.js (function () { // 移动端自动播放兼容首次触碰后播放音乐 var audio document.querySelector(audio); if (audio) { var playAudio function () { audio.play().catch(function () { // 这里吞掉异常避免控制台报错 }); document.removeEventListener(touchstart, playAudio); }; document.addEventListener(touchstart, playAudio); } // 心率缩放的视觉动画 var title document.querySelector(.main-title); if (title) { setInterval(function () { title.classList.toggle(beat); }, 500); } })();代码逻辑很直接。audio.play()在移动端如果没触发用户手势会被拒绝并返回一个 Promise因此用catch吞掉异常。touchstart监听确保首次触摸屏幕就触发播放。setInterval每 500 毫秒切换一次.beat类配合 CSS 里的缩放动画形成心跳效果。配套 CSS 片段.main-title.beat { animation: heartbeat 1s ease-in-out infinite; } keyframes heartbeat { 0% { transform: scale(1); } 25% { transform: scale(1.05); } 50% { transform: scale(1); } 75% { transform: scale(1.05); } 100% { transform: scale(1); } }这个动画的关键在关键帧设计不是简单线性变大变小而是“变速心跳感”在 25% 和 75% 两个位置放大中间回落到原始大小模拟真实心跳节奏。飘落花瓣效果如果纯手写需要维护粒子数组、更新位置、碰撞检测代码量不小。一个更轻量的做法是用 CSS 动画配合随机定位生成静态装饰元素但视觉上动态感稍弱。4.3 移动端适配的 3 个细节第一viewport标签必须有缺失会造成手机端页面被缩放得特别小。前面每个 HTML 文件的head里都写了nameviewport不要删掉。第二按钮尺寸。手机端点击目标最小建议 44px 高不然容易误触。在 CSS 中可以给.action-buttons button加上min-height: 48px; padding: 12px 24px; font-size: 16px;。第三背景音乐文件大小要控制。音质再好超过 5MB 的文件在弱网环境下会一直加载不出来。建议先用格式工厂或 FFmpeg 压缩成 128kbps 的 MP3单首 1MB 到 2MB 就够用。FFmpeg 压缩命令ffmpeg -i original.mp3 -ar 44100 -ac 2 -b:a 128k bgm.mp3-b:a 128k把音频码率压到 128kbps人声和纯音乐的听感在这个码率下损失很小。需要更小体积可以降到 96k。5. PHP 表白网站避坑指南部署与运行的 5 个常见问题前端再漂亮后端跑不起来也是白搭。这一章写我见过的 5 个最典型的运行问题按“现象 → 原因 → 解决”的方式拆开大家遇到类似问题能直接对号入座。5.1 现象页面能打开但 CSS 完全没加载有人把源码上传后发现页面“光秃秃”的所有样式都丢了。用浏览器开发者工具查看时CSS 文件的状态是 404。原因通常不是代码写错而是目录结构变了。比如把index.php放在了子目录里CSS 却仍通过相对路径assets/css/style.css引用而相对路径是相对于当前 URL 路径解析的不是相对于文件位置。假设子目录是love/浏览器会去请求love/assets/css/style.css如果 CSS 实际在上级目录的assets/下就 404 了。解决方法是使用绝对路径。在 PHP 中可以用$base_url /love;然后手动拼到link标签里更简单的做法是直接按根目录部署让所有文件位于服务器 Web 根目录下。如果一定要放子目录就用 PHP 动态输出根路径?php $base_url rtrim(dirname($_SERVER[SCRIPT_NAME]), /); ? link relstylesheet href?php echo $base_url; ?/assets/css/style.css这段代码用SCRIPT_NAME自动算出当前脚本所在目录再拼上静态资源路径。好处是以后换目录不用改代码坏处是每个页面都要重复写。更省事的做法全部文件放在根目录直接用/assets/css/style.css。5.2 现象点击按钮后页面变成空白表单提交到response.php返回一片空白甚至提示“此页面无法显示”。先确认是不是 500 错误再查 PHP 错误日志。最常见原因是指定的data目录不存在。代码里file_put_contents(data/responses.log, ...)时如果目录不存在PHP 不会自动创建目录就会报failed to open stream: No such file or directory。部分虚拟主机允许你上传时保留空目录但更多时候空目录会被清理或没有上传。解决就是提前检测目录没有则自动创建$log_dir __DIR__ . /data; if (!is_dir($log_dir)) { mkdir($log_dir, 0755, true); } $log_file $log_dir . /responses.log;mkdir第三个参数true表示递归创建父目录不存在时一并创建。权限0755对目录来说够用不要直接用0777避免安全风险。5.3 现象每次点击都会重复记录一条回应用户点了“我愿意”日志里出现了好几条同样的记录。原因是用户在结果页按了 F5 刷新浏览器重新提交了上一次的 POST 请求。解决思路就是前面说过的 PRG 模式Post/Redirect/Get表单提交到response.php后它不直接输出内容而是用header(Location: ...)重定向到结果页。结果页是 GET 请求无论怎么刷新都不会重新触发response.php的写入逻辑。你可以在response.php的最后加上这段// 记录完成重定向到各自的结果页 if ($choice accept) { header(Location: accept.php); } else { header(Location: reject.php); } exit;如果这样做了还有重复记录那就说明response.php内部本身就输出了内容导致header()无效并报警告。检查response.php里是否有空格或 BOM 头这个很隐蔽用编辑器改成 UTF-8 无 BOM 即可。5.4 现象在手机上打开背景音乐不播放代码没问题桌面浏览器正常安卓和 iOS 上静音或直接不响。这是浏览器自动播放策略限制——不允许网页加载后自动发声必须用户先有交互。前面main.js中已经写了touchstart监听方案。如果你把这段删除或没加就换成点击按钮时触发播放。优先级建议用click事件而不仅是touchstart因为某些桌面浏览器两种事件都支持var startPlay function () { audio.play().catch(function () {}); document.removeEventListener(click, startPlay); document.removeEventListener(touchstart, startPlay); }; document.addEventListener(click, startPlay); document.addEventListener(touchstart, startPlay);如果加了这段还不播放检查音频文件是不是损坏、路径是否正确是否跨域加载。可以打开浏览器开发者工具看 Network 面板里面音频资源返回 200 还是 404。5.5 现象页面能打开但 URL 重写不生效有些人会加一条伪静态规则想把main.php伪装成/love之类的短链接。在 Apache 下写.htaccess在 Nginx 下要在location里配try_files规则写错就直接 404。我不建议在表白网站上做 URL 重写理由有两个一是这个项目页面少短链接带来的收益极小二是对方收到链接后如果看到路径里有main.php反而更容易猜出后面还有accept.php、reject.php。保持原样即可。如果真的要配Apache 环境下.htaccess这样写RewriteEngine On RewriteRule ^love$ main.php [L]Nginx 环境则要写在 server 块中location /love { rewrite ^/love$ /main.php last; }注意.htaccess文件需要用 ASCII 编码保存不能有中文注释否则部分服务器会报 500。6. 给这个网站加一点私藏技巧后台查看回应记录与防刷新防绕过最后一章分享两个我比较常用的增强技巧不复杂但能给这个项目加点“后手”。一是做一个简单的后台查看记录页面二是给结果页加上禁止回退或防重复处理的额外保护。6.1 用一个超简单的后台页面读取回应记录admin.php不搞复杂的登录体系就靠一个独立的文件锁。虽然安全等级不高但对这个场景够用了。读取data/responses.log把它逐行解析成表格展示。?php // admin.php session_start(); // 简单访问令牌生产环境请用更强的鉴权 $token admin123; $submitted_token isset($_GET[token]) ? $_GET[token] : ; if ($submitted_token ! $token) { die(访问被拒绝); } $log_file __DIR__ . /data/responses.log; $lines file($log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); ? !DOCTYPE html html langzh-CN head meta charsetUTF-8 title回应记录/title /head body table border1 cellpadding6 styleborder-collapse:collapse; thead trth时间/thth来源 IP/thth选择/th/tr /thead tbody ?php foreach ($lines as $line): ? ?php if (preg_match(/\[(.*?)\] IP:(.*?) choice:(.*)$/, $line, $m)): ? tr td?php echo htmlspecialchars($m[1], ENT_QUOTES, UTF-8); ?/td td?php echo htmlspecialchars($m[2], ENT_QUOTES, UTF-8); ?/td td?php echo $m[3] accept ? 接受了 : 再想想; ?/td /tr ?php endif; ? ?php endforeach; ? /tbody /table /body /htmlfile()函数把日志文件按行读入数组FILE_IGNORE_NEW_LINES去掉每行末尾的换行符FILE_SKIP_EMPTY_LINES跳过空行。正则部分是可选的如果日志格式固定直接按explode( , $line)拆分也可以但正则更稳不会因为 IP 中包含空格而错乱。调用方式示例admin.php?tokenadmin123。注意这种直接在 URL 带 token 的做法不适合公网长期暴露真要放公网至少要改成 POST 提交和会话校验。6.2 结果页防刷新防回退的两种手段对方接受之后如果在accept.php上刷新理论上不会再次写入日志因为已经重定向过了。但有一个新问题手机浏览器点“回退”可能会退到main.php再次看到表白内容体验不好。更关键的是对方可能从main.php重新提交一次“接受”产生第二条日志。最简单粗暴但不完美的办法是在main.php提交之后立即销毁会话或标记$_SESSION[responded] true;然后在main.php开头判断if (!empty($_SESSION[responded])) { header(Location: accept.php); exit; }这样对方点回退回main.php时会被自动再弹回accept.php避免重复看到选择按钮。但这有个副作用如果对方真的想再做一次选择比如先拒绝了后来又改主意就没有入口了。所以这个方法更适合你明确知道对方一定会接受、不希望节外生枝的场景。想要更灵活可以只在同一会话内允许一次提交超时就失效if (time() - $_SESSION[responded_time] 3600) { unset($_SESSION[responded]); }这个设计是给“选择之后最好别反悔”的场景留一个温和的缓冲比直接锁死更人性化。6.3 记录对方所在城市一个可选的小扩展如果你的 PHP 环境支持geoip扩展或者你用的是带第三方 IP 库的框架可以顺手把对方 IP 解析成城市名写进日志里。这样“我愿意”记录里不仅有时间还有地点以后做纪念小卡片时能多一行内容。不引第三方库最简做法是调用公开 IP 位置接口。但这不是 PHP 内置功能不同服务的响应格式差异大还涉及隐私我不推荐在表白网站上引。真要记录记 IP 就足够城市这个信息后续想加随时能加。6.4 收尾一次真实场景的使用习惯我自己在给朋友搭这种网站时最后一步一定是把所有页面的title和短信预览里显示的那行文字改干净。对方收到链接时微信或短信里大概率会先看到 URL 和页面标题如果标题写着“PHP 表白源码测试”惊喜瞬间变笑话。另一个习惯是部署后一定亲手走一遍完整流程打开入口页 → 输错一次口令 → 输对进入 → 点接受 → 看结果页 → 打开后台看日志。这套流程每次都能发现一两个问题比如某些手机浏览器音频不播、某些系统字体回退导致排版错位。你把这个流程当成上线前自检清单会省下很多现场尴尬。这个方向值得做但记得把它当成一份礼物去打磨而不是只当成一段代码去交付。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑