简介一套基于 PHP 与 MySQL 开发的学生报名系统源码 v2012主要面向中小学校、培训机构等需要自行组织报名与考务管理的场景。系统覆盖分区管理、学生信息登记报名号、姓名、性别、出生年月、毕业学校、身份证号、家庭住址、父母姓名、联系电话及摄像头拍照、考场座位号手动分配、准考证打印以及汇总数据并导出为 Excel 等完整流程。压缩包约 2.37MB包含程序目录与 MySQL 数据库文件数据库连接参数集中在 www/manager/config 下的 database.php 中修改并附有默认管理员账号 admin结构直观、代码量不大便于直接部署或二次开发。已有 518 人学习浏览对于想快速搭建报名场景、或参考学习 PHPMySQL 表单处理、图片采集与数据导出功能的开发者是一套具有实用价值的示例。1. 学生报名系统源码 v2012一个老项目为什么现在还值得翻出来做报名系统的朋友很多是来翻老代码的。学生报名系统源码 v2012 不是最新项目而是一类典型的 PHP MySQL 报名网站前台一个报名表单后台一套审核列表再加上导出就能撑起一个学期几千名学生的选课登记。它能解决的是“今天就要上线、又没有专职开发”的现实问题也适合课程设计、培训机构临时搭报名页。别急着打开压缩包就改先照着下面的方式把骨架读出来你会发现这比重写一套更省时间。这篇文章会从源码读法、部署、表结构、迁移坑到改造成导出系统都过一遍适合新手按步骤跑通也适合熟手拿来做二次开发底子。2. 先看懂 v2012 这套报名系统的源码骨架模块划分与数据流2.1 老报名系统的标配模块前台报名、后台审核、统计导出2012 年前后的 PHP 报名系统功能上不比现在简单只是没有前后端分离的概念。我拿到这套源码时不会先看每个文件而是先找入口把数据流画出来。典型结构是这样index.php展示报名表单表单提交到submit.phpsubmit.php做必填校验和重复报名检查通过后写入 MySQL管理员访问admin/login.php登录登录状态写入 sessionadmin/list.php拉出报名记录并按状态筛选最后admin/export.php把数据导出成 Excel 或 CSV。为什么 v2012 这类学生报名系统源码大多选 PHP MySQL而不是 ASP.NET 或 JSP因为 2012 年国内虚拟主机市场以 PHP 为主部署门槛低MySQL 随买随装学校服务器也能轻松跑起来。现在回看这套结构最大的优点是逻辑直白一个文件干一件事新手跟着submit.php往下一行一行看很快就能改。缺点是过程式代码里的数据库操作、防重复校验、鉴权都堆在一起后续往 PHP 7 迁移时需要逐个处理。理解这条数据流之后后面所有部署和排错都围绕它展开。2.2 源码目录怎么读从入口文件到配置文件先在终端里扫一遍目录我习惯用tree或者ls -R。常见的 v2012 风格源码解压后会长成这样student_signup_v2012/ ├── index.php # 前台报名入口展示表单 ├── submit.php # 接收报名数据的处理脚本 ├── config.php # 数据库连接与全局参数 ├── sql/ │ └── signup.sql # 建库建表脚本 ├── static/ │ ├── css/ │ └── js/ └── admin/ ├── login.php # 后台登录页 ├── check_login.php # 校验管理员账号 ├── list.php # 报名列表 ├── audit.php # 审核通过/拒绝 └── export.php # 导出报名数据这个目录本身就在告诉你业务边界根目录是前台admin是后台。读代码时我会先打开config.php因为它决定了你能不能连上数据库。老源码里的配置往往长这样?php // config.php —— 老源码通常把数据库和系统参数放在一起 define(DB_HOST, localhost); define(DB_USER, root); define(DB_PASS, 123456); define(DB_NAME, student_signup); date_default_timezone_set(Asia/Shanghai); $conn mysql_connect(DB_HOST, DB_USER, DB_PASS) or die(数据库连接失败: . mysql_error()); mysql_select_db(DB_NAME, $conn); mysql_query(SET NAMES utf8, $conn); ?这里有两个关键点。第一mysql_connect是 PHP 5 时代的函数PHP 7 里已经移除所以后面部署时环境版本一定要选对第二SET NAMES utf8决定了页面和数据库之间的字符集如果这段被改成 gbk导出的 CSV 在 Excel 里打开就会乱码。我一般会把DB_HOST、DB_USER、DB_PASS、DB_NAME四个值先抄出来再去sql/signup.sql里确认库名是否一致不一致时优先以config.php为准改数据库名因为源码里的查询语句往往直接写死了表名。3. 把 v2012 跑起来本地环境搭建与最小部署步骤3.1 环境选择为什么我建议用 PHP 5.6 Apache而不是最新 PHP 8老源码最怕的不是业务逻辑复杂而是黑匣子式报错。v2012 这类代码的核心依赖是mysql_connect系列函数这套函数在 PHP 7 里被彻底移除。如果你拿一台装了 PHP 8 的服务器直接跑页面大概率白屏错误日志里只写一句Call to undefined function mysql_connect()。所以本地复现的第一原则是不要追求新版而是尽量复刻当年的运行环境。我常用的方案是 XAMPP 的 PHP 5.6 版本它把 Apache、MySQL、PHP 打包在一起启动面板里点两个按钮就能跑起来。对比一下版本差异环境mysql_* 函数老代码兼容性建议PHP 5.4 / 5.6支持已废弃但可用最好本地复现首选PHP 7.0移除差需改代码不推荐直接跑PHP 8.x移除更差还改了类型语法迁移成本高Nginx PHP-FPM取决于 PHP 版本需配伪静态可以跑但没必要为什么不用 Nginx因为 v2012 这类老代码几乎没有伪静态规则index.php?actiondetail这种 URL 在 Nginx 下默认就是对的但老代码里偶尔会用$_GET[id]不需要 rewrite。真正的问题是 Nginx 默认不解析 PHP还要多配一层 fastcgiXAMPP 的 Apache 把这事省了。在 Windows 上直接开 XAMPP Control Panel在 Linux 上用/opt/lampp/lampp start就这么简单。3.2 本地跑通的最小步骤导入数据库、改配置、启动先把源码解压到 Web 根目录。假设压缩包解压后目录叫student_signup路径是htdocs/student_signup。然后在命令行里进入目录确认文件结构完整cd /path/to/xampp/htdocs/student_signup ls -la这一步是为了确认config.php、sql/signup.sql、index.php都在。如果少了sql目录说明网上下载的源码可能故意去掉了 sql 文件通常要去admin/install.php在线安装或者自己按第四章的表结构建。接着启动 MySQL并导入建表脚本mysql -u root -p sql/signup.sql导入完成后再确认表有没有建成功mysql -u root -p -e USE student_signup; SHOW TABLES;正常会看到course、student_info两个表。这里要注意老代码里的表前缀如果不一致比如带v2012_前缀那么signup.sql和代码里的 SQL 也要对应否则后面前台报名一定会报“Table not found”。导入成功后改config.php里的数据库账号。XAMPP 里 MySQL 的默认账号是root密码取决于你安装时设置的值// 打开 config.php重点修改这四个 define define(DB_HOST, localhost); // 本机运行不要改成 127.0.0.1老连接库可能不认 define(DB_USER, root); // XAMPP 默认是 root define(DB_PASS, 你自己的密码); // 留空也可以但建议设置 define(DB_NAME, student_signup); // 要和导入的库名一致紧接着启动 Apache 和 MySQL。XAMPP 图形界面下直接点 Apache 和 MySQL 右边的 StartLinux 下执行/opt/lampp/lampp start启动后先用curl探一下入口看是否返回 200curl -I http://localhost/student_signup/index.php返回HTTP/1.1 200 OK说明 PHP 环境正常如果返回 404优先检查源码目录名和 URL 是否一致如果返回 500打开 XAMPP 的 Apache 错误日志最常见的是mysql_connect报错那说明你系统里的 PHP 不是 5.6而是 7 或 8需要回到 3.1 重装环境。到这里v2012 报名系统就已经能打开前台页面了。后台地址是http://localhost/student_signup/admin/login.php默认账号密码通常写在check_login.php里但为了安全建议先登录进去改掉这个坑在第五章专门讲。4. 报名表设计与人库字段取舍v2012 的字段范式4.1 报名主表与扩展字段表老系统的设计逻辑很多新手看到源码里的 SQL 脚本只有几张表会觉得“这不正规”但老系统的设计逻辑恰恰是“够用就好”。v2012 这类源码一般不会设计十几张表核心就两张课程表和报名学生表。课程表存有哪些班、班容量多少报名表存学生信息、报读课程、审核状态。下面这条建表语句是从老系统里提炼出来的常见写法也适合你自己重建一份signup.sqlCREATE TABLE course ( id INT(11) NOT NULL AUTO_INCREMENT, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, quota INT(11) NOT NULL DEFAULT 30 COMMENT 班容量, start_date DATE DEFAULT NULL COMMENT 开课日期, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT课程表; CREATE TABLE student_info ( id INT(11) NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 学员姓名, gender ENUM(男,女) DEFAULT 男, birthday DATE DEFAULT NULL COMMENT 出生日期, phone VARCHAR(20) NOT NULL COMMENT 手机号, course_id INT(11) NOT NULL COMMENT 报读课程ID, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, status TINYINT(4) NOT NULL DEFAULT 0 COMMENT 0待审 1通过 2拒绝, create_time DATETIME DEFAULT NULL COMMENT 报名时间, PRIMARY KEY (id), KEY idx_status (status), UNIQUE KEY uk_phone_course (phone, course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT报名学生表;为什么create_time默认值留空而不是写成DEFAULT CURRENT_TIMESTAMP因为老 MySQL 5.5 对 DATETIME 的默认值支持不统一写成 DEFAULT NULL让插入语句显式写NOW()反而更兼容。uk_phone_course这个唯一键是防重复报名的核心它允许同一个手机号报不同课程但不允许同一个手机号反复报同一门课。status字段用 TINYINT 而不是 VARCHAR 存“待审核/已通过/已拒绝”是因为后续筛选排序更快代码里对数字做判断也简洁。4.2 必填校验与防重复报名代码写法与参数看懂了表再来看submit.php里最常见的核心逻辑。老源码的过程式写法虽然丑但每一步都能看清楚。我一般会把必填校验、格式校验、课程校验、重复报名校验、插入语句这五步顺序读下来?php // submit.php —— 报名提交核心v2012 常见过程式写法 require_once inc/conn.php; $name trim($_POST[name] ?? ); $phone trim($_POST[phone] ?? ); $gender ($_POST[gender] ?? ) 女 ? 女 : 男; $birthday trim($_POST[birthday] ?? ); $remark trim($_POST[remark] ?? ); $course_id intval($_POST[course_id] ?? 0); if ($name || $phone ) { exit(姓名和手机号不能为空); } if (!preg_match(/^1[3-9][0-9]{9}$/, $phone)) { exit(手机号格式不正确); } // 检查课程是否存在intval 防注入 $course_check mysql_query(SELECT id FROM course WHERE id . $course_id); if (mysql_num_rows($course_check) 0) { exit(请选择有效课程); } // 防重复同一课程同一手机号只允许报名一次 $check mysql_query( SELECT id FROM student_info . WHERE phone . mysql_real_escape_string($phone) . . AND course_id . $course_id ); if (mysql_num_rows($check) 0) { exit(该手机号已报名该课程勿重复提交); } $sql INSERT INTO student_info (name, gender, birthday, phone, course_id, remark, status, create_time) . VALUES ( . mysql_real_escape_string($name) . , . $gender . , . ($birthday ! ? . $birthday . : NULL) . , . mysql_real_escape_string($phone) . , . $course_id . , . mysql_real_escape_string($remark) . , 0, NOW()); mysql_query($sql) or die(报名失败 . mysql_error()); echo 报名成功请等待工作人员审核; ?这里有几个参数值得细说。intval把course_id强制转成整数这一步能在源头把大部分 SQL 注入堵死因为哪怕原来传进来是字符串转完后也只能是数字。mysql_real_escape_string是对字符串类型字段做转义不要用已经废弃的addslashes替代因为后者不知道数据库连接的字符集容易造成乱码或多出来反斜杠。birthday为空时要显式拼NULL而不是空字符串否则DATE字段会存成0000-00-00后面查询和 Excel 导出都会出问题。最后NOW()是让 MySQL 数据库生成时间避免 PHP 的date()受时区影响。这是老代码里很聪明的做法也直接规避了 2038 年问题——因为字段是 DATETIME不是 INT 时间戳。5. 避坑/常见问题把 v2012 迁到新环境最容易踩的 5 个坑5.1 PHP 7 下 mysql_* 函数报错页面直接白屏现象用 PHP 7 或 8 打开index.php一片空白Apache 错误日志里写PHP Fatal error: Call to undefined function mysql_connect()。 原因PHP 7 正式移除了ext/mysql扩展老代码里的mysql_connect、mysql_query、mysql_fetch_array全部失效。 解决最简单的是换回 PHP 5.6 环境跑不要和官方文档犟。如果必须迁移到 PHP 7可以写一个兼容函数层把老函数映射到mysqli上?php // compat.php —— PHP 7 迁移垫片放到 config.php 之前 require if (!function_exists(mysql_connect)) { function mysql_connect($host, $user, $pass) { return new mysqli($host, $user, $pass); } function mysql_select_db($db, $conn) { return $conn-select_db($db); } function mysql_query($sql, $conn null) { global $conn; return ($conn ?? $GLOBALS[conn])-query($sql); } function mysql_num_rows($rs) { return $rs ? $rs-num_rows : 0; } function mysql_fetch_array($rs) { return $rs ? $rs-fetch_array(MYSQLI_ASSOC) : false; } function mysql_real_escape_string($s) { global $conn; return $conn-real_escape_string($s); } } ?这个垫片能救急但要注意mysql_fetch_array老默认返回数字关联混合数组而这里强制MYSQLI_ASSOC极端情况下页面里用$row[0]取值的代码会失效。我的建议是本地复现直接用 PHP 5.6垫片只留给确实没法换环境的线上服务器。5.2 MySQL 8 连接报认证协议错误现象数据库账号密码都正确但mysql_connect时报Client does not support authentication protocol requested by server。 原因MySQL 8.0 默认使用caching_sha2_password认证插件而 PHP 5.6 的libmysql不认识这种新协议。 解决要么把数据库换成 MySQL 5.7要么把老账号改成mysql_native_password。在 MySQL 命令行里执行ALTER USER root% IDENTIFIED WITH mysql_native_password BY 123456; FLUSH PRIVILEGES;注意这里的root%要和你config.php里的DB_HOST对应。如果DB_HOST是localhost那 MySQL 里可能建的是rootlocalhost两条都要看一遍否则改完还是报错。这个坑我当年查了半小时血泪经验。5.3 后台和导出文件中文乱码现象前台提交的中文姓名后台list.php显示成“锟斤拷”导出 CSV 用 Excel 打开全是乱码。 原因字符集不统一。老源码页面可能是 GBK数据库表是 utf8连接时又执行了SET NAMES gbk三处不一致必然乱码。 解决全部收敛到 UTF-8。先改config.php里的连接字符集再在每个输出页面头部加header// config.php 中 mysql_query(SET NAMES utf8, $conn); // 每个需要输出的 php 文件开头 header(Content-Type: text/html; charsetutf-8);导出 CSV 时还有一个隐藏细节Excel 打开 UTF-8 编码的 CSV 会乱码必须输出 BOM 头。只加header不够要在输出第一行前打印 BOMecho \xEF\xBB\xBF; // UTF-8 BOMExcel 认这个这属于那种“代码逻辑没问题但就是乱码”的玄学问题本质是编码规范。修完之后前台、后台、导出三处必须全部一致只改一处等于没改。5.4 报名时间变成 0000-00-00 或比北京时间早 8 小时现象后台列表里的create_time显示0000-00-00 00:00:00或者报名时间比实际时间晚 8 小时。 原因插入时用了 PHP 的date(Y-m-d H:i:s)但php.ini里没有设置时区默认 UTC 时间就比北京时间慢 8 小时如果存的是空字符串MySQL 严格模式会转成0000-00-00。 解决在config.php里设置时区并且把插入语句改成用 MySQL 的NOW()date_default_timezone_set(Asia/Shanghai);INSERT INTO student_info (..., create_time) VALUES (..., NOW());这里还要提醒一句老项目如果create_time字段是INT存 UNIX 时间戳时间一旦过了 2038 年 1 月 19 日就会溢出。v2012 这个年代的系统有不少这么写的遇到这种表结构建议直接把字段改成 DATETIME再把老数据用FROM_UNIXTIME转一遍趁早用 NOW() 收尾。5.5 后台地址暴露和默认弱口令现象直接访问/admin/list.php也能看到报名名单后台账号密码是admin/123456公网部署后放着不管。 原因老源码为了演示方便把默认密码写死在代码里而且有些页面的登录校验只写在list.php内部但list.php可能被直接访问绕过。 解决第一步改默认密码。老代码里常用md5加密用md5($pass)比对但因为这个算法太容易撞库至少要加盐if (md5(v2012_salt_ . $_POST[pass]) $admin_hash) { $_SESSION[admin_login] 1; }第二步在所有后台页面顶部补上统一校验而不是只靠跳转。常见做法是在每个 php 文件开头加这几行session_start(); if (empty($_SESSION[admin_login])) { header(Location: login.php); exit; }第三步如果站点只给内部使用把后台目录用 Apache 的Directory配置限制 IP或者干脆给admin目录改名。老系统本身没有复杂的权限体系能做到这三步就比 90% 还挂着默认密码的存量站点要安全。6. 验证报名流程与二次开发技巧把 v2012 改成能用的系统6.1 用一条测试数据跑通报名-审核-导出全流程拿到手先别急着改界面先花五分钟跑通主流程。在 MySQL 里手动插一条测试数据比在网页表单里反复点提交更可控INSERT INTO student_info (name, gender, birthday, phone, course_id, remark, status, create_time) VALUES (测试生, 女, 2012-05-01, 13900000001, 1, 自动化验证, 0, NOW()); SELECT id, name, status, create_time FROM student_info WHERE phone 13900000001;看到返回id数字说明表结构没问题。然后去浏览器里访问admin/login.php登进去在列表中找到这条记录点“通过”按钮刷新后再查一次状态变成了 1就说明审核功能没坏。最后再点导出按钮打开 CSV 看中文不是乱码这套 v2012 源码就可以正式投入使用了。6.2 给 v2012 加一个不依赖 PHPExcel 的最简导出很多老源码的导出是用 PHPExcel 库但这个库太重了老环境跑起来容易超内存。与其折腾不如直接用 PHP 内置的fputcsv输出 CSV200 行以内代码就能搞定。改到admin/export.php里替换原来繁琐的 HTML 表格打印逻辑?php // admin/export.php —— 极简 CSV 导出 require_once ../inc/conn.php; session_start(); if (empty($_SESSION[admin_login])) { header(Location: login.php); exit; } header(Content-Type: text/csv; charsetutf-8); header(Content-Disposition: attachment; filenamesignup_ . date(Ymd) . .csv); echo \xEF\xBB\xBF; // UTF-8 BOM防止 Excel 乱码 $out fopen(php://output, w); fputcsv($out, [ID, 姓名, 手机号, 课程ID, 报名时间]); $rs mysql_query(SELECT id, name, phone, course_id, create_time FROM student_info ORDER BY id DESC); while ($row mysql_fetch_array($rs)) { fputcsv($out, $row); } fclose($out); ?fputcsv会自动处理引号和逗号比手工拼接字符串安全得多。如果后续要加筛选只需要在SELECT语句后面拼WHERE status1之类的条件再把筛选值从 URL 参数里读出来拼进去注意用intval兜底就行。我自己的习惯是拿到 v2012 先不急着改界面先把这条主流程跑通确认没有乱码和鉴权问题再动导出和统计。这个顺序帮我避掉了很多返工希望帮到你。本文还有配套的精品资源点击获取