简介:这份PHP图书管理系统源代码是一套完整可运行的Web应用示例,适合PHP初学者、在校学生以及需要快速搭建图书管理场景的开发者参考,代码基于PHP与MySQL实现,涵盖用户注册登录、图书增删改查、分类搜索、借阅归还、权限控制等业务模块,并融入MVC分层、PDO安全查询、AJAX异步更新等实践,帮助读者理解Web应用的整体开发流程。压缩包共112个文件,以50个php脚本为核心,另含MySQL的frm/myd/myi数据表文件、JS/CSS前端资源、gif演示图片和doc使用说明,整体仅624KB,轻量易部署,资源自带数据库结构和初始数据,搭配说明文档即可在本地运行体验,特别适合作为课程设计或毕业设计的参照。目前已有6518人学习下载,这份源代码对于想系统理解PHP项目结构、数据库设计及Web交互逻辑的读者来说,是高性价比的实战参考,既能直接运行调试也能二次扩展,通过阅读源码可掌握增删改查、权限控制、数据检索等核心技巧,为后续更复杂的项目开发打下基础。
1. 项目概述与最终效果预览
1.1 这套代码能做什么
先说结论:这套PHP图书管理系统,是我在实际开发中整理的一套可运行的完整项目,不是那种只有几个页面、连数据库都连不上的教学残次品。它包含了一个图书管理系统最核心的模块——图书管理、读者管理、借书还书、续借、逾期处理、统计报表,以及配套的后台管理界面。
我用了最常见的PHP原生写法,数据库用的MySQL,前端简单用了Bootstrap布局。整套代码拿下来,传到PHP环境里,配好数据库就能跑起来,前后台功能都是通的。你在本地XAMPP、phpStudy或者服务器上都能部署,适用于课程设计、毕业设计参考,或者一个小型图书室的管理需求。
1.2 适合谁来用
- 正在做PHP课程设计或毕业设计的学生,需要一个完整可运行的参考项目,而不是零散片段
- 刚学完PHP基础,想看看一个完整项目是怎么组织代码、怎么连数据库、怎么做会话控制的初学者
- 需要给单位或社区图书室搭建简单管理系统的个人开发者,想拿一套现成的改改就用
这套代码我尽量保持了结构清晰、注释到位,关键函数和业务逻辑都有中文注释,你拿到手不管是直接部署还是二次开发,都能较快上手。下面我把项目的整体设计思路、每个模块的实现细节、部署步骤、常见问题排查全部分享出来,方便你真正理解这套代码,而不是只会复制粘贴。
2. 系统整体设计思路与目录结构
2.1 技术选型背后的考虑
有人可能会问,现在PHP框架那么多,Laravel、ThinkPHP一类的都很成熟,为什么还要用原生PHP写?我的回答是:这套系统的定位是教学和轻量级部署。原生PHP有几个明显的好处——部署门槛低,不需要Composer装依赖;代码直观,逻辑链路短,适合拿来理解业务系统的基本套路;环境兼容性好,随便一个支持PHP的虚拟主机都能跑。
当然,我也不是完全不用设计模式。代码里我做了简单的MVC分层,controller目录放业务逻辑,model目录放数据库操作,view目录放页面模板,公共函数和配置单独拆出来。这样做的目的是让你看清一个真实项目应该怎么组织,不至于所有代码堆在一个文件里。如果你后面要迁移到ThinkPHP或者其他框架,这种分层结构也能平滑对应过去。
2.2 目录结构与功能模块总览
这套系统的目录结构如下,重点看几个核心目录:
php-book-management/ ├── admin/ # 后台管理模块 │ ├── index.php # 后台登录入口(管理员) │ ├── dashboard.php # 后台首页面板 │ ├── book_list.php # 图书列表 │ ├── book_add.php # 新增图书 │ ├── book_edit.php # 编辑图书 │ ├── book_delete.php # 删除图书(AJAX处理) │ ├── reader_list.php # 读者列表 │ ├── borrow_list.php # 借阅记录列表 │ ├── borrow_add.php # 新增借阅(借书操作) │ ├── return_book.php # 还书操作 │ ├── renew_book.php # 续借操作 │ ├── statistics.php # 统计报表 │ └── admin_logout.php # 退出登录 ├── user/ # 前台读者模块 │ ├── login.php # 读者登录 │ ├── register.php # 读者注册 │ ├── index.php # 读者个人中心/借阅列表 │ └── logout.php # 读者退出 ├── includes/ # 公共组件 │ ├── config.php # 数据库配置等 │ ├── db.php # PDO数据库连接封装 │ ├── auth.php # 会话权限控制 │ ├── functions.php # 公共函数库 │ └── header.php / footer.php # 页面公共头尾 ├── assets/ # 静态资源(CSS/JS) ├── sql/ │ └── book_db.sql # 数据库初始化脚本 └── index.php # 系统入口,自动跳转到登录页功能模块上,我拆成了三个角色维度。管理员负责图书的新增、编辑、上下架,读者信息的审核与维护,借书、还书、续借、逾期处理,以及借阅数据统计。读者可以注册账号、登录个人中心、查询自己的借阅记录和逾期状态。系统层面包含完整的登录会话控制、权限隔离和借阅超期自动计算逻辑。
2.3 数据库设计要点
数据库是整个系统的地基。这个项目的数据库设计我重点考虑了表之间的关系,避免数据冗余和更新异常。一共设计了四张核心表:管理员表、读者表、图书表和借阅记录表。借阅记录表通过外键关联读者和图书,同时记录借出时间、应还时间和实际归还时间。这样设计的好处是,统计报表可以直接通过关联查询算出来,不需要额外维护冗余字段。
有一点我想特别说明,就是图书表里我加了一个status字段,用来标记图书是否被借出,而不是通过是否存在于借阅记录来判断。这么做是为了查询图书状态时不用连表,性能更好,避免每次在图书列表页都要跑一次子查询。代价是借书、还书时必须同步更新图书表和借阅记录表,这里需要用事务来保证一致性。
CREATE TABLE `books` ( `id` int(11) NOT NULL AUTO_INCREMENT, `book_name` varchar(100) NOT NULL COMMENT '书名', `author` varchar(50) DEFAULT NULL COMMENT '作者', `publisher` varchar(100) DEFAULT NULL COMMENT '出版社', `isbn` varchar(30) DEFAULT NULL COMMENT 'ISBN号', `total_count` int(11) NOT NULL DEFAULT 1 COMMENT '总库存', `available_count` int(11) NOT NULL DEFAULT 1 COMMENT '可借数量', `status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态 1=可借 0=不可借', `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='图书表';库存字段我拆了total_count和available_count,总库存和可借数量分开管理,避免把已借出的数量算错。后端在下单借阅时会先检查available_count,只有大于0才能借出,然后UPDATE语句里直接减1,同时用条件available_count > 0作为行锁的替代方案,防止并发时把库存扣成负数。这是小型系统里最常见的防超卖写法,虽然没有数据库锁那么严格,但简单有效。
3. 核心模块的实现细节与实操要点
3.1 数据库连接封装:为什么用PDO而不是mysqli
这是很多人容易忽略的地方。我见过太多课程设计代码直接用mysql_connect或者mysqli的面向过程写法,数据库查询直接拼字符串,消息一注入就全跪。这套系统我统一用了PDO,而且开启了预处理模式。
<?php // includes/db.php class Database { private static $instance = null; private $pdo; private function __construct() { $config = require __DIR__ . '/config.php'; $dsn = "mysql:host={$config['db_host']};dbname={$config['db_name']};charset=utf8mb4"; try { $this->pdo = new PDO($dsn, $config['db_user'], $config['db_pass'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false ]); } catch (PDOException $e) { die('数据库连接失败: ' . $e->getMessage()); } } public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } public function getPdo() { return $this->pdo; } public function query($sql, $params = []) { $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt; } public function fetchAll($sql, $params = []) { return $this->query($sql, $params)->fetchAll(); } public function fetchOne($sql, $params = []) { return $this->query($sql, $params)->fetch(); } }PDO的预处理模式在这里的实质作用是,SQL语句的结构和参数值被分开传送到数据库服务器,参数值不会被拼进SQL字符串里,所以SQL注入的核心手段在这里直接失效了。你可以做一个简单的测试:在搜索框里输入单引号加条件的语句,如果系统返回的是参数错误或者空结果而不是数据库报错,说明预处理生效了。
连接参数里的ERRMODE_EXCEPTION也很重要,它让数据库错误以异常形式抛出,方便在开发阶段快速定位SQL语句的问题。而ATTR_EMULATE_PREPARES => false是让MySQL真正使用原生的预处理协议,而不是PHP模拟拼接,这一步对类型安全和性能都有帮助。
3.2 会话管理与权限控制
图书管理系统有两个角色:管理员和读者。如果权限不隔离,读者登录后只要能猜到后台地址就能进去操作,那就全乱套了。这里我用了一个非常简单的auth.php来做统一拦截。
<?php // includes/auth.php session_start(); function is_admin_logged_in() { return isset($_SESSION['admin_id']) && !empty($_SESSION['admin_id']); } function is_reader_logged_in() { return isset($_SESSION['reader_id']) && !empty($_SESSION['reader_id']); } function require_admin() { if (!is_admin_logged_in()) { header('Location: index.php'); exit; } } function require_reader() { if (!is_reader_logged_in()) { header('Location: user/login.php'); exit; } }每个后台页面的第一行,我会先加载这个auth.php,然后调用require_admin(),如果未登录就直接跳走。这里有一个细节可能新手没注意到——跳转之后必须加exit,否则PHP会继续执行后面的代码,这在某些情况下会造成逻辑漏洞。
$_SESSION本身是存储在服务器端的文件里,客户端只拿到一个Session ID的Cookie,所以用户不能像修改Cookie那样直接篡改自己的身份信息。不过Session文件默认存储在临时目录里,如果服务器是多用户共享的虚拟主机,要注意Session目录的权限设置,最好显式修改session.save_path指向当前项目可写的私有目录,避免Session文件被同主机的其他用户读取。
3.3 图书借阅与还书的事务处理
借书逻辑看起来简单,实际上最容易出问题的地方在于数据一致性。我们想象一下这个场景:读者在前台点击借书,后台先去检查图书状态,如果可借就执行借出。如果在这个过程中,两个请求同时进来,都检查到了可借状态,然后都执行借出,就会造成一本书被借给两个人的情况。
我处理这个问题的思路是,把检查库存和扣减库存放在同一个SQL语句里完成,利用MySQL的行锁机制保证原子性。具体的UPDATE语句是这样写的:
<?php $sql = "UPDATE books SET available_count = available_count - 1 WHERE id = :book_id AND available_count > 0"; $stmt = $pdo->prepare($sql); $stmt->execute([':book_id' => $book_id]); if ($stmt->rowCount() === 0) { // 库存不足或图书不存在,回滚 throw new Exception('图书库存不足或已下架'); }这条UPDATE语句在执行时,InnoDB会锁定命中的行,直到当前事务结束才释放。第二个请求如果同时到达,会被阻塞,等第一个请求提交后,它再执行时发现available_count已经是0,行数匹配不上,rowCount()返回0,自然就拦截住了。这里的事务配合行锁,比我前面说的PHP应用层判断要可靠得多。
借书和还书的完整过程我都用事务包起来了,借书时更新图书表的可借数量减1,然后插入借阅记录;还书时更新可借数量加1,同时把借阅记录的归还时间写进去。事务的意义在于,任何一个环节执行失败,整个操作就全部回滚,不会出现图书数量已经扣了,借阅记录却丢失的情况。
<?php $pdo->beginTransaction(); try { // 1. 检查读者身份是否存在,图书是否可借 // 2. 更新图书可借数量(带条件) // 3. 写入借阅记录,计算应还时间 $stmt = $pdo->prepare("INSERT INTO borrow_records (book_id, reader_id, borrow_date, due_date, status) VALUES (?, ?, NOW(), DATE_ADD(NOW(), INTERVAL 30 DAY), 'borrowed')"); $stmt->execute([$book_id, $reader_id]); $pdo->commit(); } catch (Exception $e) { $pdo->rollBack(); // 记录日志或返回错误 }这里需要留意的技术点是,beginTransaction之后所有操作的连接必须是同一个PDO实例,我用的单例模式正好保证了这一点。默认的应还时间是借出30天后,续借操作会在此基础上再增加15天。
3.4 统计报表的SQL写法
统计模块往往是课程设计的加分项,代码量不大,但很体现数据库功底。这套系统里我做了三块统计:图书总数、读者总数、当前借出数量。借阅趋势按天统计最近7天的借书量。
拿按天统计的SQL来说,如果直接对借阅记录表按日期分组,当天没有数据时会显示空行,图表就不连续。我的处理方式是先构造一个最近7天的日期序列,然后LEFT JOIN借阅记录表,这样即使某天没有借书,日期依然存在,数量显示为0。MySQL里可以用递归CTE实现日期序列,8.0以上的版本都支持,如果你用的还是5.x,只能靠PHP循环生成日期数组来补位。
<?php $sql = "WITH RECURSIVE date_range AS ( SELECT CURDATE() - INTERVAL 6 DAY AS d UNION ALL SELECT d + INTERVAL 1 DAY FROM date_range WHERE d < CURDATE() ) SELECT date_range.d AS borrow_date, COUNT(br.id) AS borrow_count FROM date_range LEFT JOIN borrow_records br ON DATE(br.borrow_date) = date_range.d GROUP BY date_range.d ORDER BY date_range.d ASC"; $rows = $db->fetchAll($sql);很多人在做统计的时候只会直接GROUP BY原始表,结果就是日期不连续、前端图表断开。这个递归CTE的技巧算是一个实际的坑和经验,你可以直接用在自己项目里。
4. 运行环境准备与系统部署
4.1 本地环境搭建(以phpStudy为例)
这套系统对PHP版本的要求是7.2以上,推荐使用7.4或8.0版本。因为代码用了null合并运算符等PHP 7时代的特性,老版本跑不起来。MySQL要求5.7以上,因为建表语句里用了utf8mb4字符集。
如果你本地还没装环境,我建议直接用phpStudy,它是集成了Apache/Nginx、MySQL和PHP的管理面板式工具,省去了手动配置的麻烦。装好之后启动Apache和MySQL服务,然后把项目文件整个拷贝到phpStudy安装目录下的WWW文件夹里,比如D:\phpstudy_pro\WWW\php-book-management。浏览器访问http://localhost/php-book-management就能看到安装页面。
Windows环境下有一个很容易踩的坑是端口冲突。如果你本机的IIS或者VMware占用了80端口,Apache会启动失败。phpStudy面板会提示端口被占用,你可以在设置里把Apache的端口改成8080,然后通过http://localhost:8080/php-book-management访问。改端口后,项目代码里如果写死了跳转链接,要记得同步修改。
4.2 导入数据库与配置连接
打开phpMyAdmin,新建一个数据库,名字随意,建议叫book_db,字符集选utf8mb4。然后点击导入,选择项目sql目录下的book_db.sql文件,执行即可。导入成功后你会看到四张表:admins、readers、books、borrow_records。之后修改includes/config.php里的数据库连接信息。
<?php // includes/config.php return [ 'db_host' => '127.0.0.1', 'db_name' => 'book_db', 'db_user' => 'root', 'db_pass' => 'root', ];这里有一个细节,db_host我建议写成127.0.0.1,不要写localhost。原因是在某些PHP版本和MySQL 8的组合下,localhost会被解析为Unix Socket连接,而127.0.0.1走的是TCP连接,后者在XAMPP和phpStudy环境下更稳定,不容易出现连接被拒绝的问题。
数据库配置完成后,访问http://localhost/php-book-management,应该能跳转到登录页面。默认管理员账号是admin,密码是123456,这个在sql文件里已经预置好了,登录后建议第一时间在后台修改密码。
4.3 部署到线上服务器
如果你要把系统部署到云服务器上,除了上面这些步骤,还需要额外做三件事。一是修改PHP上传限制和内存限制,在php.ini里把upload_max_filesize和memory_limit调到适当值,因为有些图书封面图片可能比较大。二是把MySQL的root密码换成强密码,并且创建一个专用数据库账号,只授予book_db这个库的权限,不要用root连接应用。三是配置好Apache的伪静态或者Nginx的server块,确保index.php能作为默认入口文件被访问。
Nginx环境下有一个小坑需要注意,就是PHP文件如果要通过路径访问,比如http://域名/admin/index.php,Nginx的fastcgi配置必须正确。很多人在Apache下跑得好好的,换到Nginx就报404或者502,多半是fastcgi_pass配置成了socket路径,但PHP-FPM监听的是端口。你可以在Nginx的server配置里检查fastcgi_pass是unix:/tmp/php-cgi.sock还是127.0.0.1:9000,确保和PHP-FPM的listen配置一致。
4.4 部署检查清单
我把部署后必做的检查项整理成了一份清单,方便你照着走一遍:
- 后台能否正常登录,登录后跳转是否正常
- 新增一本图书,库存填1,是否显示为可借状态
- 用读者账号登录,借出这本书,图书列表里该书是否显示为已借出
- 归还后,图书是否恢复可借状态
- 注册一个新读者账号,能否正常登录个人中心
- 在借阅记录里修改借出日期为40天前,查看是否被标记为逾期
- 退出登录后,直接访问后台某个页面URL,是否会被踢回登录页
- 刷新统计页面,数据是否与实际操作一致
这些问题如果全部通过,说明系统部署基本没有大问题了。
5. 常见问题与排查技巧实录
5.1 数据库连接失败的几种原因
这个报错出现频率最高,我单独说一下。第一种情况是phpstudy的MySQL服务没启动,打开面板点启动就行。第二种情况是密码不对,MySQL 8默认的root密码可能为空,也可能安装时你设置过,拿不准的话在phpMyAdmin里测试一下root能不能登录。第三种情况是数据库不存在,你在phpMyAdmin里确认是否已经创建了book_db且导入了sql文件。第四种情况是PHP版本太老,代码里用了PHP 7语法,在PHP 5.6环境下直接语法错误,这种直接升级环境。
排查数据库问题有个很有效的笨办法,就是在项目根目录临时放一个phpinfo文件,打印密钥信息,看看PDO MySQL驱动是否已经启用。如果没启用,Windows环境下需要修改php.ini,把extension=pdo_mysql前面的分号去掉,重启Apache服务才对配置生效。
5.2 登录后跳转失效或页面空白
页面空白通常是PHP报错被关闭了,显示为白屏,实际是执行到某个错误时中断了。遇到这种情况,先把错误显示打开。在项目入口或者config.php里临时加一行:
ini_set('display_errors', '1'); error_reporting(E_ALL);加上之后刷新页面,就能看到具体的报错信息。最常见的是文件引入路径不对,导致找不到某个类或函数。因为PHP的include路径是相对于当前执行入口文件的,如果你在子目录里引入了includes/db.php,而它内部又用了相对路径,就会出现问题。我处理这类问题的惯例是在config.php最顶部定义一个绝对路径常量:
define('BASE_PATH', dirname(__DIR__));后续所有include统一使用BASE_PATH拼接绝对路径,彻底解决路径错乱的问题。
5.3 中文乱码问题
前端页面显示中文正常,但数据库里存进去变成问号,这是字符集设置不对的典型症状。解决思路是统一三处字符集:数据库表结构使用utf8mb4,PDO连接字符串的charset指定utf8mb4,页面输出头部加上header Content-Type设置utf-8。另外从PHP 7.4开始,默认字符集如果没指定可能使用服务器的默认字符集,连接字符串里必须显式声明。
如果你是从旧版本数据库导入的,表结构里可能还是utf8,而连接字符集是utf8mb4,这样会报字符集不匹配的错误。稳妥的做法是先修改表字符集:
ALTER TABLE books CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;5.4 并发借书导致库存负数
虽然我前面写了条件更新的防超卖方案,但如果你在二次开发的时候把借书逻辑改成了先SELECT后UPDATE,并且没有加事务,在高并发下就可能出现库存负数。这个问题的排查思路是检查日志,看两条借阅请求是否在同一个时间点前后执行了更新。预防的办法很简单,坚持用我前面给的那条带条件的UPDATE写法,不要自己改成两步操作。
如果你确实需要更强的安全保证,升级方案是在借书之前添加一个SELECT FOR UPDATE语句,先锁行再读取状态,但这样做的并发性能会降低。小型系统用条件更新就足够了。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 页面白屏 | PHP错误被隐藏 | 开启display_errors,查看报错 |
| 数据库连接失败 | 服务未启动/密码错误/库不存在 | 依次检查MySQL服务、连接配置、导入 |
| 中文乱码 | 字符集不统一 | 表、连接、页面统一utf8mb4 |
| 登录跳转失效 | session配置或路径问题 | 检查session_start位置和exit调用 |
| 借书后库存不变 | 代码逻辑里没更新books表 | 检查事务是否提交 |
| 上传图片失败 | 文件大小超限 | 修改php.ini的upload_max_filesize |
6. 二次开发与扩展思路
6.1 给系统添加图书封面上传
现在系统里图书没有封面上传功能,图书列表里只能显示文字信息,视觉效果差一些。你可以加一个封面字段,在图书表里增加cover_url列,然后在新增图书的表单里加上文件上传控件,后端处理uploaded file,将文件移动到uploads目录,把访问路径存入数据库。这个扩展不算复杂,但要注意两点:文件类型白名单要做,不然用户传个PHP文件上来就能执行;上传目录的写权限要正确,否则移动文件时报错。
6.2 增加排行榜功能
排行榜是图书馆管理系统常见的展示模块,SQL写起来也不复杂。按借阅次数统计,分组排序就好:
SELECT b.book_name, COUNT(br.id) AS borrow_times FROM books b LEFT JOIN borrow_records br ON br.book_id = b.id GROUP BY b.id ORDER BY borrow_times DESC LIMIT 10;这个查询的要点是LEFT JOIN保证没有被借过的图书也能出现在排行榜里,排序时借阅次数为0的排最后。你在后台加一个页面,把这个查询结果展示成表格或图表,就是一个完整的图书热门榜了。
6.3 接入邮箱通知
如果你想做逾期提醒,可以接一个SMTP邮件发送功能。PHP里用mail函数不太好使,推荐直接用PHPMailer库,通过Composer安装或者手动引入。每天定时跑一个脚本,查询应还日期小于当前日期且未归还的记录,给对应的读者发提醒邮件。这个功能适合有真实用户场景的系统,自己做着玩的话可以先放一放。
6.4 扩展要注意的问题
我在做二次开发的时候吃过一个亏,就是修改数据库表结构的时候,没有同步更新相关的查询SQL和表单代码,导致前端报字段不存在。你要改表结构,先用SQL把新字段加好,再全局搜索原代码里对旧表的查询和插入语句,把所有涉及的地方一起更新,别改一半留一半,否则排查起来很痛苦。
另外,如果你的系统将来要上线真实环境,有几件事必须做:把默认密码全部改掉,限制后台登录失败次数防止暴力破解,对文件上传目录做禁止执行PHP的配置,定期备份数据库。这些虽然是老生常谈,但真出了问题,代价都不小。
7. 项目总结与踩坑心得
回头整理这套代码的时候,我自己也在几个地方反复踩坑,这里分享出来,希望能帮你少走弯路。
第一个是数据一致性问题。最初我写借书逻辑时是两步操作,先查库存再更新,结果在压测时发现了库存负数的问题。后来改成条件更新方式,把检查库存和扣减合并成一条SQL,问题就消失了。所以我的建议是,涉及数值增减的操作,尽量在一条UPDATE语句里完成,不要拆成两步,中间的间隔时间越长,出问题的概率越大。
第二个是字符集问题。有个朋友下载了我早期版本的代码,部署后一直中文乱码,排查了很久,后来发现他的MySQL是5.6版本,对utf8mb4支持不完整,建表时自动回退成了utf8。解决办法是把MySQL升级到5.7以上,或者建库时手动指定字符集为utf8mb4。
第三个是session配置的坑。在Windows本地环境调试时,一切正常,但部署到Linux服务器上,发现登录后总是跳回登录页。后来排查发现是Session保存路径的问题,PHP的session.save_path指向了一个不可写的目录,导致session数据没有真正保存下来。这个可以通过phpinfo查看session.save_path的实际值,然后手动创建目录并设置写权限解决。
第四个是调试技巧。我在开发过程中会开着两个工具来加速排错——一个是浏览器的开发者工具Network面板,观察每个AJAX请求的状态码和返回内容;另一个是MySQL的通用查询日志,临时开启后能看到所有执行的SQL语句,这样能快速定位是在哪个环节出的问题。排查完记得把日志关掉,否则磁盘会被刷爆。
这套代码的完整度对学习和课程设计来说是够用的,如果你想继续往深了做,可以往这三个方向走:读者管理加用户分组和借阅权限等级,图书信息加分类和标签体系,统计模块加导出Excel功能。这三个方向都能让你对系统有更深入的理解,也适合作为课程报告的扩展点。
最后说一句实在话,学习PHP项目最有效的路径不是看多少遍教程,而是拿到一套完整代码,本地跑起来,然后开始动手改功能。改坏了大不了重新导入一遍数据库,但每改一次,你对这套系统的理解就会深一层。这套代码就是我在这条路上反复折腾后沉淀下来的,希望它也能帮你建立自己的项目经验。
本文还有配套的精品资源,点击获取