简介:这是一套基于ThinkPHP5开发的轻量级图书管理系统Demo源码,面向PHP初学者与Web全栈入门者,帮助快速掌握MVC框架实战、前后端交互及常见业务模块开发。项目采用ThinkPHP5作为核心后端框架,后台界面集成EasyUI,前台使用Bootstrap,全程通过jQuery实现异步加载,涵盖字符串(单行/多行/富文本)、整型、浮点型、日期、图片与文件等六类字段的完整CRUD操作,并支持多条件组合模糊查询、分页展示及Excel导出功能;富文本编辑依赖UEditor插件,图片与文件上传已封装为基类方法uploadPhoto和uploadFile,显著提升代码复用性与可维护性。压缩包为RAR格式,大小9.57MB,包含全部可运行源码文件(文件总数未提供,故不列具体数量),无额外文档或视频,但结构清晰、注释充分,适合直接部署学习。目前已有553人学习下载,是理解ThinkPHP5+MySQL+EasyUI协同开发的典型教学案例。
1. 这不是“跑个 demo 就完事”的 ThinkPHP5 图书管理系统——它是一套可落地、可调试、可演进的 Web 应用最小闭环
你下载了DemoLibraryBasedThinkPHP5.rar,解压后看到application/public/thinkphp/三个核心目录,index.php能访问但首页报错“数据库连接失败”,admin.php登录页空白,config/database.php里写着'host' => '127.0.0.1'却没提端口和字符集——这不是一个“点开即用”的演示包,而是一份面向 PHP 中级开发者的真实教学切片:它用 ThinkPHP5.1 的标准分层结构(MVC+路由+中间件),实现了图书增删改查、分类管理、用户登录鉴权、借阅记录追踪等 8 个核心业务动作,所有逻辑都落在application/common/model/Book.php、application/admin/controller/Book.php等真实路径下,而非写在控制器里的一堆echo。适合刚学完 PHP 基础、能写 MySQL 建表语句、但还没独立搭过完整 Web 项目的开发者;也适合需要快速验证 ThinkPHP5 在 Nginx+PHP-FPM 环境下兼容性的一线运维。它不教你怎么配 LNMP,但每一步配置错误都会抛出明确异常——比如PDOException: SQLSTATE[HY000] [1045] Access denied,而不是静默失败。真正价值不在“能运行”,而在“报错时你知道该查哪一行、改哪个参数、重试哪条命令”。
2. 从解压到首页可访问:ThinkPHP5 图书管理系统本地部署的四步实操链
ThinkPHP5 的运行依赖明确的 PHP 版本、扩展与目录权限,DemoLibraryBasedThinkPHP5.rar并未自带composer.json或.env,因此必须手动补全环境契约。常见误区是直接把public/目录设为 Web 根目录却忽略runtime/写入权限,或误将index.php放在子目录导致路由解析失败。以下流程经 PHP 7.3–7.4 实测,覆盖 Windows WAMP 与 Linux Ubuntu 20.04 + Nginx 场景。
2.1 环境校验与基础服务启动
ThinkPHP5.1 要求 PHP ≥ 5.6.0 且 ≤ 7.4.x(注意:PHP 8.0+ 不兼容),必须启用pdo_mysql、mbstring、curl、openssl四个扩展。执行以下命令验证:
php -v # 输出应为 PHP 7.3.27 或 7.4.16 等版本号,非 8.0.x 或 5.4.x php -m | grep -E "pdo_mysql|mbstring|curl|openssl" # 应返回四行,每行含对应模块名,缺一则需修改 php.ini 启用提示:若
php -m无输出,说明 CLI 模式未加载正确 php.ini,用php --ini查看路径,编辑该文件取消;extension=pdo_mysql.so前的分号(Linux)或;extension=php_pdo_mysql.dll(Windows)。
Nginx 用户需确认fastcgi_pass指向正确的 PHP-FPM socket(如/run/php/php7.4-fpm.sock),Apache 用户需开启mod_rewrite并确保.htaccess生效。关键检查点:访问http://localhost/test.php(内容为<?php phpinfo(); ?>)时,页面顶部显示 PHP 版本,底部Loaded Configuration File显示的路径与php --ini一致。
2.2 解压与目录结构标准化
DemoLibraryBasedThinkPHP5.rar解压后得到根目录含application/、public/、thinkphp/、extend/、vendor/(若存在)等文件夹。严禁将整个解压包直接放到 Web 根目录(如/var/www/html/),必须将public/下所有内容(含index.php、static/、.htaccess)作为 Web 可访问入口:
# Linux 示例:假设解压到 /home/user/DemoLibraryBasedThinkPHP5/ cd /home/user/DemoLibraryBasedThinkPHP5/ sudo cp -r public/* /var/www/html/ sudo chown -R www-data:www-data /var/www/html/ sudo chmod -R 755 /var/www/html/ # 创建 runtime 目录并赋权(ThinkPHP5 运行时日志/缓存目录) sudo mkdir -p /var/www/html/runtime sudo chown -R www-data:www-data /var/www/html/runtimeWindows WAMP 用户:将public/内全部文件复制到wamp64/www/demo-library/,然后访问http://localhost/demo-library/。
注意:
public/外的application/和thinkphp/必须保留在 Web 根目录同级位置(即/var/www/html/的父目录),否则index.php中的define('APP_PATH', __DIR__ . '/../application/');将无法定位应用目录,报错Fatal error: require(): Failed opening required '../application/...。
2.3 数据库初始化与配置注入
系统默认使用 MySQL,建库语句藏在database.sql(通常位于根目录或application/database/下)。若无此文件,需根据application/database.php中的'database' => 'demo_library'手动创建:
CREATE DATABASE `demo_library` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- utf8mb4 是 ThinkPHP5 推荐字符集,支持 emoji 及四字节 UTF-8 字符导入数据表结构(假设database.sql存在):
mysql -u root -p demo_library < /home/user/DemoLibraryBasedThinkPHP5/database.sql # 输入密码后,应无报错输出配置文件application/database.php需修改以下 4 项(其他保持默认):
| 参数 | 推荐值 | 说明 |
|---|---|---|
'hostname' | '127.0.0.1' | 不用localhost(可能触发 socket 连接,慢且易错) |
'database' | 'demo_library' | 与上步创建的库名严格一致,含反引号 |
'username' | 'root' | 开发环境可用 root,生产环境必须新建专用账号 |
'password' | 'your_password' | 若 MySQL 8.0+ 使用 caching_sha2_password 插件,需降级认证方式:ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password'; |
提示:修改后访问
http://localhost/demo-library/,若仍报数据库错误,查看/var/www/html/runtime/log/下最新.log文件,搜索SQLSTATE错误码——这是比浏览器白屏更精准的排错入口。
2.4 路由与入口文件验证
ThinkPHP5 默认开启 URL 重写,public/.htaccess(Apache)或 Nginx 的try_files规则必须生效。测试方法:访问http://localhost/demo-library/index.php/admin/login应显示后台登录页;若提示No input file specified,说明重写未生效。
Nginx 配置片段(加在server块内):
location / { try_files $uri $uri/ /index.php?$QUERY_STRING; } location ~ \.php$ { fastcgi_pass unix:/run/php/php7.4-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }Apache 用户确认AllowOverride All已在<Directory>中启用,并重启服务。此时访问http://localhost/demo-library/应显示图书列表首页,URL 中不含index.php——这是路由生效的标志。
3. 图书核心业务逻辑拆解:从 Model 层验证规则到 Controller 层借阅状态流转
DemoLibraryBasedThinkPHP5的业务骨架集中在application/common/model/Book.php(图书模型)、application/common/model/BorrowRecord.php(借阅记录)及application/admin/controller/Book.php(后台控制器)。它不采用 Eloquent ORM 风格,而是基于 ThinkPHP5 的think\Model基类,通过validate()方法定义字段规则,用where()->update()实现状态变更。理解这套逻辑,才能安全地扩展“预约”“逾期提醒”等功能。
3.1 Book 模型的验证与关联设计
application/common/model/Book.php定义了图书主表结构及验证规则:
<?php namespace app\common\model; use think\Model; class Book extends Model { // 设置数据表名(若与类名不一致) protected $table = 'books'; // 定义验证规则(用于 add/edit 接口) protected $validate = [ ['name', 'require|max:50', '书名不能为空|书名不能超过50个字符'], ['isbn', 'unique', 'ISBN 已存在'], ['category_id', 'number|gt:0', '分类ID必须为数字|分类ID必须大于0'], ['stock', 'number|egt:0', '库存必须为数字|库存不能为负数'], ]; // 定义自动完成(新增时自动写入时间) protected $auto = ['create_time']; protected $type = [ 'create_time' => 'timestamp', ]; }关键点说明:
protected $validate数组中,['isbn', 'unique', 'ISBN 已存在']表示对isbn字段调用unique验证器,自动检测books表中是否已存在相同值;protected $auto = ['create_time']表示新增记录时自动填充create_time字段,protected $type将其转为时间戳;category_id的gt:0规则防止前端传入 0 或负数 ID,避免关联查询失败。
提示:验证失败时,
$book->validate(true)返回false,$book->getError()获取具体错误信息(如“ISBN 已存在”),Controller 层据此返回 JSON 错误响应。
3.2 BorrowRecord 借阅记录的状态机实现
借阅功能的核心是BorrowRecord模型的状态流转:status字段取值为0(待审核)、1(已借出)、2(已归还)、-1(已拒绝)。application/admin/controller/Borrow.php中的doBorrow()方法体现状态控制逻辑:
// application/admin/controller/Borrow.php public function doBorrow() { $data = input('post.'); $record = new BorrowRecord(); // 1. 检查用户是否已借满(假设最多借 3 本) $borrowed = BorrowRecord::where(['user_id' => $data['user_id'], 'status' => 1])->count(); if ($borrowed >= 3) { $this->error('您已借阅 3 本书,无法继续借阅'); } // 2. 检查图书库存 $book = Book::get($data['book_id']); if (!$book || $book->stock <= 0) { $this->error('图书库存不足'); } // 3. 创建借阅记录(初始状态为 0) $result = $record->allowField(true)->save([ 'user_id' => $data['user_id'], 'book_id' => $data['book_id'], 'status' => 0, // 待审核 'create_time' => time(), ]); if ($result) { // 4. 更新图书库存(原子操作) $book->setInc('stock', -1); // stock = stock - 1 $this->success('借阅申请已提交,请等待管理员审核'); } else { $this->error('申请失败'); } }逻辑说明:
BorrowRecord::where(...)->count()统计当前用户已借数量,硬编码3是业务规则,可抽离为配置项;$book->setInc('stock', -1)是 ThinkPHP5 提供的原子递减方法,等价于UPDATE books SET stock = stock - 1 WHERE id = ?,避免并发超借;status = 0表示申请待审,管理员在后台点击“通过”时才更新为1,此设计分离了用户操作与管理员审批。
3.3 Admin 控制器的权限隔离与菜单渲染
application/admin/controller/Base.php是后台控制器基类,通过initialize()方法强制校验登录态:
protected function initialize() { parent::initialize(); $adminId = session('admin_id'); if (!$adminId) { $this->redirect(url('login/index')); // 未登录跳转登录页 } }菜单数据来自application/admin/controller/Index.php的menu()方法,读取application/admin/config/menu.php配置:
// application/admin/config/menu.php return [ ['name' => '图书管理', 'url' => 'book/index', 'icon' => 'fa-book'], ['name' => '分类管理', 'url' => 'category/index', 'icon' => 'fa-tags'], ['name' => '借阅记录', 'url' => 'borrow/index', 'icon' => 'fa-exchange'], ];模板中通过{:menu()}函数渲染,url('book/index')自动生成/admin/book/index路由。这种配置驱动菜单的方式,比硬编码 HTML 更易维护。
4. 关键参数调优与三类高频故障的定位指令
ThinkPHP5 的性能与稳定性高度依赖runtime/目录权限、缓存配置及数据库连接池设置。DemoLibraryBasedThinkPHP5默认关闭所有缓存,开发阶段需手动开启以提升响应速度;而线上部署时,debug模式必须关闭,否则暴露敏感路径。以下参数直接影响系统可用性,需按场景调整。
4.1 runtime 目录权限与日志级别控制
runtime/是 ThinkPHP5 的核心临时目录,存放缓存、日志、模板编译文件。权限错误会导致mkdir(): Permission denied错误。标准修复命令:
# Linux 下确保 www-data 用户可写 sudo chown -R www-data:www-data /var/www/html/runtime sudo find /var/www/html/runtime -type d -exec chmod 755 {} \; sudo find /var/www/html/runtime -type f -exec chmod 644 {} \; # 日志级别设为 'error'(生产环境)或 'debug'(开发环境) # 修改 application/config.php 中的 'log' 配置: // 'level' => ['error'], // 生产环境仅记录错误 // 'level' => ['sql', 'error', 'info'], // 开发环境记录 SQL 与流程提示:若
runtime/log/下无.log文件生成,检查application/config.php中'log' => ['file' => true]是否为true,且runtime/目录存在。
4.2 数据库连接池与超时参数优化
application/database.php中的连接参数直接影响高并发下的稳定性:
| 参数 | 默认值 | 推荐值(开发) | 推荐值(生产) | 说明 |
|---|---|---|---|---|
'deploy' | 0 | 0 | 1 | 1启用读写分离(需主从库) |
'timeout' | 30 | 30 | 5 | 连接超时秒数,生产环境缩短防阻塞 |
'break_reconnect' | false | false | true | 断线自动重连,避免单次失败中断请求 |
'params' | [] | [\PDO::ATTR_PERSISTENT => true] | [\PDO::ATTR_PERSISTENT => true] | 启用持久连接,减少握手开销 |
启用持久连接后,需在 PHP-FPM 配置中限制pm.max_children,避免 MySQL 连接数爆满:
; /etc/php/7.4/fpm/pool.d/www.conf pm.max_children = 32 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 204.3 路由缓存与模板编译开关
开发阶段频繁修改控制器,需禁用路由缓存;生产环境则必须开启以提升性能:
# 开发时清除路由缓存(避免修改 route.php 后不生效) php think clear --route # 生产环境生成路由缓存 php think build --route # 模板编译:开发时设为 false(实时解析),生产设为 true(编译后执行) // application/config.php 'template' => [ 'cache' => true, // 生产环境开启 ],验证路由缓存是否生效:查看runtime/cache/下是否有route.php文件,其内容为return array ( ... );形式数组。
5. 从 demo 到生产:三个可立即落地的安全加固与扩展技巧
DemoLibraryBasedThinkPHP5的代码结构清晰,但作为教学 demo,它省略了生产环境必需的安全防护与扩展接口。以下技巧无需重写核心逻辑,只需在现有文件中添加几行代码,即可显著提升健壮性。重点解决:弱密码爆破、XSS 注入、API 接口未鉴权三大风险。
5.1 后台登录增加图形验证码与登录失败锁定
application/admin/controller/Login.php的checkLogin()方法需增强。原逻辑仅校验用户名密码,攻击者可暴力遍历。添加验证码与失败计数:
// application/admin/controller/Login.php public function checkLogin() { $captcha = input('post.captcha'); if (!captcha_check($captcha)) { $this->error('验证码错误'); } $username = input('post.username'); $password = input('post.password'); // 记录失败次数(使用 Redis 或文件存储) $lockKey = 'login_lock_' . $username; $failCount = cache($lockKey); if ($failCount && $failCount >= 5) { $this->error('登录失败超过 5 次,请 15 分钟后再试'); } $admin = Db::name('admin')->where('username', $username)->find(); if (!$admin || !password_verify($password, $admin['password'])) { cache($lockKey, $failCount ? $failCount + 1 : 1, 900); // 15分钟过期 $this->error('用户名或密码错误'); } // 登录成功,清除失败记录 cache($lockKey, null); session('admin_id', $admin['id']); $this->success('登录成功', url('index/index')); }配套在登录模板application/admin/view/login/index.html中添加验证码:
<div class="form-group"> <label>验证码</label> <div class="input-group"> <input type="text" name="captcha" class="form-control" placeholder="请输入验证码"> <span class="input-group-btn"> <img src="{:captcha_src()}" alt="captcha" onclick="this.src='{:captcha_src()}?'+Math.random()" style="cursor:pointer;height:36px;"> </span> </div> </div>提示:
captcha_src()是 ThinkPHP5 内置函数,无需额外安装扩展;cache()使用文件缓存,生产环境建议切换为 Redis。
5.2 图书列表页输出过滤 XSS 攻击载荷
application/home/controller/Book.php的index()方法直接输出Book::all()结果到模板,若数据库中存有<script>alert(1)</script>,将被浏览器执行。在模板层统一过滤:
<!-- application/home/view/book/index.html --> {volist name="books" id="book"} <tr> <td>{$book.name|htmlspecialchars}</td> <td>{$book.isbn|htmlspecialchars}</td> <td>{$book.author|htmlspecialchars}</td> <td>{$book.description|htmlspecialchars}</td> </tr> {/volist}|htmlspecialchars是 ThinkPHP5 模板引擎的内置过滤器,等价于 PHP 的htmlspecialchars($str, ENT_QUOTES, 'UTF-8'),可防御反射型 XSS。
5.3 为借阅接口添加 JWT Token 鉴权(兼容前后端分离)
若需将Borrow.php的doBorrow()改为 API 接口,需添加 Token 验证。在application/common/middleware/TokenAuth.php中实现:
<?php namespace app\common\middleware; use think\Request; use Firebase\JWT\JWT; use Firebase\JWT\Key; class TokenAuth { public function handle(Request $request, \Closure $next) { $token = $request->header('Authorization'); if (!$token) { return json(['code' => 401, 'msg' => '缺少 Token']); } try { $key = 'your_secret_key_here'; // 生产环境从 config 读取 $decoded = JWT::decode($token, new Key($key, 'HS256')); $request->uid = $decoded->uid; // 将用户 ID 注入 Request } catch (\Exception $e) { return json(['code' => 401, 'msg' => 'Token 无效']); } return $next($request); } }在application/route.php中为 API 路由注册中间件:
// 开放登录接口(不鉴权) Route::post('api/login', 'api.Login/login'); // 借阅接口强制鉴权 Route::post('api/borrow', 'api.Borrow/doBorrow')->middleware('token_auth');此时前端调用POST /api/borrow时,需在 Header 中携带Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...,服务端自动校验签名与有效期。
本文还有配套的精品资源,点击获取