PHP原生学生管理系统:权限分层与CSV导入导出实战
2026/9/15 16:10:28 网站建设 项目流程

简介:这是一套基于PHP与MySQL开发的学生成绩管理系统源码,面向Web开发初学者及课程设计实践者,帮助理解前后端交互、数据库操作与基础权限管理逻辑。资源共33个文件,包含6个核心PHP脚本(如login.php、database.php)、4个CSS样式文件、4个JavaScript交互脚本、14张界面图标PNG及1个SQL建库文件db.sql,辅以README.md说明文档和LICENSE协议,整体包体积仅2.63MB,轻量易部署。已有4363人学习下载,体现其在教学实践中的广泛参考价值。读者可直接运行系统体验学生/教师双角色登录(默认账号密码明确标注),深入分析原生HTML5+CSS3前端结构、MySQL8兼容性适配细节,以及未封装但具教学意义的数据库连接与CRUD实现逻辑,适合用于PHP入门项目拆解与Web全栈开发实训。

1. 这不是又一个“PHP+MySQL学生管理系统”演示项目,而是一份可直接跑通、带完整权限分层与 CSV 导出能力的生产级教学骨架

你可能已经见过几十个叫“学生成绩管理系统”的 PHP 项目——它们大多停在登录页就断了,数据库字段名和注释对不上,教师端压根没实现,或者连mysqli_connect()都没加错误处理。但这个StuScoreSystem-main不同:它用纯原生 HTML/CSS/JS 实现了学生查分、教师录分、管理员批量导入(CSV)、按班级/科目/分数段筛选、排序图标联动、二维码分享成绩页,甚至保留了dataRoot.phpdataStu.php的双数据访问层设计。它不依赖任何框架,却通过database.php封装了连接池式 MySQLi 初始化,用root.jsindex.js分离了权限逻辑与展示逻辑。适合刚学完 PHP 基础语法、正卡在“怎么把表单提交到数据库”环节的开发者;也适合需要快速交付一个轻量级教务后台的中小学校信息员——只要改三处密码、导入一次 SQL、启动本地 PHP 服务,就能立刻看到带真实交互的界面,而不是对着echo "Hello World"发呆。


2. 从零部署:修改 database.php、导入 db.sql、启动服务三步闭环

2.1 修改 database.php 中的 MySQL 凭据并理解连接封装逻辑

database.php是整个系统数据库访问的唯一入口,其核心不是简单地new mysqli(),而是做了连接复用与错误兜底。打开该文件,定位到第 12 行左右:

$pw = 'fish';

注意:此处'fish'是硬编码密码,必须替换为你本地 MySQL 用户(通常是root)的实际密码。若你使用非 root 用户,请同步修改第 10 行的$user = 'root';

再往下看,关键逻辑在getDBConnection()函数中:

function getDBConnection() { static $conn = null; if ($conn === null) { $conn = new mysqli('localhost', 'root', $pw, 'stu_score_db', 3306); if ($conn->connect_error) { die("连接失败: " . $conn->connect_error . " (errno: " . $conn->connect_errno . ")"); } $conn->set_charset("utf8mb4"); } return $conn; }
  • static $conn = null实现了单例式连接复用,避免每次查询都新建连接;
  • utf8mb4字符集设置是必须的,否则中文姓名或科目名(如“数学Ⅱ”)可能乱码;
  • die()输出带errno的错误码,比静默失败更利于排错——比如errno: 1045表示认证失败,errno: 1049表示数据库不存在。

2.2 执行 db.sql 创建数据库与表结构,并验证字符集

db.sql文件包含建库、建表、插入默认用户三条语句。用命令行执行(推荐):

mysql -u root -p < db.sql

输入密码后,应无报错输出。随后验证数据库是否创建成功且字符集正确:

SHOW DATABASES LIKE 'stu_score_db'; USE stu_score_db; SHOW CREATE TABLE students;

预期结果中CREATE TABLE语句末尾应含DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci。若显示latin1,说明导入时未指定字符集,需重新执行:

mysql -u root -p --default-character-set=utf8mb4 < db.sql

提示:MySQL 8.0 默认排序规则为utf8mb4_0900_ai_ci,若你的环境是 MySQL 5.7,需将db.sql中所有COLLATE=utf8mb4_0900_ai_ci替换为COLLATE=utf8mb4_unicode_ci,否则建表会失败。

2.3 启动 PHP 内置服务器并确认路由可达

不要依赖老旧的 PHPStudy 或 XAMPP——PHP 7.4+ 自带php -S命令即可启动开发服务器。进入解压后的项目根目录(含login.php的目录),执行:

php -S localhost:8000 -t .

此时访问http://localhost:8000/login.php应显示登录页。若报错Failed to open stream: No such file or directory,检查当前目录是否正确;若页面空白,查看终端输出的 PHP 错误(如Parse error),常见原因是 PHP 版本低于 8.1(项目明确要求 PHP ≥ 8.1.3)。

验证服务是否真正加载了路由:在浏览器开发者工具 Network 标签页中,刷新登录页,观察login.csslogin.js是否返回 200 状态码。若 CSS/JS 404,说明-t .参数未指向静态资源所在目录——此时需改为php -S localhost:8000 -t ./或明确指定router.php


3. 权限体系解析:学生/教师/管理员三级角色如何通过 session 与 URL 路径隔离

3.1 登录态校验逻辑分布在 login.php、root.php、index.php 三处

系统未使用统一中间件,而是通过分散校验实现权限控制。以login.php为例,其核心验证代码在第 45 行附近:

if ($row['role'] == 'student') { $_SESSION['user_id'] = $row['id']; $_SESSION['role'] = 'student'; header("Location: index.php"); exit; } elseif ($row['role'] == 'teacher') { $_SESSION['user_id'] = $row['id']; $_SESSION['role'] = 'teacher'; header("Location: root.php"); exit; } else { $_SESSION['user_id'] = $row['id']; $_SESSION['role'] = 'admin'; header("Location: root.php"); exit; }
  • 学生跳转至index.php(仅查看自己成绩);
  • 教师与管理员均跳转至root.php,但后续功能由root.php中的$_SESSION['role']判断分支;
  • exit必须存在,否则header()重定向后仍会继续执行后续 PHP 代码,造成安全漏洞。

3.2 root.php 中的权限路由分发与功能开关

root.php是教师与管理员的统一入口,其顶部有明确的角色判断:

if ($_SESSION['role'] !== 'teacher' && $_SESSION['role'] !== 'admin') { header("Location: login.php"); exit; }

随后根据 GET 参数?action=加载不同功能模块:

action 值对应功能教师可见管理员可见关键限制
score_input录入/修改学生成绩仅能操作所教班级
batch_importCSV 批量导入仅管理员可访问
user_manage用户增删改教师无此链接

查看root.php源码,你会发现score_input模块中有一段关键 SQL 查询:

$sql = "SELECT * FROM students WHERE class_id = ? AND subject = ?"; $stmt = $conn->prepare($sql); $stmt->bind_param("is", $_SESSION['class_id'], $subject);
  • $_SESSION['class_id']在登录时由dataRoot.php查询并写入 session,确保教师只能看到自己任教班级的学生;
  • bind_param("is", ...)使用预处理语句防止 SQL 注入,这是项目中少有的安全实践亮点。

3.3 index.php 的学生侧数据隔离与前端渲染逻辑

学生登录后进入index.php,其数据获取逻辑在dataStu.php中:

$sql = "SELECT s.*, sub.name as subject_name FROM scores s JOIN subjects sub ON s.subject_id = sub.id WHERE s.student_id = ? ORDER BY s.score DESC"; $stmt = $conn->prepare($sql); $stmt->bind_param("i", $_SESSION['user_id']);
  • WHERE s.student_id = ?严格绑定当前登录学生的 ID,杜绝越权查看他人成绩;
  • ORDER BY s.score DESC实现默认按分数降序排列,配合前端sort_up.png/sort_down.png图标实现点击排序;
  • 前端 JS 通过fetch('dataStu.php')获取 JSON 数据,再用innerHTML渲染表格——虽未用 Vue/React,但 DOM 操作逻辑清晰,适合初学者模仿。

4. CSV 批量导入与导出:从 db.sql 结构到 dataRoot.php 的文件解析全流程

4.1 db.sql 中的 students 与 scores 表结构决定 CSV 格式

db.sql定义了两个核心表:

CREATE TABLE `students` ( `id` int NOT NULL AUTO_INCREMENT, `student_id` varchar(20) NOT NULL COMMENT '学号', `name` varchar(50) NOT NULL COMMENT '姓名', `class_id` int NOT NULL COMMENT '班级ID', PRIMARY KEY (`id`) ); CREATE TABLE `scores` ( `id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL COMMENT '学生ID', `subject_id` int NOT NULL COMMENT '科目ID', `score` decimal(5,2) NOT NULL COMMENT '分数', PRIMARY KEY (`id`) );

因此,合法的stuScore.csv必须满足:

  • 第一行是表头:student_id,name,class_id,subject_name,score
  • subject_name是科目名称(如“数学”),程序会在dataRoot.php中通过SELECT id FROM subjects WHERE name = ?查找对应subject_id
  • class_id必须是数据库中已存在的班级 ID,否则导入失败

4.2 dataRoot.php 中的 CSV 解析函数 parseCSV() 详解

dataRoot.php第 120 行起定义了parseCSV()函数,其核心步骤如下:

function parseCSV($file_path) { $handle = fopen($file_path, "r"); $header = fgetcsv($handle); // 读取第一行表头 $data = []; while (($row = fgetcsv($handle)) !== false) { $data[] = array_combine($header, $row); } fclose($handle); return $data; }
  • fgetcsv()自动处理逗号分隔与引号包裹(如"张三","高一(3)班","数学","95.5"),无需手动explode(',')
  • array_combine($header, $row)将每行数据转为关联数组,例如['student_id'=>'2114100328', 'name'=>'李四', ...],便于后续字段映射;
  • 函数返回后,调用方batch_import.php会对每条记录执行INSERT INTO scores (...) VALUES (...),并捕获mysqli_error()记录失败行号。

4.3 导出 CSV 的实现:header() 强制下载与 UTF-8 BOM 处理

学生端无导出功能,但管理员可在root.php?action=export触发导出。关键代码在dataRoot.phpexportScores()函数:

header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="scores_export_' . date('Y-m-d') . '.csv"'); echo "\xEF\xBB\xBF"; // UTF-8 BOM,解决 Excel 打开中文乱码 $output = fopen('php://output', 'w'); fputcsv($output, ['学号', '姓名', '科目', '分数', '录入时间']); foreach ($records as $row) { fputcsv($output, [$row['student_id'], $row['name'], $row['subject_name'], $row['score'], $row['created_at']]); } fclose($output);
  • \xEF\xBB\xBF是 UTF-8 BOM 字节序列,Windows Excel 依赖它识别 UTF-8 编码;
  • fopen('php://output', 'w')直接将 CSV 写入 HTTP 响应体,避免临时文件;
  • fputcsv()自动转义逗号、换行符,比拼接字符串更安全。

5. 排错实战:5 类高频问题与对应日志定位方法

5.1 “登录失败:用户名或密码错误”但凭据确认无误

最常见原因是db.sql中的users表未正确插入默认用户。执行以下 SQL 验证:

SELECT username, password, role, class_id FROM users WHERE username IN ('2114100328', 'teacheroot');

预期结果:

  • 2114100328password字段值应为sha256('2114100328')的哈希值(项目使用 SHA256 加密);
  • teacherootpassword应为sha256('fishroot')
  • class_idteacheroot必须为NULL(教师无班级归属),否则root.php中的班级过滤会失效。

若哈希值不符,手动更新:

UPDATE users SET password = SHA2('2114100328', 256) WHERE username = '2114100328'; UPDATE users SET password = SHA2('fishroot', 256) WHERE username = 'teacheroot';

5.2 页面显示“Fatal error: Uncaught mysqli_sql_exception”

此类错误通常源于 MySQL 8.0 的密码认证插件变更。执行以下命令修复:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES;

然后重启 MySQL 服务。若使用 Docker,需在my.cnf中添加:

[mysqld] default_authentication_plugin=mysql_native_password

5.3 CSV 导入后部分学生分数为空

检查stuScore.csv是否存在空行或字段数不匹配。用wc -l stuScore.csv查看总行数,再用head -n 5 stuScore.csv查看前五行。若发现某行字段数少于表头数(如漏写score),fgetcsv()会返回false,导致array_combine()报错。解决方案:用 Excel 打开 CSV,删除空行,保存为“CSV UTF-8”格式。

5.4 排序图标点击无反应,Network 中 dataStu.php 返回 500

打开浏览器开发者工具,切换到 Console 标签页,查看 JS 错误。常见原因是index.js第 87 行的fetch()请求路径错误:

fetch('dataStu.php?sort=' + sortField + '&order=' + sortOrder)

若项目部署在子目录(如http://localhost/project/login.php),需将路径改为相对路径./dataStu.php或绝对路径/project/dataStu.php

5.5 成绩页面显示 NaN 或空白分数

检查scores表中score字段是否为NULL。执行:

SELECT * FROM scores WHERE score IS NULL LIMIT 5;

若存在,说明录入时未校验必填项。修复方法:在root.php?action=score_input的表单提交逻辑中,增加 PHP 层校验:

if (empty($_POST['score']) || !is_numeric($_POST['score'])) { $_SESSION['error'] = "分数必须为数字"; header("Location: root.php?action=score_input"); exit; }

提示:所有用户输入必须同时做前端 JS 校验(index.js中已有)和后端 PHP 校验,二者缺一不可。


6. 进阶技巧:用 PHP 内置函数实现成绩统计图表与 PDF 导出

6.1 用 imagecreate() 生成柱状图成绩单(无需 GD 库扩展)

项目未自带图表功能,但可利用 PHP 原生 GD 函数快速生成 PNG 成绩图。在index.php底部添加:

<?php // 生成学生成绩柱状图 $width = 400; $height = 200; $im = imagecreate($width, $height); $bg = imagecolorallocate($im, 255, 255, 255); $bar_color = imagecolorallocate($im, 70, 130, 180); $border_color = imagecolorallocate($im, 0, 0, 0); // 假设 $scores 是从 dataStu.php 获取的成绩数组 $max_score = max(array_column($scores, 'score')); $scale = ($height - 40) / $max_score; foreach ($scores as $i => $s) { $x = 50 + $i * 80; $y = $height - 20 - ($s['score'] * $scale); imagefilledrectangle($im, $x, $y, $x + 50, $height - 20, $bar_color); imagerectangle($im, $x, $y, $x + 50, $height - 20, $border_color); imagestring($im, 2, $x + 10, $height - 15, $s['subject_name'], $border_color); } header('Content-Type: image/png'); imagepng($im); imagedestroy($im); exit; ?>

将此代码保存为chart.php,在index.php<img src="chart.php">即可显示动态图表。注意:需确保 PHP 编译时启用了 GD 支持(php -m | grep gd)。

6.2 用 TCPDF 库实现 PDF 成绩单导出(兼容 PHP 8.1)

下载 TCPDF(https://github.com/tecnickcom/tcpdf),解压后将tcpdf.phpconfig/目录放入项目lib/文件夹。在root.php中添加导出按钮,点击后调用:

require_once('lib/tcpdf/tcpdf.php'); $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); $pdf->AddPage(); $pdf->writeHTML('<h2>学生成绩单</h2><table border="1"><tr><th>科目</th><th>分数</th></tr>', true, false, true, false, ''); foreach ($scores as $s) { $pdf->writeHTML('<tr><td>' . $s['subject_name'] . '</td><td>' . $s['score'] . '</td></tr>', true, false, true, false, ''); } $pdf->Output('score_report.pdf', 'D');
  • true, false, true, false, ''参数控制 HTML 解析、自动分页、表格边框等行为;
  • Output('filename.pdf', 'D')'D'表示强制下载,'I'表示内联显示。

6.3 优化 MySQL 查询性能:为高频字段添加复合索引

当前scores表仅对id有主键索引,但教师按班级查分、学生按科目查分均为高频操作。执行以下 SQL 提升响应速度:

-- 为教师按班级+科目查分优化 CREATE INDEX idx_class_subject ON scores(class_id, subject_id); -- 为学生按科目查分优化 CREATE INDEX idx_student_subject ON scores(student_id, subject_id); -- 查看索引效果 EXPLAIN SELECT * FROM scores WHERE student_id = 123 AND subject_id = 5;

EXPLAIN输出中type应为refkey应显示刚创建的索引名,rows应远小于全表行数。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询