拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PHP图书管理系统老代码改造:从部署到借还书事务与乱码修复

PHP图书管理系统老代码改造:从部署到借还书事务与乱码修复 简介一套面向PHP学习者和毕业设计的图书管理系统源代码采用PHPMySQL实现覆盖图书录入、分类管理、模糊搜索、在线借阅、归还处理及用户权限控制等完整业务闭环适合用于课程实践、毕业设计或作为企业级Web开发的入门范本。压缩包总计112个文件包括50个php业务脚本、frm/myd/myi数据库文件、gif/jpg/css/js前端资源以及doc使用说明整体仅624KB轻量易部署便于直接导入本地环境运行调试。目前已有6542人学习浏览反馈具有很高的参考价值。通过这套源码读者可以系统理解MVC分层、PDO数据库操作、Session会话管理、SQL防注入等关键技术点同时项目附带数据库表结构与说明文档方便二次开发快速改造成带分类、借阅、统计功能的个性化图书管理系统是巩固PHP实战能力的高性价比素材。1. PHP图书管理系统为什么这套老代码到今天还有人翻出来改一个十几年前写的老项目至今还在被一届又一届学生、培训班学员和想快速交付外包的开发翻出来改这就是PHP图书管理系统。搜索PHP源码你几乎绕不开它后台管书、管读者、管借还前台查书加一个登录就收工。它本身不难难的是你拿到的源代码多半是PHP 5时代的写法连mysql扩展都已经在PHP 7里被移除放到今天的PHP 8环境几乎必报错。这篇笔记就干两件事先把这套老代码跑起来再把它改到能用的状态顺带把借书还书、中文乱码、登录失效这些边角坑讲透。适合刚学PHP想拿项目练手的人也适合接了小单子要快速改出一套能交货的系统。2. 跑起来之前这套系统的技术栈和常见目录结构2.1 为什么这套系统万年不变地选原生PHP加MySQL市面上的PHP图书管理系统源代码绝大多数是原生PHP不带Laravel、ThinkPHP这些框架连PDO都少见到处是mysql_query。原因很现实这代码的原始场景是教学和课设教材十几年没换章节老师教的就是include文件加SQL拼接另一部分是外包交付图的是部署简单——一台Apache、一个MySQL、把目录丢进htdocs就能跑。对读者来说这反而是好事你能看懂的代码才是能改的代码一个框架包进来光路由和依赖就能劝退一半新手。那为什么是MySQL不是SQLite当年这套系统的数据量假设是几千本书、几百个读者MySQL的安装门槛低phpMyAdmin导入SQL文件点两下就行。SQLite虽然更轻但PHP老代码里的mysql扩展和它完全不兼容改造成本大于收益。所以不要一上来就想着换数据库老系统最忌讳大改底层。另外你会发现几乎每个版本的结构都是“前台一个index.php后台一个admin文件夹”没有MVC、没有路由、没有模板引擎。这不是懒惰而是这套系统的业务复杂度撑不起更重的架构。图书管理就三类操作增删改查图书、增删改查读者、借书还书。用最直白的include组织页面反而让每一个功能的追踪路径都极短登录页连登录校验列表页连数据库查询提交页连SQL语句三跳以内就摸到底了。2.2 读懂目录结构与请求流向我经手过的这类源码目录结构大同小异通常长这样book-system/ ├── admin/ 管理员后台 │ ├── book_add.php │ ├── book_list.php │ ├── book_edit.php │ ├── reader_add.php │ ├── borrow_add.php │ └── ... ├── index.php 前台入口通常是查书页 ├── login.php 登录页 ├── conn.php 数据库连接文件 ├── css/ 样式 └── book_db.sql 数据库脚本第一次拿到代码别急着改先把请求流向捋一遍。以最常见的流程为例用户在login.php提交表单表单action指向同一个文件或admin_check.php脚本里先session_start()再去admin表比对用户名和密码成功就把用户名写进$_SESSION然后header跳转到admin/index.php。后台每个页面顶部通常include一个check_login.php有的叫auth.php、check.php里面就一行判断逻辑如果$_SESSION[admin]没设就die(请先登录)或跳回登录页。这套东西谈不上什么架构但能跑。你拿到源码第一件事就是把登录页、连接文件、检查登录三个文件找出来确认它们之间的include关系。很多版本里check_login.php不是被每个后台页面都include的有些页面漏了这行就成了一个未授权入口。改代码前先把这个缺陷记下来后面的验证清单里要用到。2.3 数据库表设计与字段对应关系数据库脚本叫book_db.sql还是books.sql不重要表几乎永远是四张admin管理员、book图书、reader读者、borrow借阅记录。常见字段如下表关键字段说明adminid, username, passwordpassword通常存MD5bookid, name, author, price, number, sortnumber是库存sort是分类readerid, name, sex, barcode, gradebarcode是借书证号borrowid, reader_id, book_id, borrow_time, back_time, if_backif_back为1表示已还这里最容易被新手忽略的是borrow表和book表之间的库存联动借书时要插入一条borrow记录、同时把book.number减1还书时反过来。很多老版本直接用两条独立的UPDATE和INSERT搞定中间没有事务保护如果第二步失败库存就对不上了。我在第四章会单独拉出来说这里先记住借还逻辑和数据一致性强相关。还有一个细节这套系统的表之间几乎没有外键约束。reader_id、book_id都是逻辑关联删掉一个读者他的borrow记录还在甚至可能指向一个不存在的读者ID。这也是老代码的普遍毛病物理外键会影响灵活性但代价是脏数据全靠业务代码控制改动时稍不注意就会出现孤儿记录。你导入SQL后可以用一条LEFT JOIN把这种孤儿记录扫出来SELECT b.id, b.reader_id, b.book_id FROM borrow b LEFT JOIN reader r ON b.reader_id r.id LEFT JOIN book bk ON b.book_id bk.id WHERE r.id IS NULL OR bk.id IS NULL;注意拿到SQL文件后先看建表语句里CHARSET是不是utf8很多老脚本默认latin1这是后面中文乱码的根源之一。3. 本地部署把源码跑通的最小操作步骤3.1 环境选型phpStudy还是XAMPP这套老代码只认Apache加PHP加MySQL。Nginx加PHP-FPM也能跑但老代码里大量使用.htaccess和相对路径URLNginx默认不处理.htaccess会出现路径重写失效或目录访问403的问题新手排查起来很头大。所以本地开发我一般直接用集成环境Windows上装phpStudy或XAMPPmacOS上装MAMP也行三者都能一键启动Apache和MySQL。phpStudy的优势是自带PHP版本切换遇到PHP5老代码可以直接切回PHP 5.6这是你最重要的后悔药。XAMPP的优势是文档多、报错信息全Windows下遇到问题一搜一大把解决方案。我的建议是电脑上装phpStudy把PHP 5.6和PHP 7.4都装上测试老代码时切回5.6改造完再切7.4验证一遍。这里有个很实际的排查点Apache默认监听80端口如果本机装了IIS、Docker、或某些国产软件占用了80Apache会启动失败。phpStudy里可以直接把端口改成8080但改完访问地址要跟着变。另一个点是PHP版本切换后必须重启Apache不然phpinfo里看到的还是旧版本。3.2 放置源码与导入数据库集成环境装好后把代码目录整个拷进Web根目录。phpStudy默认是WWWXAMPP是htdocs目录名建议起成book这样访问路径是http://localhost/book/避免中文目录在旧版Apache下的编码问题。如果源码包是zip解压时注意有没有双层目录——很多包解开后是book-system/book-system/xxx这种要去掉一层再放进Web根目录。接下来导入数据库两种方式都行。命令行方式适合熟悉MySQL的人mysql -u root -p book_db.sql按回车后会提示输入MySQL的root密码本地集成环境默认密码一般是空或者root。导入成功后可以用下面的命令验证表是否存在SHOW DATABASES; USE book_db; SHOW TABLES;如果不想碰命令行phpMyAdmin更适合新手浏览器打开http://localhost/phpmyadmin点“导入”选择book_db.sql文件字符集选utf8执行即可。导入失败最常见的原因是SQL文件里带了CREATE DATABASE语句而你当前的MySQL账号没有建库权限。解决方法是把这一行手动删掉改成在phpMyAdmin里先手动建一个空的book_db库再导入。还有一个隐蔽问题SQL文件可能从某个Windows老环境里拷出来编码是GBK。用文本编辑器打开乱码的话说明文件本身不是UTF-8导入前先用编辑器把编码转成UTF-8无BOM否则导入后中文会直接变问号。3.3 修改数据库连接配置导入成功后找数据库连接文件。这个文件在绝大部分版本里叫conn.php也有叫config.php、db.php的位置在项目根目录或include目录。打开后会看到类似这样的内容?php $conn mysqli_connect(localhost, root, , book_db); if (!$conn) { die(数据库连接失败: . mysqli_connect_error()); } mysqli_set_charset($conn, utf8); ?这是已经改造过的写法。如果是老源码大概长这样?php $conn mysql_connect(localhost, root, , book_db); mysql_select_db(book_db, $conn); mysql_query(set names utf8); ?两种写法对应本地的PHP版本老写法只在PHP 5.6及以下能用新写法在PHP 5.4以上都能用。你需要改的就是第二、三、四个参数数据库地址一般是localhost或127.0.0.1用户名是root密码按你集成环境实际填的来库名要和导入时保持一致。怎么快速确定整个项目里还有哪些地方在连接数据库全局搜索mysql_connect或mysqli_connect把出现的地方全部替换。有的项目不止一个连接文件比如admin目录里还藏了一个admin_conn.php漏改的话后台能开但一提交就报连接失败这种症状最难定位。3.4 登录后台并验证基础功能连接配置好后浏览器访问http://localhost/book/login.php用源码自带的默认账号登录。默认账号一般是admin密码123456或admin也有直接写在SQL文件里的。如果登录时提示密码错误用phpMyAdmin打开admin表看password字段的值如果是一段32位MD5说明密码是加密存储的不要试图在表里直接填明文。直接用这条SQL改UPDATE admin SET password MD5(123456) WHERE username admin;登录成功后把整个后台过一遍图书添加、图书列表、读者管理、借书还书。记录两个预期结果添加一本新书后图书列表里能看到且库存等于填写的册数借出一本书后图书库存减1借阅记录里多一行。如果这两条符合预期说明代码和库表是配套的可以进入下一步改造。如果不符合先检查SQL文件有没有被完整导入——很多源码的SQL文件有几十条INSERT语句部分MySQL环境在导入中途会因为某条语句报错而停住但phpMyAdmin只显示一个笼统的错误提示你需要去翻book表实际行数对不对。提示如果登录后页面样式全乱先看CSS路径是不是绝对路径。老代码里常见 link relstylesheet href/css/style.css这种写法项目放在子目录时浏览器会去域名根目录找css改成相对路径css/style.css就好。4. 改代码四个核心模块的落地逻辑4.1 登录会话防止被绕过的那几行先看登录逻辑。大多数版本的admin_login.php长这样session_start(); if (isset($_POST[submit])) { $username trim($_POST[username]); $password md5(trim($_POST[password])); $sql SELECT * FROM admin WHERE username {$username} AND password {$password}; $result mysqli_query($conn, $sql); if (mysqli_num_rows($result) 1) { $_SESSION[admin] $username; header(Location: admin/index.php); } else { $error 用户名或密码错误; } }逻辑拆开就三步从POST里取用户名和密码密码用MD5加密后和库里比对比对成功就写session并跳转。注意$password md5(...)这一行说明数据库里存的也是MD5值。如果你改了表里的密码但用的是明文这里永远登录失败。trim()去掉首尾空格是必要的不然用户在输入框多打个空格就查不到记录。$_SESSION[admin]存的是用户名而不是布尔值true这一步很关键后续每个页面要显示“当前操作人”时直接读这个变量就够。这个写法最大的问题是SQL注入username传一个 or 11 --就能直接登录因为拼接后的SQL变成SELECT * FROM admin WHERE username or 11 -- AND password...后面的条件被注释掉了。修复办法最基础的是用mysqli_real_escape_string转义$username mysqli_real_escape_string($conn, trim($_POST[username])); $password md5(trim($_POST[password]));但转义只是保底真正干净的方案是改成预处理语句我在第六章给出完整改法。现在先记住登录写session时要写一个用户可区分的标识至少是用户名不要只写$_SESSION[login] true否则所有用户共用一把钥匙后台也没法区分谁在操作。4.2 图书列表与分页LIMIT两个参数别写反图书列表页是最常被改的页面因为课设验收时老师十有八九会要求“分页显示”。常见实现如下$page isset($_GET[page]) ? intval($_GET[page]) : 1; $pageSize 8; $offset ($page - 1) * $pageSize; $sql SELECT * FROM book ORDER BY id ASC LIMIT {$offset}, {$pageSize}; $result mysqli_query($conn, $sql); $totalSql SELECT COUNT(*) AS total FROM book; $totalResult mysqli_query($conn, $totalSql); $total mysqli_fetch_assoc($totalResult)[total]; $totalPage ceil($total / $pageSize);LIMIT的两个参数第一个是偏移量跳过多少条第二个是每页条数。页码从1开始所以当前页$page对应偏移量是($page-1)*$pageSize。intval把URL参数强制转成整数防止page传一个负数或字符串把SQL搞崩。总页数用ceil向上取整保证余数为0时不会多出一页空页。这个分页在几千条记录时没问题但如果你改成“搜索图书”功能要记住加WHERE条件后offset计算不变COUNT语句必须加上同样的WHERE条件否则搜索后的总页数还是全表的翻页会翻到一堆不相关的内容。另一个容易翻车的地方是排序和分页混用时ORDER BY字段不固定页码会跳动。常见做法是给排序字段也设白名单只允许传id、name、price这几个固定值其余一律回退到默认排序。4.3 借书还书库存扣减必须保证原子性借书逻辑是这套系统里唯一需要点技术含量的地方。好的实现应该用事务包裹两步操作mysqli_begin_transaction($conn); try { $borrowTime date(Y-m-d); $backTime date(Y-m-d, strtotime(30 day)); $sql1 INSERT INTO borrow (reader_id, book_id, borrow_time, back_time, if_back) VALUES ({$readerId}, {$bookId}, {$borrowTime}, {$backTime}, 0); mysqli_query($conn, $sql1); $sql2 UPDATE book SET number number - 1 WHERE id {$bookId} AND number 0; $affected mysqli_affected_rows($conn); if ($affected ! 1) { throw new Exception(库存不足或图书不存在); } mysqli_commit($conn); } catch (Exception $e) { mysqli_rollback($conn); echo $e-getMessage(); }逻辑说明先插入借阅记录再把库存减1。关键在于UPDATE语句带了AND number 0条件并用mysqli_affected_rows检查实际影响的行数只有真正扣成功才算数。如果库存已经是0UPDATE不会报错但影响行数为0这时抛出异常并回滚借阅记录也不会插入。这就是事务的原子性要么两步都成功要么都不成功。老代码里很多是两条独立语句中间不设防借书时赶巧第二步失败库存就永久错位了。还书逻辑是对称的UPDATE borrow SET if_back 1 WHERE id ...再UPDATE book SET number number 1。还书时不需要检查库存上限但要注意防超还严谨一点应该在还书前先查borrow表里if_back是不是0如果已经是1说明这条记录重复还了直接提示操作无效就行。日期格式用date()生成万一服务器时区不对都会差上几个小时这种问题不容易被肉眼发现。后台可以加一个“所有借阅截止日期”列表专门用来排查这类数据质量问题。4.4 搜索功能的SQL拼接边界几乎每个课设都会要求加搜索。常见写法是把关键词拼进WHERE$keyword $_GET[keyword]; $sql SELECT * FROM book WHERE name LIKE %{$keyword}% OR author LIKE %{$keyword}%;这个写法搜索结果没问题但$keyword直接拼进LIKE里用户输入一个%就把全表打出来了输入一个_会把所有名字里带任意字符的书都匹配出来。更稳的写法是先用mysqli_real_escape_string转义$keyword mysqli_real_escape_string($conn, $_GET[keyword]); $sql SELECT * FROM book WHERE name LIKE %{$keyword}% OR author LIKE %{$keyword}%;注意mysqli_real_escape_string第一个参数是连接对象第二个才是字符串和mysql_real_escape_string的参数顺序不一样老代码改造时最容易栽在这个位置。搜索条件加好后还要把同样的WHERE复制到COUNT语句里否则分页页码依然对不上。搜索页还有一个体验细节输入空字符串时等价于全表查询会把分页逻辑搞得很难看。一般做法是$keyword为空时直接不拼WHERE条件查询走完整列表分支。做完这些去浏览器里分别搜“PHP”“红楼梦”、输入空字符串、输入百分号%能扛住这四种输入才算过关。5. 必踩的坑老代码本地运行的5个常见翻车现场5.1 报错Call to undefined function mysql_connect()现象页面白屏错误日志里出现Call to undefined function mysql_connect()。 原因源码用的是PHP 5时代的mysql扩展PHP 5.5开始废弃、PHP 7.0彻底移除。PHP 7以上环境里这个函数不存在。 解决想要最省事本地把PHP版本切换回5.6phpStudy里可以一键切换。想要长期用把全项目的mysql_connect、mysql_query、mysql_select_db、mysql_fetch_array改成mysqli_版本并调整参数顺序连接语句参照3.3节的改法。这里特别提醒mysql_select_db的mysqli版本是mysqli_select_db($conn, $dbname)参数顺序和原来相反很多人在这一步反复报错。全局替换时一定要把每个函数单独核对一遍不要只做前缀替换就收工。这是老代码翻新项目里最常见的血泪经验。5.2 登录成功跳回登录页现象用户名密码都对了也跳转了但后台每打开一个页面又被弹回登录页。 原因后台页面检查$_SESSION[admin]不存在就跳走。产生原因通常是session.save_path目录没有写入权限或者代码里session_start()位置不对比如在输出HTML之后才调用也有的是登录成功后跳转地址写错。 解决先确认检查登录的文件里有没有session_start()没有就加上。再看php.ini里session.save_path配置的目录是否存在且可写。XAMPP在Windows下常见这个问题把目录改成当前项目的tmp目录或给原目录加写入权限即可。最后检查login.php里登录成功后header(Location: admin/index.php)这个地址如果项目嵌套在子目录里改成绝对路径如/book/admin/index.php避免在子目录页面相对跳转时跑偏。5.3 页面中文全是问号或乱码现象图书名、读者名显示成????或干脆是乱码方块。 原因SQL文件里的表是latin1编码页面声明了utf8两边不一致也有的版本是页面没声明charset浏览器默认按GBK解析。 解决先看PHP文件里有没有header(Content-Type: text/html; charsetutf-8)没有就在每个页面顶部加。再在连接数据库后执行mysqli_set_charset($conn, utf8)对应老代码的mysql_query(set names utf8)。如果库里已经存了乱码数据用这条SQL把表转成utf8ALTER TABLE book CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;对已存在的乱码数据CONVERT不一定能救回来因为数据在写入时就已经丢了一部分字节。所以建库导入那一步就要选对字符集这比事后补救省事得多。还有一种隐藏情况php文件本身是GBK编码存储的页面上所有中文字符串都是乱码这种不是数据库问题要用编辑器把文件编码另存为UTF-8无BOM再刷新页面。5.4 MySQL 8的认证插件连不上现象conn.php里账号密码都对但报错“Authentication plugin caching_sha2_password cannot be loaded”。 原因MySQL 8默认用户认证插件是caching_sha2_password而老版本PHP的mysqli客户端不支持这个新插件。 解决两种。一是改MySQL侧把root用户的认证插件改回mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;二是换PHP版本PHP 7.2以上对MySQL 8的支持要完善得多。本地开发我一般建议双管齐下PHP端用新版本MySQL侧改插件保底。如果在服务器上改不了root可以新建一个专用账号再改成native password避免影响生产环境其他应用。改完插件后记得重启一次MySQL服务某些版本的认证缓存不会立即刷新。5.5 后台能登录但所有提交都报500现象点添加图书、添加读者报500错误或页面直接空白。 原因多半是PHP短标签。老代码里大量使用 后再跟一个换行这个换行会被当成输出内容在header()跳转之前产生“headers already sent”报错表现为登录后跳转失败。处理办法是把文件末尾的?删掉PHP本身允许不带关闭标签结尾这能让输出缓冲更干净。6. 收尾验证系统有没有真的做对以及三条进阶思路如果上面都跑通了先按下面的清单把系统过一遍确认它处在可用状态。验证项操作路径预期结果登录访问/login.php输入admin和正确密码跳转到后台首页且刷新后不失效防绕过不登录直接访问/admin/book_list.php被弹回登录页或提示请先登录添加图书后台-图书管理-添加列表出现新书库存等于填写册数借书读者管理-选择读者-选择图书-提交库存减1借阅记录新增一行还书借阅管理-还书库存加1记录if_back变为1搜索前台输入“PHP”和“%”正常结果不白屏不报错每一项都符合预期这套系统才算真正交付。接下来想往下再走我建议按顺序做三件事。第一把所有SQL拼接改成预处理语句。不必把所有页面一次改完先改登录和借书还书这两个入口这是攻击者最容易盯上的地方。$stmt mysqli_prepare($conn, SELECT * FROM admin WHERE username ? AND password ?); mysqli_stmt_bind_param($stmt, ss, $username, $password); mysqli_stmt_execute($stmt); $result mysqli_stmt_get_result($stmt);第二加一个逾期未还的提示。在后台首页加一段查询把借阅时间超过30天且if_back为0的记录列出来对图书管理员很有用。第三把admin表的MD5密码升级成password_hash。顺手做的话登录逻辑也要同步改成password_verify验证这个改动虽然小却是整套系统里安全含金量最高的一步。老系统的好处是能被你看透。我自己的习惯是拿到手先跑通再改连接层再补安全层最后动业务逻辑三层走完才敢说这套系统接住了。这套流程不光适用于图书管理系统任何一份年代久远的PHP源码都适用希望帮到你。本文还有配套的精品资源点击获取
返回列表