简介:这套酒店管理系统PHP源代码基于PHP4.2与IIS5.0环境搭建,使用Access数据库存储数据,面向酒店信息化管理与Web开发学习者,覆盖接待、餐饮、采购、经理四大核心部门,可实现客户入住退房、房间状态管理、菜单点餐、采购库存监控及财务报表等日常运营流程的自动化处理。源码共125个文件、549KB,其中71个PHP文件为核心业务逻辑,HTML与CSS文件负责前端界面,3个数据库文件存储业务数据,另有JPG、GIF图片资源及日志、配置文件,目录结构清晰便于定位各模块代码。目前已有3698人学习下载,对于想理解企业级PHP应用分层设计、业务流程建模的开发者,这是一份结构完整的实战案例。资源包附带数据库与配置文件,支持直接搭建环境运行,也便于二次开发定制。
1. 酒店管理系统PHP源码:先搞懂它是什么,再决定怎么用
运营一家二三十间房的民宿或中小酒店,房态、订单、入住退房登记全靠在纸上和Excel里来回倒腾,真正忙起来很容易漏单。这套酒店管理系统PHP源码,就是为解决这个场景做的典型PHP+MySQL增删改查项目,前台管开房登记,后台维护房价、房型和订单流水,代码结构直白,部署门槛低,一个集成环境包就能跑起来,比Java或Python那套轻量得多,很适合二开。它给两类人省时间:一类是做课程设计和外包交付的PHP开发者拿源码改业务,另一类是店长或个体经营者做内部管理选型,先本地跑通再决定要不要作为日常工具。这篇按部署顺序,把源码结构、数据表约定、配置改写、高频踩坑和二次开发边界一次讲透。
2. 源码结构拆解:入口文件、目录分层与数据表约定
2.1 先看入口:一套PHP源码的“门”在哪里
拿到压缩包别急着扔进集成环境,先解压看目录。大多数原生PHP项目不搞复杂路由,目录结构一眼能猜出功能。常见结构长这样:
hotel/ ├── admin/ # 后台管理端 │ ├── index.php # 后台登录入口 │ ├── room.php # 房态管理 │ ├── order.php # 订单列表 │ └── ... ├── includes/ # 公共函数与数据库连接 │ ├── db.php # 数据库连接 │ ├── functions.php # 通用函数库 │ └── ... ├── public/ # 静态资源与上传目录 │ ├── static/ # css/js/images │ └── uploads/ # 上传的图片 ├── index.php # 前台/预订入口 └── config.php # 全局配置这个结构里真正决定系统能不能跑的,是config.php和includes/db.php。config.php保存数据库账号、密码、库名等运行参数,db.php负责把配置转成 MySQL 连接,后面每个页面要取数据都会引用它。我拿到源码会先打开config.php,把数据库连接四项看清,再打开admin/index.php看登录逻辑,确认不是加密的乱码,再做部署。经验是,如果看到源码目录里入口文件五花八门、到处都是include却找不到统一配置,说明作者水平一般,后续维护成本会高一些。
有一个容易忽略的细节:有的源码把配置写死在db.php里,没有独立config.php。这种情况改配置就得全文搜mysqli_connect或new PDO,找到连接语句所在行去改。判断依据是连接函数出现的位置,通常只有一处,别急着全文替换密碼,不然容易误伤登录校验逻辑。
2.2 数据表设计:room、order、customer 与 price 怎么关联
酒店管理系统绕不开四类数据:房间、订单、客户、价格。建表语句通常能在压缩包里的sql/文件夹找到,文件名一般是hotel.sql或db.sql。核心表room定义如下:
CREATE TABLE room ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, room_no VARCHAR(10) NOT NULL, room_type TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1单床房 2双床房 3套房', floor_no TINYINT UNSIGNED DEFAULT 1 COMMENT '所在楼层', status TINYINT NOT NULL DEFAULT 0 COMMENT '0空闲 1入住 2脏房 3维修', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY unique_room_no (room_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这张表里必须理解两个约定。room_type用TINYINT表示房型,为什么不直接存字符串?因为房价表通过这个整数做关联,取价快,改房型名称不用动所有历史订单。status是房态机,0到3四个数字分别对应空闲、入住、脏房、维修,页面上的“绿色红色灰色”状态条就是对这个字段遍历输出。要注意UNIQUE KEY unique_room_no,房间号不允许重复,否则前台开房时会出现两单抢一间房的情况。
订单表是另一条主线,常见设计如下:
CREATE TABLE `order` ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL COMMENT '订单号,建议年月日+随机数', room_id INT UNSIGNED NOT NULL, customer_name VARCHAR(30) NOT NULL, customer_phone VARCHAR(20) NOT NULL, check_in_date DATE NOT NULL, check_out_date DATE NOT NULL, total_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00, pay_status TINYINT NOT NULL DEFAULT 0 COMMENT '0未付 1已付 2已退', status TINYINT NOT NULL DEFAULT 0 COMMENT '0进行中 1已完成 2已取消', create_time DATETIME NOT NULL, KEY idx_room_id (room_id), KEY idx_check_in_date (check_in_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;设计订单表时,order_no用什么规则生成很重要。常见做法是date('YmdHis')拼接随机串,避免并发开单撞号;价格字段必须用DECIMAL,不能用 FLOAT,因为浮点算房价会出现 199.999 这类精度误差。所有日期查询都走check_in_date索引,否则订单量过 5000 后,后台“按日期查订单”会明显变慢。I学习这套表结构时,可以顺带看到作者对索引的处理,这直接反映源码质量。
老板最关心的营业额,一般不是单独建表,而是通过统计订单里sum(total_amount)得到。所以别再自己加一张专门的销售流水表,容易造成数据不一致。
2.3 房态状态机:从“空闲”到“脏房”到底怎么流转
房态字段虽然只有四个数字,但流转逻辑是这套系统最容易改出错的地方。我总结了一套合理的流转规则:
一个正常开房流程是:前台选空闲房 → 登记客户 → 房间状态从 0 变 1;退房结账后,房间不是直接回到 0,而是先变成 2(脏房),保洁打扫后改成 0(空闲);维修则独立于整个状态链,只有空闲和脏房能改维修。拿到代码后,先搜update room set status,把改动房态的地方全部找出来对照这个规则。很多源码会在退房时一步到位把状态改成 0,这是偷懒写法,虽然跑起来没问题,但酒店真实运营场景里会漏掉保洁环节。二开时如果要加“打扫记录”功能,就需要在退房到空闲之间插入一个管理员确认动作,改的就是这一行 SQL。
2.4 权限与登录:admin 表为什么不能用明文密码
资源包里如果自带admin管理账号,建议看一下密码加密方式。靠谱的源码会存password_hash()生成的 60 位哈希串,登录时用password_verify()校验;草率一些的源码直接存明文或md5()。用 MD5 的问题是现在有大量彩虹表可查,管理后台一旦暴露,爆破只是时间问题。
如果源码本身是 MD5,可以在不改动表结构的情况下,在登录校验位置加一层自校验:把用户输入值先做md5($input)再和库里比,同时后台提供“修改密码”页面,新密码直接写password_hash($newpass, PASSWORD_DEFAULT)。这个改动对旧数据的兼容性很好,已存在的 MD5 密码还能继续登录,新改的密码则升级成 BCrypt 哈希。这样改完,admin表也不用来回迁移,适合接手老项目时快速加固。
3. 本地部署与核心配置:从环境检查到登录页跑通
3.1 环境版本选型:PHP 7.4 为什么比 8.2 更稳
部署第一步不是把源码丢进根目录,而是确认 PHP 版本。很多老源码在 PHP 8.x 上会直接白屏或报函数错误,原因是 8.0 移除了一批老函数,比如mysql_query、each(),并改变了str_replace等对 null 参数的处理。我自己的习惯是优先用 PHP 7.4 跑这类源码,它是 7.x 阶段最成熟的版本,兼容开源 CMS 和原生业务代码都可靠,MySQL 用 5.7 或 8.0 都行,但要注意 PHP 7.4 与 MySQL 8 的认证插件通常默认兼容,不用额外设置。
在集成环境根目录放一个探针文件可以快速确认环境:
<?php // env_check.php echo '当前PHP版本:' . PHP_VERSION . '<br>'; echo function_exists('mysqli_connect') ? 'mysqli扩展: 正常' : 'mysqli扩展: 缺失'; echo '<br>'; echo function_exists('pdo_mysql') ? 'pdo_mysql: 正常' : 'pdo_mysql: 缺失'; echo '<br>'; echo ini_get('upload_max_filesize') . '<br>'; echo ini_get('post_max_size') . '<br>';这个探针能看明白三件事:PHP 版本是否在 7.4 附近,MySQLi 和 PDO 扩展是否开启,上传限制是多少。不少旧源码依赖 mysqli 扩展,如果集成环境默认只开了 PDO,后台所有涉及数据库的地方会报“未定义函数 mysqli_connect”。upload_max_filesize和post_max_size决定能否上传房型图片,如果后者只有 2M,传图就会莫名失败。常见做法是直接在集成环境面板里改 PHP 配置,改完重启服务再跑探针。
3.2 数据库导入与 config 改写:你需要改的只有四个参数
将hotel.sql导入 MySQL 后,源码能不能连上库全看config.php的参数是否正确。典型配置长这样:
<?php // config.php define('DB_HOST', '127.0.0.1'); define('DB_PORT', '3306'); define('DB_NAME', 'hotel_db'); define('DB_USER', 'root'); define('DB_PASS', ''); define('DB_CHARSET', 'utf8mb4');这里真正需要改的是DB_NAME、DB_USER、DB_PASS三项,DB_HOST和DB_PORT基本不用动。如果数据库名不是hotel_db,就得改成你导入时建的库名。有两点容易踩:一是DB_PORT,如果在集成环境里把 MySQL 端口改成了 3307,这里必须同步改,否则报连接被拒绝;二是DB_CHARSET,源码写死utf8而数据库建表是utf8mb4,中文虽然能存,但遇到 emoji 或生僻字会报Incorrect string value,我建议统一用utf8mb4,这也是 MySQL 5.7 以后的默认趋势。
还有一点不得不提:config.php里不要用绝对路径define('BASE_PATH', '/Users/xxx/hotel'),这类写法在换机器部署时会立刻失效,应该用动态常量,例如define('BASE_PATH', dirname(__FILE__))。如果源码里是绝对路径,二开时统一搜BASE_PATH做替换。
3.3 伪静态与目录权限:Nginx 和 Apache 差别很大
登录页打开以后跳转 404,最常见原因是伪静态规则没有生效。Apache(通常集成环境默认)在项目根目录放一个.htaccess即可:
# .htaccess RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L]Nginx 则要在 server 配置里加try_files指令,不认.htaccess。也就是如果你从 Apache 换到 Nginx,会发现.htaccess完全不起作用,因为 Nginx 根本不读这个文件。对应配置是:
# nginx.conf 片段 server { location / { try_files $uri $uri/ /index.php?$query_string; } }try_files的含义是:先按物理文件找,找不到就把它交给index.php处理。手工部署到生产环境时用的是 Nginx 还是 Apache,决定了源码需不需要保留.htaccess。如果放 Nginx 下,建议删掉.htaccess,避免部分配置指令被服务器报 500。此外,Linux 下运行还要给public/uploads目录写权限,否则后台上传图片会提示成功但前端不显示。常见的操作是:
# 给上传目录和 runtime 目录写权限 chmod -R 755 ./public/uploads chmod -R 755 ./includes/cache检查上传目录是否写入成功,别只信网页提示,要直接看服务器上有没有生成文件。开发环境建议把错误显示打开,方便定位:在入口文件第一行加ini_set('display_errors', '1'),生产环境务必关掉。
4. 部署排错与避坑:四个高频故障的处理记录
4.1 登录页跳转 404:伪静态规则没生效
现象:后台登录页能打开,但输入账号密码点登录后地址栏跳到一个类似/admin/index.php?s=/login的地址,然后页面变成 404。
原因:常见的有两种。一种是源码使用 PATH_INFO 或兼容模式路由,URL 中的/index.php/xxx需要服务器解析;另一种是 Nginx 环境没有配置try_files导致无法重写。旧源码里写死$_SERVER['PATH_INFO']的情况下,Apache 默认支持,Nginx 却默认没有启用该参数。
解决:看项目里有没有.htaccess,有就说明按 Apache 设计;Nginx 下改server配置:
location / { try_files $uri $uri/ /index.php$uri?$args; include fastcgi_params; }改完重载 Nginx 配置,再清掉浏览器缓存测试。我遇到过改了配置仍 404 的,最后发现是 URL 里有路径大小写不一致,Linux 文件系统对大小写敏感,Admin/index.php与admin/index.php是两个文件。先确认服务器是大小写敏感的文件系统,再看入口文件路径和 URL 是否严格一致。
4.2 数据库连接异常:端口、密码与认证插件三者都可能出问题
现象:打开首页或登录页时,页面顶部直接报Warning: mysqli_connect(): (HY000/1045): Access denied for user...,或提示无法连接 MySQL server。
原因:这类报错分辨识两种:1045 是用户名密码不对或 host 不匹配,2002 是端口不通。如果确认密码没错,很可能是 MySQL 8 默认的认证插件是caching_sha2_password,而源码用的 mysqli 扩展不支持这种认证。
解决:把 MySQL 用户的认证方式改成老版mysql_native_password:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '这里写你的密码'; FLUSH PRIVILEGES;在集成面板环境中,创建站点时填的数据库密码与 PHP 配置里的DB_USER/DB_PASS必须一一对应。一个容易忽视的细节是DB_USER能否从localhost访问,如果安装时默认创建用户是root@localhost,连接字符串里写127.0.0.1有时会被当成root@127.0.0.1拒绝。我一般会先命令行mysql -uroot -p验证本地能登录,再用 PHP 探针测一遍,这样能快速隔离是 PHP 层问题还是 MySQL 层问题。
4.3 中文乱码:三处字符集要一致
现象:前台房间名称、后台订单客户姓名全部显示为问号或乱码,但数据库中直接看是中文。
原因:PHP 文件保存编码、MySQL 连接字符集、数据表默认字符集三者不一致。最常见是源码文件本身是GBK编码,数据库表是utf8mb4,页面声明是 UTF-8,三个环节互相冲突,展示层必然出错。
解决:按顺序统一。先查数据库表结构:
SHOW TABLE STATUS LIKE 'room';如果Collation不是utf8mb4_unicode_ci,就把表和字段都转成 UTF-8:
ALTER TABLE room CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;然后在config.php里确认DB_CHARSET为utf8mb4,连接时执行set names utf8mb4。对于 PHP 文件本身是 GBK 的情况,要另做处理:把源码文件用编辑器转成 UTF-8 无 BOM,否则可能在 header 输出时出现空白换行导致 session 失效。转换后有必须留意:转完文件编码后,再检查所有header('Location:...')是否前面有不可见字符,这类问题通常导致后台跳转失败,但在浏览器里又很难直接看到空格。
4.4 图片上传成功但不显示:目录权限和上传配置双重排查
现象:后台添加房型时选择图片,提示上传成功,但回到列表页还是显示默认图片,直接在浏览器开图片地址返回 404。
原因:两种因素叠加。一种是的public/uploads目录没有写权限,PHP 把文件写到了临时目录,网页以为是成功了;另一种是upload_max_filesize或post_max_size太小,大图被服务器截断,而源码没做大小校验就返回了成功状态。
解决:先确认文件到底在不在,用文件管理器看上传目录。没有文件,就查 PHP 上传配置,把upload_max_filesize调到 10M、post_max_size调到 12M,限制上传图片尺寸用前端 JS 加服务端双重校验。文件在但打开 404,查 Web 服务器中public/uploads是否被伪静态规则拦截,Nginx 有时会把带点的 URL 当作文件请求处理,这时要在 location 里增加对 uploads 目录的排除:
location ~* ^/(public/uploads)/ { }把 uploads 目录切权后,我还会顺手检查源码里列表展示图片时是用的相对路径还是拼的绝对 URL。相对路径很容易因入口文件的目录层级变化而失效,正确做法是用BASE_URL常量拼完整路径,这一点在部署和迁移时特别重要,页面会因此显示正常。
4.5 后台提示会话过期但明明刚登录:Session 文件目录不可写
现象:登录后台后点任意菜单,又跳回登录页说“登录超时”,或页面依然显示未登录。浏览器开发者工具里能看到 session_id 一直在变,或者 session 文件没有生成。
原因:PHP 的session.save_path指定的目录不存在或没有写权限。Linux 下比较典型,集成环境默认目录可能是/tmp,如果open_basedir限制运行目录,PHP 无法写 session 文件,就会每次请求都生成新会话。
解决:在配置文件中显式指定 session 目录:
ini_set('session.save_path', dirname(__FILE__) . '/runtime/session'); session_start();同时用命令确认目录可写:
mkdir -p runtime/session chmod -R 755 runtime/session如果源码在入口文件里设置了session_set_cookie_params,要注意 cookie 的有效域名和路径是否和当前环境匹配。本地用localhost访问时,域名如果被写死成了www.xxx.com,cookie 不落盘,自然一直掉线。搜session_set_cookie_params的domain参数,改成空或当前域名即可。
5. 二次开发与换皮:把一套酒店源码改成自己的项目
5.1 换肤切入点:先梳理出公共的头尾模板
拿到源码想做换皮,最容易翻车的是不懂这项目的模板组织方式。原生 PHP 项目的页面常常在同一个文件里既写 HTML 又写 SQL,换皮肤不能瞎改公共函数,否则前台列表和后台管理会被一起搞坏。我的做法是先找每个页面开头重复出现的include,比如header.php和footer.php,站点统一的导航、CSS 引入都在这里。把样式文件换成自己的 CSS,再把导航菜单项名称改成真实运营文案即可。改样式时建议分两级:第一级动颜色和字体,第二级改页面布局。如果源码使用后端模板引擎(像 Smarty),就定位.tpl文件,不用动核心 PHP,工作量瞬间少一半。
5.2 新增房型与房价联动:改数据还是改代码
酒店管理系统最常见的二开需求是加新房型。直接在后台表单里添加是理想情况,但如果源码在room_type里写死了下拉选项1、2、3,就需要在代码中找select或option的定义位置。新房价逻辑应优先考虑通过数据库实现:
INSERT INTO price (room_type, price, week_price, create_time) VALUES (4, 168.00, 198.00, NOW());加数据后,代码里要检查给前台展示价格的地方是否有switch或if分支把 4 过滤掉——不少源码在列表页用if ($type <= 3)控制是否展示新房型,防护逻辑多写一层才能保证新数据能显示出来。价格策略要区分平日价和周末价的话,可以在price表增加is_weekend字段,查询时按日期截取N或D判断:
<?php $day = date('N', strtotime($check_in_date)); $price_type = ($day >= 5) ? 'week_price' : 'price';从 5 到 7 算周末还是周五到周日都算,这个口径要提前和运营确认,做进代码后就不用每天手动改价格了。
5.3 上线迁移三步:备份、改配置、验证
本地跑通不等于直接可上线。我坚持用一套固定的上线流程:先打包本地数据,拖到线上执行恢复;再改配置文件里数据库连接、站点 URL 和上传目录;最后逐条走核心业务流程。备份和恢复是重中之重,我习惯用下面的命令做快照:
# 导出数据库结构+数据,压缩后保留日期标记 mysqldump -uroot -p hotel_db | gzip > hotel_db_$(date +%Y%m%d).sql.gz # 线上恢复 gunzip -c hotel_db_$(date +%Y%m%d).sql.gz | mysql -u线上用户 -p线上库名恢复后别急着测页面,先看三行:config.php的BASE_URL、数据库连接参数、上传目录权限。把这三项确认完,再去测开房流程,从下单到入住、退房、结算一路走通,不要只登录后台看菜单。当初我接一套老源码,就是因为没把线上数据库的timezone和字符集核对,结果线上跑出的订单时间比本地快 8 小时,客人实际凌晨 2 点入住,系统却记录成上午 10 点。从那以后我每次拿新源码都会先把探针、字符集、时间区域验证列为必做项,跑通后还要在浏览器里清空缓存蹭一遍全流程再交付,养成习惯后虽然多花十几分钟,却很少再被人叫回来修环境问题。希望这些记录能帮你把部署成本压到最低,一次跑通。
本文还有配套的精品资源,点击获取