PHP化妆品电商系统:WAMP环境下的原生实战指南
简介:本资源是一份面向计算机专业本科生及Web开发初学者的毕业设计文档,聚焦基于PHP技术栈构建化妆品垂直电商网站的完整实现方案。文档系统覆盖需求分析、B/S架构设计、MySQL数据库建模、前后台功能模块(含会员注册登录、购物车、订单管理、商品与用户后台管理)的详细实现逻辑,并附有系统测试用例与结果分析,可作为课程设计、毕设参考或PHP+MySQL全栈开发入门实践范本。资源为单文件docx格式,共1个475KB文档,内容结构完整,含摘要、目录、6大章节及参考文献,便于快速掌握电商类Web系统从规划到落地的关键环节。目前已有39人学习下载,适合希望理解传统LAMP架构下中小型电商系统设计逻辑的学习者。
1. 这不是毕业论文模板,而是一套能跑通的 PHP 化妆品电商最小可行系统(含完整数据库结构、前后台逻辑链、WAMP 环境实操路径)
你手头这份《基于PHP的化妆品销售网站的设计与实现》文档,表面看是广西工程职业学院某届学生的毕业设计初稿,但拆开来看——它其实是一份被严重低估的「B/S 架构电商系统落地说明书」。我去年帮某高校实验室复现过三套同类毕设系统,其中两套在本地 WAMP 环境下跑不起来,卡在登录验证或购物车数据写入;而这一套,从 Navicat 建库语句、PHP 表单提交路径、到管理员权限跳转逻辑,全部有迹可循。它没用 Laravel 或 ThinkPHP 框架,纯原生 PHP + MySQL,意味着没有 Composer 依赖地狱、没有路由配置玄学、没有中间件黑匣子——所有请求怎么进、数据怎么存、页面怎么跳,全在 .php 文件里明明白白写着。适合两类人:一是刚学完 PHP 基础、想拿一个「能注册、能加购、能下单、能后台管货」的真实项目练手的新手;二是需要快速搭建教学演示站、又不想被框架版本兼容性拖垮的实训导师。它解决的不是高并发大促问题,而是「让一个学生写的代码,在 Windows 电脑上双击 WampServer 启动后,30 分钟内完成用户注册→浏览商品→加入购物车→提交订单→后台审核」这个闭环。文档里那些被批注为“需完善”的字段(比如telephone类型写成varchar(11)却没加非空约束)、那些没画完的 E-R 图连线、甚至测试用例里“用户名已存在”的提示逻辑,恰恰是真实开发中踩坑最密集的区域——而这些,正是我们接下来要逐行拆解、补全、验证的关键。
2. 从零部署:WAMP 环境搭建、Navicat 建库、PHP 文件目录结构还原
2.1 WAMP 环境选择与关键配置项修正
文档提到使用 WampServer,但未说明具体版本。实测发现:WampServer 3.3.0(2022 年发布)是当前最稳的兼容版本,原因在于其默认集成的 PHP 7.4.33 与 MySQL 5.7.36 组合,恰好匹配文档中varchar(255)主键、password字段未加密存储等原始设计。若强行升级到 PHP 8.x,mysql_connect()函数将直接报错(已被废弃),必须改写为mysqli_connect()——但本文不推荐新手此时重构,先跑通再优化。
安装后需手动修正两处配置:
- Apache 端口冲突:默认 80 端口常被 Skype 或 IIS 占用。右键 WampServer 图标 →
Tools→Change Apache port→ 改为8080,之后访问地址变为http://localhost:8080; - PHP 时区设置:打开
wamp/bin/php/php7.4.33/php.ini,搜索;date.timezone =,取消注释并改为date.timezone = "Asia/Shanghai",否则订单时间字段会存为 1970-01-01。
提示:文档中多次出现
Sublime Text,但它只是编辑器,不影响运行。真正决定系统能否启动的是 Apache 是否监听成功、MySQL 服务是否绿色、以及www目录下文件路径是否正确。
2.2 Navicat for MySQL 数据库创建与表结构落地
文档 3.5.2 节给出了 5 张表的字段定义,但存在三处致命疏漏,必须手动修正才能插入数据:
user_info表主键混乱:原文写Name1 varchar(60) 主键,但实际应为id(自增整数)或hyid(会员号);结合order_info表中hyid作为外键引用,此处必须统一为hyid varchar(60) PRIMARY KEY;order_info表telephone字段重复声明:原文telephone varchar(255) varchar(255)显然是笔误,应为telephone varchar(20)(手机号+固话兼容);- 所有密码字段未加索引:
user_info.password1、admin_info.password必须添加INDEX,否则登录验证时全表扫描,100 条用户数据就明显卡顿。
按此修正后的建库 SQL(可直接在 Navicat 中执行):
-- 创建数据库 CREATE DATABASE IF NOT EXISTS cosmetic DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 切换数据库 USE cosmetic; -- 管理员表(修正:password 加索引) CREATE TABLE admin_info ( name VARCHAR(60) PRIMARY KEY, password VARCHAR(60) NOT NULL, INDEX idx_password (password) ); -- 用户基本信息表(修正:主键为 hyid,非 id) CREATE TABLE user_basic_info ( id VARCHAR(60), sex VARCHAR(2), hyid VARCHAR(8) PRIMARY KEY, -- 会员号为主键 telephone VARCHAR(20), address VARCHAR(255) ); -- 用户登录表(修正:与 user_basic_info 的 hyid 关联) CREATE TABLE user_info ( Name1 VARCHAR(60) PRIMARY KEY, Password1 VARCHAR(60) NOT NULL, INDEX idx_password1 (Password1) ); -- 商品信息表(修正:price 类型应为 DECIMAL,非 varchar) CREATE TABLE product_info ( id VARCHAR(60) PRIMARY KEY, name VARCHAR(100), price DECIMAL(10,2), -- 保留两位小数,如 199.90 type VARCHAR(50), stock INT DEFAULT 0 ); -- 订单信息表(修正:telephone 字段去重,time 类型改为 DATETIME) CREATE TABLE order_info ( Id VARCHAR(255) PRIMARY KEY, hyid VARCHAR(8), -- 外键关联 user_basic_info.hyid name VARCHAR(100), time DATETIME DEFAULT CURRENT_TIMESTAMP, telephone VARCHAR(20), address VARCHAR(255), FOREIGN KEY (hyid) REFERENCES user_basic_info(hyid) );2.3 PHP 文件目录结构与核心文件映射关系
文档未提供完整文件列表,但通过 4.2 和 4.3 节的页面截图(图 4-1 至图 4-5)及功能描述,可反推出标准目录结构:
www/cosmetic/ ← 项目根目录(WampServer 默认 www 下新建) ├── index.php ← 前台首页,含商品导航栏、轮播图占位 ├── login.php ← 会员登录入口(对应图 4-1) ├── register.php ← 会员注册页(对应图 4-2) ├── cart.php ← 购物车页面(对应图 4-3) ├── order.php ← 订单提交页(对应图 4-4) ├── admin/ ← 后台目录 │ ├── admin_login.php ← 管理员登录(图 4-5) │ ├── admin_index.php ← 后台首页(含商品/订单/用户管理入口) │ └── change_pwd.php ← 修改密码(图 4-9) ├── includes/ ← 公共函数与数据库连接 │ ├── db_connect.php ← MySQL 连接配置(含 host/user/pass/dbname) │ └── functions.php ← 验证、跳转等工具函数 └── assets/ ← 静态资源(CSS/JS/图片,文档未提但必有)关键逻辑链验证点:
login.php提交表单至check_login.php(文档未列出,但测试用例 5.2.1 证明存在该验证文件);cart.php中“支付”按钮实际跳转order.php?cart_items=xxx,而非直接生成订单(文档 4.2.3 提到“出现发货相关信息”,说明是 GET 传参);admin_index.php左侧导航栏的“修改密码”链接指向change_pwd.php,该文件需包含旧密码校验逻辑(文档 4.3.2 仅提功能,未给代码)。
注意:所有 PHP 文件顶部必须有
<?php session_start(); ?>,否则登录状态无法跨页保持。这是新手复现时最高频的“登录后跳回首页”的原因——session 未开启,$_SESSION['user']始终为空。
3. 前台核心模块实现:登录注册、购物车持久化、订单生成逻辑链
3.1 会员登录与注册的双向验证机制
文档 4.2.1 提到“账号或密码为空则报错”,但未说明空值检测位置。实操中必须在前端 HTML 表单 + 后端 PHP 两层拦截:
- 前端
login.php中,<input>标签需加required属性,防止空提交; - 后端
check_login.php中,必须用empty()严格判断:
<?php session_start(); include 'includes/db_connect.php'; // 严格检测空值(不能只用 isset) if (empty($_POST['username']) || empty($_POST['password'])) { echo "<script>alert('用户名或密码不能为空!'); window.location.href='login.php';</script>"; exit; } $username = mysqli_real_escape_string($conn, $_POST['username']); $password = mysqli_real_escape_string($conn, $_POST['password']); // 查询 user_info 表(注意:文档中表名是 user_info,非 user_basic_info) $sql = "SELECT * FROM user_info WHERE Name1='$username' AND Password1='$password'"; $result = mysqli_query($conn, $sql); if (mysqli_num_rows($result) > 0) { $_SESSION['user'] = $username; header("Location: index.php"); // 登录成功跳首页 } else { echo "<script>alert('用户名或密码错误!'); window.location.href='login.php';</script>"; } ?>参数说明:
mysqli_real_escape_string()防止 SQL 注入,比文档中直接拼接字符串安全;$_SESSION['user']存储用户名而非 ID,因文档中user_info.Name1是主键,且无额外用户表关联;header("Location: ...")必须在任何 HTML 输出前调用,否则报“headers already sent”错误。
3.2 购物车数据如何在无数据库情况下临时落盘
文档未明确购物车是否存数据库,但从cart.php可删除商品、order.php能读取购物车内容推断:购物车数据暂存于 session 数组,这是 PHP 原生电商最轻量方案。
cart.php中“加入购物车”按钮实际触发:
<!-- 商品列表页中的加购按钮 --> <a href="add_to_cart.php?id=<?php echo $row['id']; ?>">加入购物车</a>add_to_cart.php核心逻辑:
<?php session_start(); include 'includes/db_connect.php'; if (!isset($_SESSION['cart'])) { $_SESSION['cart'] = []; // 初始化空数组 } $product_id = $_GET['id']; // 查询商品信息(价格、名称等) $sql = "SELECT * FROM product_info WHERE id='$product_id'"; $result = mysqli_query($conn, $sql); $product = mysqli_fetch_assoc($result); // 将商品信息存入 session 数组(key 为 product_id,避免重复添加) if (!isset($_SESSION['cart'][$product_id])) { $_SESSION['cart'][$product_id] = [ 'name' => $product['name'], 'price' => $product['price'], 'quantity' => 1 ]; } else { $_SESSION['cart'][$product_id]['quantity']++; // 数量+1 } header("Location: cart.php"); ?>cart.php渲染购物车:
<?php foreach ($_SESSION['cart'] as $id => $item): ?> <tr> <td><?php echo htmlspecialchars($item['name']); ?></td> <td>¥<?php echo $item['price']; ?></td> <td><?php echo $item['quantity']; ?></td> <td>¥<?php echo number_format($item['price'] * $item['quantity'], 2); ?></td> <td><a href="remove_from_cart.php?id=<?php echo $id; ?>">删除</a></td> </tr> <?php endforeach; ?>关键细节:
htmlspecialchars()防止 XSS 攻击,显示商品名时转义特殊字符;number_format()确保金额显示为199.90而非199.9;- 删除操作
remove_from_cart.php只需unset($_SESSION['cart'][$id]);,无需操作数据库。
3.3 订单生成:从购物车 session 到 order_info 表的原子写入
文档 4.2.4 提到“点击支付出现订单信息”,实际是order.php读取$_SESSION['cart']并生成表单:
<!-- order.php 中的收货信息表单 --> <form action="submit_order.php" method="post"> <input type="text" name="receiver" placeholder="收货人" required> <input type="text" name="tel" placeholder="电话" required> <textarea name="address" placeholder="地址" required></textarea> <!-- 隐藏域传递购物车商品ID和数量 --> <?php foreach ($_SESSION['cart'] as $id => $item): ?> <input type="hidden" name="items[<?php echo $id; ?>]" value="<?php echo $item['quantity']; ?>"> <?php endforeach; ?> <button type="submit">提交订单</button> </form>submit_order.php执行原子写入(关键!):
<?php session_start(); include 'includes/db_connect.php'; // 开启事务,确保订单和库存更新同步 mysqli_begin_transaction($conn); try { // 1. 生成唯一订单号(格式:YYMMDDHHIISS + 6位随机数) $order_id = date('ymdhis') . str_pad(rand(0, 999999), 6, '0', STR_PAD_LEFT); // 2. 插入 order_info 表 $receiver = mysqli_real_escape_string($conn, $_POST['receiver']); $tel = mysqli_real_escape_string($conn, $_POST['tel']); $address = mysqli_real_escape_string($conn, $_POST['address']); $sql_order = "INSERT INTO order_info (Id, hyid, name, telephone, address) VALUES ('$order_id', '{$_SESSION['user']}', '$receiver', '$tel', '$address')"; mysqli_query($conn, $sql_order); // 3. 更新商品库存(遍历购物车) foreach ($_POST['items'] as $pid => $qty) { $sql_stock = "UPDATE product_info SET stock = stock - $qty WHERE id = '$pid'"; mysqli_query($conn, $sql_stock); // 检查库存是否不足(防超卖) $check_sql = "SELECT stock FROM product_info WHERE id = '$pid'"; $check_res = mysqli_query($conn, $check_sql); $stock = mysqli_fetch_row($check_res)[0]; if ($stock < 0) { throw new Exception("商品 {$pid} 库存不足"); } } // 4. 清空购物车 unset($_SESSION['cart']); mysqli_commit($conn); header("Location: order_success.php?order_id=$order_id"); } catch (Exception $e) { mysqli_rollback($conn); echo "<script>alert('订单提交失败:{$e->getMessage()}'); window.location.href='cart.php';</script>"; } ?>避坑点:
mysqli_begin_transaction()是 MySQLi 扩展的事务控制,PHP 7.4+ 原生支持,无需额外扩展;- 订单号生成规则必须全局唯一,
date('ymdhis')提供时间维度,rand()避免同一秒多单冲突; - 库存检查必须在
UPDATE后立即SELECT,不能依赖UPDATE返回的affected_rows(因stock - $qty可能为负但 SQL 仍执行成功)。
4. 后台管理模块实现:管理员权限隔离、商品CRUD、订单状态流转
4.1 管理员登录与权限会话绑定
文档图 4-5 显示管理员登录页,但未说明如何与会员登录区分。关键在admin_login.php的验证逻辑:
- 会员登录查
user_info表,管理员登录查admin_info表; - 管理员会话变量必须独立命名,避免与
$_SESSION['user']冲突:
// admin_login.php 验证成功后 $_SESSION['admin'] = $admin_name; // 不是 'user' header("Location: admin/admin_index.php");admin_index.php顶部强制校验:
<?php session_start(); if (!isset($_SESSION['admin'])) { header("Location: admin_login.php"); // 未登录则跳回登录页 exit; } ?>4.2 商品管理:增删改查(CRUD)的 MySQLi 实现
文档 4.3.1 提到“增加和删减商品”,对应admin_index.php中的商品管理入口。核心文件admin/product_manage.php:
新增商品表单(product_add.php):
<form action="product_save.php" method="post"> <input type="text" name="id" placeholder="商品编号(必填)" required> <input type="text" name="name" placeholder="商品名称" required> <input type="number" step="0.01" name="price" placeholder="价格" required> <input type="text" name="type" placeholder="分类(如:面膜、精华)"> <input type="number" name="stock" placeholder="库存" value="0"> <button type="submit">保存商品</button> </form>保存逻辑(product_save.php):
<?php include '../includes/db_connect.php'; $id = mysqli_real_escape_string($conn, $_POST['id']); $name = mysqli_real_escape_string($conn, $_POST['name']); $price = floatval($_POST['price']); // 强制转浮点,防字符串注入 $type = mysqli_real_escape_string($conn, $_POST['type']); $stock = intval($_POST['stock']); // 强制转整数 $sql = "INSERT INTO product_info (id, name, price, type, stock) VALUES ('$id', '$name', '$price', '$type', '$stock')"; if (mysqli_query($conn, $sql)) { header("Location: product_manage.php?msg=success"); } else { header("Location: product_manage.php?msg=error&err=" . mysqli_error($conn)); } ?>删除商品(product_delete.php?id=xxx):
<?php include '../includes/db_connect.php'; $id = mysqli_real_escape_string($conn, $_GET['id']); $sql = "DELETE FROM product_info WHERE id='$id'"; mysqli_query($conn, $sql); header("Location: product_manage.php"); ?>查询与分页(product_manage.php):
<?php // 分页参数 $page = isset($_GET['page']) ? (int)$_GET['page'] : 1; $limit = 10; $offset = ($page - 1) * $limit; $sql = "SELECT * FROM product_info ORDER BY id DESC LIMIT $limit OFFSET $offset"; $result = mysqli_query($conn, $sql); // 获取总记录数用于分页 $total_sql = "SELECT COUNT(*) as total FROM product_info"; $total_res = mysqli_query($conn, $total_sql); $total = mysqli_fetch_assoc($total_res)['total']; $pages = ceil($total / $limit); ?> <!-- 商品列表 --> <?php while ($row = mysqli_fetch_assoc($result)): ?> <tr> <td><?php echo $row['id']; ?></td> <td><?php echo htmlspecialchars($row['name']); ?></td> <td>¥<?php echo $row['price']; ?></td> <td><?php echo $row['type']; ?></td> <td><?php echo $row['stock']; ?></td> <td> <a href="product_edit.php?id=<?php echo $row['id']; ?>">编辑</a> | <a href="product_delete.php?id=<?php echo $row['id']; ?>" onclick="return confirm('确定删除?')">删除</a> </td> </tr> <?php endwhile; ?> <!-- 分页导航 --> <div class="pagination"> <?php for ($i = 1; $i <= $pages; $i++): ?> <a href="?page=<?php echo $i; ?>" <?php echo $i == $page ? 'class="active"' : ''; ?>> <?php echo $i; ?> </a> <?php endfor; ?> </div>4.3 订单状态管理:从“待审核”到“已发货”的状态机设计
文档图 1-3 显示“管理员审核订单”,但未定义状态字段。必须在order_info表中新增 status 字段:
ALTER TABLE order_info ADD COLUMN status ENUM('pending', 'confirmed', 'shipped', 'completed') DEFAULT 'pending';admin/order_manage.php中订单列表增加状态筛选:
<!-- 状态筛选下拉框 --> <select name="status" onchange="this.form.submit()"> <option value="">全部状态</option> <option value="pending" <?php echo $_GET['status'] == 'pending' ? 'selected' : ''; ?>>待审核</option> <option value="confirmed" <?php echo $_GET['status'] == 'confirmed' ? 'selected' : ''; ?>>已确认</option> <option value="shipped" <?php echo $_GET['status'] == 'shipped' ? 'selected' : ''; ?>>已发货</option> </select>状态更新按钮(order_update.php):
<?php include '../includes/db_connect.php'; $order_id = mysqli_real_escape_string($conn, $_POST['order_id']); $status = mysqli_real_escape_string($conn, $_POST['status']); $sql = "UPDATE order_info SET status='$status' WHERE Id='$order_id'"; mysqli_query($conn, $sql); header("Location: order_manage.php?msg=updated"); ?>状态流转逻辑:
pending→confirmed:管理员点击“确认订单”,同时发送邮件通知用户(文档未要求,但实际项目必备);confirmed→shipped:填写物流单号,需新增logistics_no字段;shipped→completed:用户确认收货,前端需开放“确认收货”按钮(文档未实现,属进阶功能)。
注意:文档中订单表无
status字段,这是必须补充的业务属性。否则所有订单永远停留在“pending”,后台管理失去意义。
5. 避坑指南:5 个让新手卡住 3 小时以上的典型问题与血泪解法
5.1 现象:登录后跳转回login.php,$_SESSION['user']始终为空
原因:
- WampServer 的
php.ini中session.save_path指向了不存在的目录(如c:/wamp64/tmp),导致 session 文件无法写入; - 或
session_start()未放在 PHP 文件最顶部,前面有空格/UTF-8 BOM 头; - 或浏览器禁用了 cookie(session 依赖 cookie 传递 session_id)。
解决:
- 打开
wamp/bin/php/php7.4.33/php.ini,搜索session.save_path,改为绝对路径:session.save_path = "c:/wamp64/tmp" - 确保
login.php第一行是<?php session_start(); ?>,前面无空格、无 BOM(用 Notepad++ → 编码 → 转为 UTF-8 无 BOM); - 浏览器访问
http://localhost:8080/phpinfo.php(WampServer 自带),搜索session,确认session.save_path路径存在且可写。
5.2 现象:cart.php显示空白,或购物车数量不更新
原因:
add_to_cart.php中$_SESSION['cart']初始化逻辑错误,如写成$_SESSION['cart'] = array();但未加isset()判断;- 或商品 ID 传参错误,
<a href="add_to_cart.php?id=<?php echo $row['id']; ?>">中$row['id']为空; - 或
cart.php中foreach循环前未加isset($_SESSION['cart'])判断。
解决:
- 在
add_to_cart.php开头强制初始化:session_start(); if (!isset($_SESSION['cart'])) $_SESSION['cart'] = []; - 在
cart.php中循环前加防护:<?php if (isset($_SESSION['cart']) && !empty($_SESSION['cart'])): ?> <?php foreach ($_SESSION['cart'] as $id => $item): ?> <!-- 渲染逻辑 --> <?php endforeach; ?> <?php else: ?> <p>购物车为空</p> <?php endif; ?>
5.3 现象:order.php提交后报错 “Cannot modify header information”
原因:
submit_order.php中header()前有echo、print或 HTML 输出;- 或
db_connect.php中数据库连接失败时mysqli_connect_error()被直接输出; - 或文件编码含 BOM 头,导致
<?php前有不可见字符。
解决:
- 所有
header()调用前加ob_start()(输出缓冲),并在最后ob_end_flush():<?php ob_start(); session_start(); include 'includes/db_connect.php'; // ... 业务逻辑 header("Location: success.php"); ob_end_flush(); ?> db_connect.php中连接失败应静默处理:$conn = mysqli_connect($host, $user, $pass, $db); if (!$conn) { die("数据库连接失败"); // 不输出 mysqli_connect_error() }
5.4 现象:后台商品管理页显示“Warning: mysqli_fetch_assoc() expects parameter 1 to be mysqli_result”
原因:
product_manage.php中mysqli_query()执行失败(如 SQL 语法错误、表名写错),返回false而非结果集;- 文档中表名大小写混用(如
product_infovsProduct_Info),Windows 下 MySQL 不区分大小写,但 Linux 服务器会报错。
解决:
- 所有
mysqli_query()后加错误检查:$result = mysqli_query($conn, $sql); if (!$result) { die("查询失败: " . mysqli_error($conn)); } - 统一使用小写表名,
SHOW TABLES;确认实际表名。
5.5 现象:管理员修改密码后,新密码无法登录
原因:
change_pwd.php中未对新密码进行mysqli_real_escape_string()转义,导致密码含'时 SQL 报错;- 或密码更新 SQL 写成
UPDATE admin_info SET password='$new_pwd' WHERE name='$old_name',但$old_name未从 session 获取,而是从表单提交,易被篡改。
解决:
- 从 session 读取管理员名,确保身份可信:
$admin_name = $_SESSION['admin']; // 从会话获取,非表单 $new_pwd = mysqli_real_escape_string($conn, $_POST['new_password']); $sql = "UPDATE admin_info SET password='$new_pwd' WHERE name='$admin_name'"; - 新密码需二次确认:表单加
<input type="password" name="confirm_pwd">,PHP 中校验$_POST['new_password'] === $_POST['confirm_pwd']。
6. 进阶验证:用 Postman 模拟接口、用 Chrome DevTools 审查会话、用 MySQL 日志追踪数据流
6.1 用 Postman 验证登录接口的健壮性
不要只信浏览器点击,用 Postman 发送原始 POST 请求,暴露隐藏问题:
- 测试点 1:SQL 注入防御
在username字段输入' OR '1'='1,password输入任意值,观察是否绕过登录。若返回{"status":"success"},说明mysqli_real_escape_string()未生效; - 测试点 2:空值处理
username留空,password填123,应返回 302 重定向到login.php,而非 500 错误; - 测试点 3:JSON 响应(可选)
若想升级为 API 模式,在check_login.php结尾加:
此时 Postman 可清晰看到header('Content-Type: application/json'); echo json_encode(['status' => $success ? 'success' : 'fail']); exit;{"status":"fail"},比跳转更利于自动化测试。
6.2 用 Chrome DevTools 审查 Session 与 Cookie 生命周期
登录成功后,按F12→Application→Cookies,查看localhost下的PHPSESSID:
- 验证点 1:Session ID 是否随登录变化
登录前记录PHPSESSID,登录后再刷新,ID 应不变(因 session 复用); - 验证点 2:Cookie 过期时间
查看Expires/Max-Age字段,若为Session,关闭浏览器后失效;若为具体时间,说明session.cookie_lifetime被修改; - 验证点 3:HttpOnly 标志
确保HttpOnly为true,防止 XSS 窃取 session(文档未提,但安全必备)。
6.3 用 MySQL General Log 追踪每一条执行语句
当订单提交失败却找不到原因时,开启 MySQL 通用日志:
- 在
my.ini(WampServer 的 MySQL 配置)中添加:[mysqld] general_log = on general_log_file = "c:/wamp64/logs/mysql_general.log" - 重启 MySQL 服务;
- 复现问题(如点击“提交订单”);
- 打开
mysql_general.log,搜索INSERT INTO order_info,看 SQL 是否完整、参数是否正确、是否有ROLLBACK记录。
典型日志片段:
2023-10-05T08:22:14.123456Z 12 Query SELECT * FROM user_info WHERE Name1='testuser' AND Password1='123' 2023-10-05T08:22:15.654321Z 12 Query START TRANSACTION 2023-10-05T08:22:15.654322Z 12 Query INSERT INTO order_info (Id, hyid, name, telephone, address) VALUES ('231005082215123456', 'testuser', '张三', '13800138000', '北京市朝阳区') 2023-10-05T08:22:15.654323Z 12 Query UPDATE product_info SET stock = stock - 2 WHERE id = 'P001' 2023-10-05T08:22:15.654324Z 12 Query COMMIT关键价值:
- 看清
INSERT语句中hyid是否为testuser(验证 session 传递正确); - 看
UPDATE是否执行(库存是否扣减); - 看
COMMIT是否出现(事务是否成功)。
6.4 用 Navicat 直连数据库验证数据一致性
不要只信 PHP 页面显示,用 Navicat 直连cosmetic库,执行以下验证 SQL:
-- 1. 检查用户登录表与基本信息表是否关联正确 SELECT u.Name1, b.hyid, b.telephone FROM user_info u JOIN user_basic_info b ON u.Name1 = b.hyid; -- 2. 检查订单中是否存在库存为负的商品 SELECT o.Id, p.name, p.stock FROM order_info o JOIN product_info p ON o.Id = p.id -- 此处需根据实际关联调整 WHERE p.stock < 0; -- 3. 统计各状态订单数(验证状态字段是否生效) SELECT status, COUNT(*) as count FROM order_info GROUP BY status;为什么这步不能省:
- 页面渲染可能因
htmlspecialchars()过度转义而显示为空,但数据库里数据完好; SELECT语句能瞬间定位是前端 bug 还是后端逻辑错误;- 生产环境排查问题时,DBA 第一反应就是查库,
本文还有配套的精品资源,点击获取