简介:这是一份面向网站开发初学者与心理测试类站点运营者的静态页面源码包,版本为 v1.0,围绕情感、性格、社交等主题搭建了完整测试栏目,涵盖测试空间、情爱测试、心理测试、社交测试、成功测试、性格测试、性爱测试、个性测试、异类测试九大类,全部采用纯静态页面实现,无需后端即可直接部署预览,也适合本地打开直接体验,可作为快速搭建轻量级互动测试页面的参考模板。压缩包共482个文件,主体为447个网页文件,辅以33个GIF动图用于界面装饰,另有1个JS脚本与1个DB数据文件,整体仅996KB,结构简洁、加载轻便。已有1515人学习/下载,资源目录分类清晰,九个栏目各自成块,可按需定位对应页面,也能通过少量图片与脚本理解静态页面的交互组织方式。
1. 心理测试源码 v1.0:一个能直接跑的 PHP 测评包,先拆别急着改
如果你只是想给公司内部做一个心理测评工具,从零写前后端至少两周;直接下这份心理测试源码 v1.0,一个晚上就能在服务器上跑起来。它是典型的 PHP 老工程:没有框架,答题页是纯 HTML+JS,后端用 Session 记录答案,MySQL 存题库与结果,解压到 Web 目录就能访问。但“能跑”不等于“能用”——它的维度计分、反向题、结果阈值都藏在代码里,不拆开过一遍,改出来的问卷分数会张冠李戴。适合拿去做课程设计、团队内部测评,也适合源码建站时把它当功能模块嫁接。这篇按我实际拆包顺序来:先看结构与链路,再部署,再换题库,最后集中讲几个高频坑。
2. 运行链路与源码结构:从答题入口到结果落库的完整路径
这套源码的整体结构并不复杂,属于“前端单页 + 后端脚本”的老实写法。没有路由、没有模板引擎,一个 PHP 文件就是一个页面,数据流是一条直线:index.php 出题,答题提交回 index.php 存 Session,全部答完跳转 result.php 计分并落库。这种结构的缺点是不够现代,但优点是心理测试这套场景逻辑简单,文件少,适合自己动手改。
拆开压缩包之后,核心目录是下面这一套,我通常先按这个表格过一遍再动手:
| 路径 | 职责 |
|---|---|
| index.php | 答题入口,读取题目、渲染选项、处理翻页 |
| result.php | 计分结果页,展示维度得分并写入记录表 |
| inc/config.php | 数据库连接、站点名称、题目总数常量 |
| inc/score.php | 计分逻辑,维度题号、反向题、阈值映射都在这 |
| admin/ | 后台管理,题库增删改、查看测试记录 |
| assets/ | CSS、JS、图片,答题页换皮肤改这里 |
| data/psych_test.sql | 数据库初始化脚本,含表结构和示例题目 |
| install/ | 环境自检脚本,检查 PHP 版本和扩展是否满足要求 |
拿到包之后别直接扔到服务器上,先打开 install/check.php 看一眼,它会把 PHP 版本、PDO 扩展、MySQL 扩展、目录写权限列出来。v1.0 是很多年前写的,对 PHP 版本比较挑剔,这一步能提前暴露第四章的第一个坑。
2.1 答题主流程:Session 数组怎么把几十道题串起来
v1.0 的答题状态全部放在 PHP Session 里,核心代码在 index.php 中,拆开看就是这个流程:
<?php session_start(); if (!isset($_SESSION['user_flow'])) { $_SESSION['user_flow'] = []; } // 题目ID从URL参数读,默认从第1题开始 $qid = isset($_GET['qid']) ? (int)$_GET['qid'] : 1; // 题目总数来自配置文件常量,默认50 $total = defined('TOTAL_QUESTIONS') ? TOTAL_QUESTIONS : 50; if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 选项值必须是1到5,filter_input防止脏数据写入Session $answer = filter_input(INPUT_POST, 'answer', FILTER_VALIDATE_INT); if ($answer >= 1 && $answer <= 5) { $_SESSION['user_flow'][$qid] = $answer; } $next = $qid + 1; if ($next > $total) { header('Location: result.php'); exit; } header('Location: index.php?qid=' . $next); exit; } ?>这里要理解一个关键点:$qid 是题目在数据库里的主键 ID,不是“第几页”。Session 里的 user_flow 数组用题目 ID 做键、选项值做值,这样做的好处是后续计分时可以直接按题号索引,不用管用户答題的顺序。
项目里选项是 1 到 5 五档,对应前端显示的“完全不符、较少、中等、较多、完全符合”。filter_input 这一步是我比较认可的设计,心理测试场景下用户可能快速乱点或提交异常值,加了类型校验之后,非法值根本进不了 Session,计分阶段就不会出现除以零或者越界的情况。
2.2 计分模块:维度累加与反向计分的处理方式
计分逻辑全部集中在 inc/score.php,读完之后你就明白这套源码的核心设计是什么了。
<?php require_once 'inc/config.php'; // 维度定义:维度名 -> 显示名 + 属于该维度的题目ID $dims = [ 'stress' => ['label' => '压力自评', 'qids' => [1, 3, 8, 12, 15, 19]], 'mood' => ['label' => '情绪倾向', 'qids' => [2, 5, 9, 11, 14, 20]], ]; // 反向计分题号:这类题选1分反而应该得5分 $reverse = [5, 11]; $scores = []; foreach ($dims as $key => $cfg) { $scores[$key] = 0; foreach ($cfg['qids'] as $id) { if (in_array($id, $reverse)) { // 选项1~5,反向题用 6-x 把分数翻过来 $scores[$key] += 6 - $_SESSION['user_flow'][$id]; } else { $scores[$key] += $_SESSION['user_flow'][$id]; } } } ?>这里最容易被忽视的是反向计分。比如题目“我很难让自己安静下来”,选“完全符合”得 5 分,这在情绪倾向上属于高分组;但另一题“我最近睡眠很安稳”,如果选“完全符合”还得 5 分,那方向就反了。所以设计者专门维护了一个 reverse 数组,用 6 减去原选项值实现分数反转。
每道题在入库时都有 is_reverse 字段标注是否反向,但 score.php 里的数组是手工维护的,新增题目时两处必须同步。我见过有人往题库里加了反向题,忘了把题号加进 reverse,结果这个维度的分数整体被拉高,所有测试者的结果都偏向异常区间。
2.3 落库与结果页:一条测试记录是怎么写进数据库的
全部答完提交之后,index.php 会跳转到 result.php,这个页面同时做三件事:计分、落库、展示结果。
<?php require_once 'inc/score.php'; require_once 'inc/config.php'; // 防御:没答题直接访问结果页,就踢回首页 if (empty($_SESSION['user_flow'])) { header('Location: index.php'); exit; } $total = array_sum($scores); $insert = $pdo->prepare( 'INSERT INTO t_results (user_name, score_json, total, create_time) VALUES (?, ?, ?, NOW())' ); $insert->execute([ $_SESSION['user_name'], json_encode($scores, JSON_UNESCAPED_UNICODE), $total ]); ?>score_json 字段单独存了一份各维度的明细,而不是只存总分。这个设计在后续扩展时非常有用:后台可以基于 score_json 做雷达图,或者按单一维度筛选人群,不用重新跑到线下算分。
落库之后,result.php 再拿 $total 去和阈值数组比较,落在哪个区间就显示对应的报告文案。整套流程到这里就闭环了:出题、答题、计分、存库、出报告。
3. 部署与二次开发:把示例量表换成你自己的题
理论上 v1.0 的部署步骤就是解压、导入 SQL、改数据库配置三步。但按我的经验,别太依赖 install 向导,那个向导在部分服务器上会因目录权限问题卡住,手动建库导 SQL 反而更可控。目标环境建议直接用集成环境(Apache + MySQL + PHP),跟生产环境一致性更好,后面换题库和调样式也方便调试。
3.1 建库导 SQL 与数据库配置
先创建数据库再导入初始数据,两个命令分别在命令行执行:
mysql -u root -p -e "CREATE DATABASE psych_test DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -u root -p psych_test < data/psych_test.sql第一句显式指定了 utf8mb4 字符集和排序规则,这是为了避免后面入库的中文变成问号。第二句把 data/psych_test.sql 里的表结构和示例题目导入 psych_test 库。如果库里已经有旧数据,重新导入前最好先 DROP DATABASE 再建一次,v1.0 的 SQL 脚本没有做清空保护,直接在旧表上执行可能报重复键错误。
然后修改 inc/config.php 里的连接参数:
<?php define('DB_HOST', '127.0.0.1'); define('DB_USER', 'root'); define('DB_PASS', '换成你的数据库密码'); define('DB_NAME', 'psych_test'); define('SITE_NAME', '心理测评中心'); define('TOTAL_QUESTIONS', 50); ?>四个 DB_ 开头的是常规连接信息,SITE_NAME 会出现在页面标题和结果页顶部,TOTAL_QUESTIONS 是全局题目上限,后面会多次用到。改完配置后先访问 install/check.php,看到所有检查项为绿色再继续。
提示:v1.0 的代码默认按 UTF-8 处理字符串,包括 json_encode 和文本比较。如果数据库字符集不一致,前端页面可能正常,但后台导出的记录会乱码。这个问题在第四章第二坑展开。
3.2 计分规则怎么改成自己的量表
v1.0 的计分规则不是“一题一分”,而是把题目归类到不同维度。你要改的核心参数就三处,都在 inc/score.php 里:
| 参数 | 位置 | 说明 |
|---|---|---|
| $dims | inc/score.php | 维度名、显示名、属于该维度的题号数组 |
| $reverse | inc/score.php | 反向计分题号,选 5 实际得 1 |
| $thresholds | inc/score.php | 总分区间对应的结果标题和建议文案 |
阈值数组的写法如下:
<?php $thresholds = [ ['min' => 0, 'max' => 39, 'title' => '状态平稳', 'advice' => '维持现有节奏,保持规律作息'], ['min' => 40, 'max' => 59, 'title' => '中等压力', 'advice' => '适当评估近期负荷,预留休息时间'], ['min' => 60, 'max' => 300, 'title' => '高压状态', 'advice' => '建议进一步关注身心状态'], ]; ?>这段很好理解:总分落在哪个 min 到 max 区间,就显示对应的 title 和 advice。v1.0 的阈值是线性区间,逻辑清晰但能力有限,只适合普通问卷和课程设计;如果要做严谨的心理测量量表,需要常模百分位或因子得分,那就要自己在 score.php 里扩一套算法,这个后面再讲。
3.3 题库替换与前端换皮
示例题目在 data/psych_test.sql 里,也可以直接在后台管理页面逐条加。我习惯直接写 SQL 批量替换,效率更高。题目表字段结构大概是这样的:
INSERT INTO questions (qid, dim_key, content, opt_a, opt_b, opt_c, opt_d, opt_e, is_reverse) VALUES (1, 'stress', '最近一个月,我经常觉得紧张', '完全不符', '较少', '中等', '较多', '完全符合', 0), (2, 'mood', '我很难让自己安静下来', '从不', '偶尔', '有时', '经常', '总是', 1);dim_key 必须和 score.php 里 $dims 的键名一致,否则计分时遍历不到这道题。is_reverse 为 1 表示反向计分,同时还要把题号加进 score.php 的 reverse 数组。opt_a 到 opt_e 是五个选项的显示文本,每道题可以不一样,前端会把这些字段原样渲染成单选按钮。
前端换皮更简单,v1.0 的样式集中在 assets/css/style.css,主色和圆角都定义在 :root 里的 CSS 变量上,改几个变量就能换一套视觉风格。站点标题已经在 config.php 里改过,不需要去每个页面找字符串替换。
4. 避坑:v1.0 部署与改造中四个高频翻车点
这套源码跑起来不难,但二次开发时容易踩几个老项目特有的雷。下面这四条是我实际部署中遇到过的,每个都按现象、原因、解决三个维度记录,直接照着排查能省不少时间。
4.1 坑一:PHP 7.4 以上打开首页白屏
现象:配置完成后访问 index.php,页面完全空白,服务端错误日志出现“Call to undefined function mysql_connect()”之类的记录。
原因:v1.0 的年代默认使用 mysql_ 前缀的扩展,PHP 7.0 淘汰了它,PHP 7.4 和 8.x 更是直接移除;另外 create_function 这类函数也在高版本被删除,老代码里用到的地方都会直接 fatal error。安装向导在这种情况下也跑不起来。
解决:把 inc/config.php 和所有数据库操作统一改为 PDO 或 mysqli。我一般保留 PDO,因为代码改动最小,按下面这个写法替换 mysql_connect 那一套:
<?php // 原来: mysql_connect(DB_HOST, DB_USER, DB_PASS); // 改为: $pdo = new PDO( 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4', DB_USER, DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] ); ?>改写之后,原先 mysql_query 的调用点全部用 $pdo->query 和 $pdo->prepare 替代。参数上要注意:PDO 构造函数的第一个参数里已经带上数据库名,连接后不用再执行 mysql_select_db;字符集通过 charset=utf8mb4 指定,避免后续中文乱码。
4.2 坑二:SQL 导入报 Unknown collation 或中文变问号
现象:导入 data/psych_test.sql 时报错,类似“Unknown collation: utf8mb4_0900_ai_ci”;或者导入成功,但后台看题目内容全部是问号。
原因:一种是 SQL 文件在高版本 MySQL 里导出,排序规则版本比当前数据库新,低版本不认识;另一种是建库时的字符集和连接字符集不一致,数据写入时被转成了错误编码。第三方工具导出的 SQL 还经常带 BOM 头,这个隐藏字符也会破坏建表语句。
解决:导入前在命令行里先执行 SET NAMES utf8mb4,再用 source 命令导入,顺序不要反:
mysql -u root -p SET NAMES utf8mb4; source data/psych_test.sql;同时确认三处字符集一致:建库语句里的 DEFAULT CHARACTER SET、SQL 文件头部声明的字符集、config.php 里 PDO 的 charset 参数。如果文件是编辑器转存的,另存为 UTF-8 无 BOM 格式再导入,我遇到过一次乱码就是 BOM 引起的。
4.3 坑三:题目增加到第 51 题后,最后一题答案被吞
现象:把题库扩到 50 题以上,测试者反馈答完最后一题提交后,总分和后台记录都少了一题的数据。
原因:v1.0 把题目上限写死在了三个地方,config.php 里的 TOTAL_QUESTIONS 常量是 50,前端 assets/js/test.js 里遍历题目时也是 i <= 50,后台管理里的列表分页同样写死了最大页码。新增题目只改了数据库,其他两处没同步,翻页到第 51 题时前端校验直接拦截,压根不会发起提交。
解决:题目总数改为从数据库实时读取,统一只维护一个来源。在 config.php 里改为:
<?php $stmt = $pdo->query('SELECT COUNT(*) FROM questions'); define('TOTAL_QUESTIONS', (int)$stmt->fetchColumn()); ?>前端 test.js 里的总数从后端的常量动态渲染,不要在前端再写一个数字。这个坑的教训是:老源码里“写死”的常量一旦分布在多个文件,扩充功能时必须全局搜索一遍。
4.4 坑四:启用随机出题后,同一套答案每次算出来的结果不一样
现象:后台开了随机出题模式,测试者第一次答完得到“中等压力”,重新进入再随机一次,分数区间变了,甚至后台记录里出现了同一维度两种总分。
原因:v1.0 做随机出题时只打乱了显示顺序,但提交逻辑是按“当前页码”存 Session,不是按“题目 ID”存。用户看到的第 5 题每次可能是不同的题,Session 里第 5 个位置存的内容自然就错位了,计分必然混乱。
解决:出题顺序必须锁定到单个测试会话,并且 Session 的键在用题目 ID,而不是页码。参考这样的处理:
<?php // 首次进入时生成一次性题序并锁定 if (empty($_SESSION['question_order'])) { $allQids = array_keys($questions); shuffle($allQids); $_SESSION['question_order'] = $allQids; } $qid = $_SESSION['question_order'][$pageIndex]; ?>这样用户中途刷新、换浏览器半小时后再答,题目顺序和 Session 里存答案的键都不会乱。心理测评场景里,题目顺序属于测试条件的一部分,随意随机化会影响结果稳定性,这个设计必须谨慎处理。
5. 分卷与导出:让这套源码在真实场景里更耐用
v1.0 原版只支持一套量表从头答到尾,实际使用时往往一个站点要跑好几套问卷。我给它加了个分卷参数,用 URL 区分不同测试,省得复制多个目录。
<?php $volumes = [ 'stress' => ['table' => 'questions_stress', 'title' => '压力自评'], 'mood' => ['table' => 'questions_mood', 'title' => '情绪倾向'], ]; $vol = isset($_GET['vol']) ? $_GET['vol'] : 'stress'; if (!isset($volumes[$vol])) { $vol = 'stress'; // 白名单校验,不在列表内就回默认 } $activeTable = $volumes[$vol]['table']; ?>入口统一走 ?vol=stress 或 ?vol=mood,后端根据白名单选择题库表,前端标题也跟着切换。重要的是参数校验:绝对不能把 vol 的值直接拼进 SQL,必须是数组白名单里存在的键才放行,这是老项目改造时我最看重的一点。
导出功能也很实用,后台经常需要把测试记录拉成表格。新增一个 export.php,用 fputcsv 输出全部记录:
<?php header('Content-Type: text/csv; charset=UTF-8'); header('Content-Disposition: attachment; filename=results.csv'); echo "\xEF\xBB\xBF"; // UTF-8 BOM,防止 Excel 打开中文乱码 $fp = fopen('php://output', 'w'); fputcsv($fp, ['用户', '总分', '维度JSON', '测试时间']); foreach ($pdo->query('SELECT * FROM t_results ORDER BY create_time DESC') as $row) { fputcsv($fp, [$row['user_name'], $row['total'], $row['score_json'], $row['create_time']]); } fclose($fp); ?>注意导出前输出一个 UTF-8 的 BOM 头,否则 Excel 默认用本地编码打开,中文列名很容易乱。score_json 保留的是 JSON 格式的维度明细,如果想在表格里展示单维度,可以在 fputcsv 前 json_decode 拆列。
这套源码我前后帮人拆过三次,每次翻车点都集中在 PHP 版本、字符集和题目上限这三件事上。从那以后,我每次拿到这类源码都会强制走一遍完整流程:先看 install/check.php 的结果,再手动导 SQL,然后自己从头到尾答完一套题,最后去后台确认记录和计分明细都对得上。别跳过全流程验证直接上线,那是给自己留后悔药。希望帮到你。
本文还有配套的精品资源,点击获取