资讯动态

PHP+MySQL打造二手物品交易网站:从数据库到部署全解析

发布时间:2026/9/1 23:20:07 来源:尧图企业网站定制
简介本资源是一套完整的二手物品交易网站开发实践方案面向PHP初学者、Web开发入门者及课程设计学生解决从零构建动态B2C类信息平台的核心需求。压缩包共含全部项目源码与配套文档以PHP脚本、MySQL建表与SQL语句、Apache配置说明及系统设计说明书为主源码经实测可百分百成功运行涵盖用户注册登录、商品发布、分类检索、留言互动等典型功能模块。资源大小为971KB轻量实用便于本地快速部署与代码研读。目前已有1141人学习下载适合用于课程设计、毕业设计参考或PHPMySQL全栈开发能力训练——读者可直接运行调试、理解MVC简易架构实现逻辑、掌握前后端交互流程并基于现有结构进行功能扩展与界面优化。 做二手物品交易网站这个选题放在今天看依然不过时。不管是课程设计、毕业设计还是自己练手接个外包单子PHP MySQL Apache 这套经典组合都算得上最稳的起步方案。我之前带过不少新人也帮人维护过类似的项目源码包这里把整个“二手物品交易网站”从需求拆解、数据库设计、核心代码落地到部署上线的完整过程捋一遍代码和思路都会给到保证你照着能跑起来也能搞清楚每一步到底在干什么。1. 项目概况与选型考量1.1 这是一个什么项目适合谁参考简单说这是一个完整的、可运行的二手闲置交易信息网站。用户能注册登录、发布自己闲置的商品、给商品配图、设置价格和成色描述其他用户能按分类浏览、搜索关键词、查看商品详情、收藏商品还能给卖家留言询价。管理员登录后台之后可以管理分类、审核下架违规商品、管理用户账号。整个项目包含前台交易浏览功能和后台管理功能配套源码和设计文档。这套东西特别适合下面几类人计算机相关专业的学生拿来做课程设计、毕业设计的原型尤其是需要快速出成果、又要能讲清楚设计思路的场景。刚学完 PHP 基础、MySQL 增删改查想找个完整项目练手把零散知识点串起来的开发者。接外包或者做私活的技术人员需要一套基础代码二次开发快速套壳改造成不同行业的闲置交易平台。我见过很多学生拿 PHP 写商城、写论坛最后发现二手交易网站反而是性价比最高的选题。因为它的业务逻辑足够完整——有用户体系、有商品流转、有消息互动又没有电商系统那么复杂不需要管库存、SKU、支付结算这些烧脑的东西作为练手项目刚好卡在“能学到东西”和“能做出来”的平衡点上。1.2 为什么选 PHP MySQL Apache 这个组合这个技术栈被很多人吐槽“老”但它能长盛不衰是有原因的。PHP 的语法接近 C 和 Java 的混合体上手门槛低写业务逻辑的效率特别高尤其适合这种页面导向的网站。你说它不够优雅但对这种体量的项目开发速度和维护简单才是第一位的。MySQL 在中小型应用场景下依然是绝对的主流。二手交易网站的数据量级用 MySQL 存储用户、商品、留言、收藏这些表性能绰绰有余。更重要的是MySQL 的资料多到爆炸遇到任何问题一搜就有答案这对学习者来说是最重要的隐性价值。Apache 作为 Web 服务器配置简单配合 mod_php 模块解析 PHP 文件极其稳定。虽然现在 Nginx 在某些高并发场景下更吃香但对于日均几百上千访问量的二手交易网站来说Apache 完全够用而且 phpstudy、XAMPP 这类集成环境默认就是 Apache开箱即用省去很多环境折腾的时间。还有一点必须说这套组合的成本几乎为零。Linux Apache MySQL PHP 全开源免费部署到云服务器上一年的服务器费用就是全部成本。做学习项目或者学生作业不需要授权费不需要商业许可没有任何坑。2. 系统整体设计与功能架构2.1 需求拆解从场景到功能清单先别急着写代码做项目第一步永远是捋需求。二手物品交易网站核心场景就一句话有闲置的人想卖掉有需求的人想买到便宜的二手。围绕这个场景把用户操作流程盘一遍我作为卖家我希望注册账号登录后能发布商品要填标题、描述、价格、分类、成色能传图片。能看到自己发布过哪些商品能修改信息、能下架。能收到卖家的留言方便进一步沟通价格和交易地点。我作为买家我希望不用登录也能浏览所有商品但我想收藏、留言就必须登录。能按分类筛选能搜索商品标题关键词。看到感兴趣的商品能看图、看描述、看卖家信息和发布时间。能在商品详情页给卖家留言询价。作为管理员我希望登录独立的后台能看到所有用户和商品。能添加或删除商品分类。能下架违规商品、封禁异常用户。首页有简单的数据统计比如用户总数、商品总数。功能清单理出来之后整个系统的模块结构就清晰了用户模块注册、登录、退出、个人信息维护。商品模块发布、列表、详情、搜索、分类筛选、上下架。互动模块商品留言、收藏。后台管理模块管理员登录、用户管理、商品管理、分类管理。辅助模块分页、图片上传、表单验证、安全过滤。2.2 模块划分与页面流转从页面流转的角度整个网站的用户路径是这样的访客打开首页看到的是最新发布的二手商品列表顶部有导航栏可以按分类浏览也可以输入关键词搜索。点击某个商品进入详情页这时候如果想留言、收藏就会被引导到登录页。登录成功跳转回刚才的商品页就可以正常留言了。用户登录后顶部导航会出现“发布闲置”和“个人中心”的入口。发起交易后比较常规的做法是线下自行联系交易也有版本会做简单的订单流程但多数课程设计级别项目都是留言沟通制这个在我看来完全合理因为二手交易本身信任成本高线下面交才是主体。后台的流转相对简单管理员通过独立入口登录进入控制面板左侧菜单是用户列表、商品列表、分类管理、数据看板点击每一项在右侧展示对应的表格操作按钮直接嵌在表格行里比如下架、删除、启用、禁用。功能模块确定以后下一步就是数据库设计。这一步直接决定后面写代码是顺风顺水还是到处打补丁。3. 数据库设计与实现3.1 核心数据表结构说明很多新手做数据库设计的时候喜欢堆字段想到什么加什么结果表结构臃肿查询效率还差。二手交易网站的数据表其实可以非常克制我整理了下面这一套核心表结构基本上能覆盖所有功能需求。users 用户表字段名类型说明idINT 主键自增用户IDusernameVARCHAR(50) 唯一用户名passwordVARCHAR(255)加密后的密码emailVARCHAR(100)邮箱phoneVARCHAR(20)手机号avatarVARCHAR(255)头像路径roleTINYINT1-普通用户 2-管理员statusTINYINT1-正常 0-禁用created_atDATETIME注册时间categories 分类表字段名类型说明idINT 主键自增分类IDnameVARCHAR(50)分类名称sort_orderINT排序权重created_atDATETIME创建时间goods 商品表字段名类型说明idINT 主键自增商品IDuser_idINT发布用户ID关联users表category_idINT分类ID关联categories表titleVARCHAR(100)商品标题descriptionTEXT商品描述priceDECIMAL(10,2)售价original_priceDECIMAL(10,2)原价用于展示折扣力度condition_levelTINYINT成色1-全新 2-几乎全新 3-轻微使用痕迹 4-明显使用痕迹cover_imageVARCHAR(255)封面图路径imagesTEXT额外图片多个用逗号分隔statusTINYINT1-在售 0-已下架 2-已售出view_countINT浏览量created_atDATETIME发布时间messages 留言表字段名类型说明idINT 主键自增留言IDgoods_idINT商品IDuser_idINT留言用户IDcontentVARCHAR(500)留言内容replyVARCHAR(500)卖家回复created_atDATETIME留言时间favorites 收藏表字段名类型说明idINT 主键自增收藏IDuser_idINT用户IDgoods_idINT商品IDcreated_atDATETIME收藏时间3.2 关键字段与设计取舍这套表结构有几个设计细节值得展开说因为从这些细节能看出设计者有没有实战经验。密码字段我特意用了 VARCHAR(255)而不是很多入门教程里的 VARCHAR(32)。原因很直接如果用 MD5 加密32位确实够放但 MD5 现在已经不安全了撞库成本极低。我会在代码里用 password_hash() 函数生成密码哈希这个函数默认输出的字符串超过 60 个字符所以字段必须留够长度。别小看这个细节我见过有人照网上老教程建了 32 位的字段最后 password_hash() 存不进去排查半天才发现是字段长度问题。价格字段用 DECIMAL(10,2) 而不是 FLOAT 或者 DOUBLE这是金融数据的铁律。浮点数在计算机里是近似存储0.1 0.2 不等于 0.3 的坑在 PHP 里同样存在。价格做比较、做统计的时候如果用浮点字段很容易出现莫名其妙的结果DECIMAL 是精确小数类型按字符串存储做金额计算完全没问题。images 字段用逗号分隔字符串看起来不太符合第一范式很多人会建议单独建一张商品图片表。说实话如果图片数量不固定、需要做图片轮播单独建表更规范。但在这个项目里考虑到课程设计和毕业设计的体量多图的需求再复杂也就三到五张图用逗号分隔可以直接在 PHP 里 explode() 拆成数组省去一次联表查询。这是典型的空间换时间代码更简洁性能反而更好。status 字段用了 TINYINT 而不是 ENUM这是因为 ENUM 在后面想加状态的时候需要改表结构而 TINYINT 直接约定数字含义就行扩展性更强。这里约定 1 在售、0 下架、2 已售出将来想加一个“审核中”的状态直接加个 3 就行不用动表。那么问题来了为什么商品表里不直接存一个 user_name 字段避免每次查商品还要联表查用户这样做确实可以省一次 JOIN但如果用户改了昵称商品列表里显示的卖家名就全错了。所以宁可每次都 JOIN 一下也要保证数据的一致性。这是数据库设计里非常重要的一条原则数据只存一份其他需要的地方通过关联去取。4. 核心功能模块的代码落地4.1 数据库连接封装与安全基础数据库连接这块我不建议每页都写一遍 new mysqli()而是统一封装成一个工具类。同时强烈建议使用 PDO 而不是 mysqli 原生方式。PDO 支持多种数据库驱动将来万一要迁移到 PostgreSQL 或者 SQLite只需要改一行 DSN 配置代码逻辑一行都不用动。更重要的是PDO 对预处理语句的支持非常友好能从根本上杜绝 SQL 注入。?php // config.php define(DB_HOST, 127.0.0.1); define(DB_NAME, second_hand); define(DB_USER, root); define(DB_PASS, your_password); define(DB_CHARSET, utf8mb4); function db(): PDO { static $pdo null; if ($pdo null) { $dsn mysql:host . DB_HOST . ;dbname . DB_NAME . ;charset . DB_CHARSET; $options [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES false, ]; try { $pdo new PDO($dsn, DB_USER, DB_PASS, $options); } catch (PDOException $e) { exit(数据库连接失败 . $e-getMessage()); } } return $pdo; }这里有几个细节值得注意。PDO::ATTR_ERRMODE 设置为 ERRMODE_EXCEPTION意味着任何 SQL 错误都会抛异常我们可以在全局捕获并记录日志而不是让错误默认静默或者输出到页面上。PDO::ATTR_EMULATE_PREPARES 设置为 false强制使用 MySQL 原生的预处理这个开关能让 SQL 注入防护做得更彻底。4.2 用户注册登录与会话保持用户注册登录是整个系统的地基如果这里有漏洞后面全是白搭。注册的核心逻辑是接收表单、验证数据、检查用户名是否重复、密码加密、插入数据库。密码加密用 PHP 内置的 password_hash()验证用 password_verify()这两个函数比你自己写任何加密算法都靠谱。?php // register.php 处理逻辑 require_once config.php; $username trim($_POST[username] ?? ); $password $_POST[password] ?? ; $email trim($_POST[email] ?? ); if (mb_strlen($username) 3 || mb_strlen($username) 20) { exit(用户名长度需要在3到20个字符之间); } if (strlen($password) 6) { exit(密码至少6位); } $stmt db()-prepare(SELECT id FROM users WHERE username ?); $stmt-execute([$username]); if ($stmt-fetch()) { exit(该用户名已被注册); } $hash password_hash($password, PASSWORD_DEFAULT); $stmt db()-prepare(INSERT INTO users (username, password, email, created_at) VALUES (?, ?, ?, NOW())); $stmt-execute([$username, $hash, $email]); header(Location: login.php);登录的判断就一条核心 SQL根据用户名查出这条记录然后用 password_verify() 比较密码。不要把密码查出来之后再用 MD5 加密比对那是错误做法。所有以任何形式存储明文密码、可逆加密密码的设计都应该直接丢进垃圾桶。登录成功之后用 $_SESSION 保存用户信息。注意这里只保存用户 ID 和用户名不保存密码哈希。把密码放进 Session 是很多人容易犯的错误万一 Session 文件被读取密码哈希就泄露了。?php session_start(); $_SESSION[user_id] $user[id]; $_SESSION[username] $user[username]; $_SESSION[role] $user[role];涉及到“登录后才能操作”的页面在页面最顶端加一段权限验证?php session_start(); if (empty($_SESSION[user_id])) { header(Location: login.php?redirect . urlencode($_SERVER[REQUEST_URI])); exit; }redirect 参数的意思是被拦截跳转登录之后能自动跳回用户本来想看的页面这个细节体验很好。很多新手只写“请先登录”然后跳回首页用户得重新找那个商品很傻。4.3 商品发布与图片上传商品发布页分为两块前端 HTML 表单和后端 PHP 处理脚本。表单字段对应商品表的各个列图片上传单独处理。图片上传是坑最多的环节我重点说。?php // publish.php 处理逻辑 require_once config.php; session_start(); if (empty($_SESSION[user_id])) { header(Location: login.php); exit; } $title trim($_POST[title] ?? ); $category_id (int)($_POST[category_id] ?? 0); $price (float)($_POST[price] ?? 0); $description trim($_POST[description] ?? ); $condition_level (int)($_POST[condition_level] ?? 0); if ($title || mb_strlen($title) 100) { exit(请填写正确的商品标题); } if ($price 0) { exit(价格必须大于0); } if ($condition_level 1 || $condition_level 4) { exit(请选择商品成色); } // 处理封面图 $cover_image ; if (!empty($_FILES[cover_image][name])) { $upload_dir uploads/; if (!is_dir($upload_dir)) { mkdir($upload_dir, 0755, true); } $file_ext strtolower(pathinfo($_FILES[cover_image][name], PATHINFO_EXTENSION)); $allowed_ext [jpg, jpeg, png, gif, webp]; if (!in_array($file_ext, $allowed_ext)) { exit(图片格式不支持只允许 jpg/png/gif/webp); } $check getimagesize($_FILES[cover_image][tmp_name]); if ($check false) { exit(文件不是有效的图片); } $new_name uniqid(goods_, true) . . . $file_ext; if (move_uploaded_file($_FILES[cover_image][tmp_name], $upload_dir . $new_name)) { $cover_image $upload_dir . $new_name; } else { exit(图片上传失败请检查目录权限); } } $stmt db()-prepare( INSERT INTO goods (user_id, category_id, title, description, price, condition_level, cover_image, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, 1, NOW()) ); $stmt-execute([$_SESSION[user_id], $category_id, $title, $description, $price, $condition_level, $cover_image]); header(Location: my_goods.php);图片上传最容易踩的坑我一个个说。文件类型校验只判断扩展名是不够的攻击者把一段恶意脚本命名为 .jpg 就能绕过所以必须用 getimagesize() 检查文件真实内容。这个函数会读取文件头信息只有文件真的是图片才会返回数组否则返回 false。这是防止图片马最基本的手段。上传目录要动态创建但要注意 Windows 的 Apache 环境和 Linux 环境目录权限不一样。Linux 下 uploads 目录需要给 www-data 用户写权限否则 move_uploaded_file 会静默失败而错误信息又不够直观。建议在部署文档里明确写上 chown -R www-data:www-data uploads 这一步。文件名不能直接用用户原始文件名因为可能包含中文和特殊字符也可能撞名覆盖。用 uniqid() 生成文件名可以保证并发下不会重名。4.4 商品列表、搜索与分页商品列表页是整个项目里查询逻辑最复杂的部分因为它要支持分类筛选、搜索关键词、按价格排序、分页显示还要 JOIN 用户表拿卖家信息。一个安全的做法是先把查询条件拼进一个数组最后统一绑定参数执行。?php $where []; $params []; if (!empty($_GET[category_id])) { $where[] g.category_id ?; $params[] (int)$_GET[category_id]; } if (!empty($_GET[keyword])) { $where[] g.title LIKE ?; $params[] % . $_GET[keyword] . %; } $where[] g.status 1; // 只显示在售商品 $whereSql $where ? WHERE . implode( AND , $where) : ; $page max(1, (int)($_GET[page] ?? 1)); $pageSize 12; $offset ($page - 1) * $pageSize; $countStmt db()-prepare(SELECT COUNT(*) FROM goods g $whereSql); $countStmt-execute($params); $total (int)$countStmt-fetchColumn(); $totalPages max(1, ceil($total / $pageSize)); $sql SELECT g.*, u.username, c.name AS category_name FROM goods g LEFT JOIN users u ON g.user_id u.id LEFT JOIN categories c ON g.category_id c.id $whereSql ORDER BY g.created_at DESC LIMIT $offset, $pageSize; $stmt db()-prepare($sql); $stmt-execute($params); $goodsList $stmt-fetchAll();分页的页码计算有个常见 bug当搜索出来的结果不足一页时$totalPages 可能变成 0这样前端翻页条就傻了。用 max(1, ceil($total / $pageSize)) 可以兜底保证至少有一页。LIMIT 的 offset 直接拼进 SQL 是因为它是整型前面已经做了 (int) 强转不存在注入风险。这是 PHP 里比较标准的分页写法参数绑定的部分只绑定用户可控的 where 条件。搜索这里用的是 LIKE %关键词%这个写法在数据量大的时候不走索引全表扫描性能会下降。但考虑到二手网站的商品表通常几千条、几万条封顶完全能扛住。如果真到了几十万条那时候就应该考虑引入 Elasticsearch 或者 MySQL 全文索引了。这就是“用合适的工具解决合适的问题”。商品列表的推荐展示逻辑还有个小技巧除了按发布时间倒序可以加一个加权排序比如浏览量高、价格适中的商品排前面。最简单的实现是 ORDER BY (view_count * 0.2 1) * created_at 之类的表达式但要注意别把 SQL 搞得太复杂索引会失效。这个项目里先按时间倒序已经够用了。5. 环境搭建与部署上线5.1 Windows 本地环境的一键配置很多新手卡在第一步环境装不起来。我推荐直接用 phpstudy 或者 XAMPP 这样的集成环境它们把 Apache、MySQL、PHP 打包在一起双击安装就完事省去自己配置各种扩展的痛苦。以 phpstudy 为例安装完之后需要注意几个点PHP 版本建议选 7.4 或者 8.0不要选 PHP 5.x太老、有安全漏洞很多新语法也不支持。网站根目录指向项目文件夹默认是 phpstudy 安装目录下的 WWW。MySQL 的 root 密码默认是 root但有些版本默认空密码连接时报错先试试空密码。启动之后确认 Apache 和 MySQL 两个服务都是绿色运行状态。项目文件放入 WWW 目录后浏览器访问 http://localhost/项目名/index.php如果能正常打开页面说明环境跑通了。如果报 404检查项目目录里是否有 index.php 文件如果报数据库连接失败检查 config.php 里的数据库配置是否和本地 MySQL 一致。5.2 Linux 服务器部署要点正式上线建议用 Linux 服务器Ubuntu 或 CentOS 都可以。部署几个核心要点说一下。Apache 安装后网站默认根目录在 /var/www/html默认运行用户是 www-data。这就带来两个常见坑一是项目文件权限必须是 www-data 可读二是上传目录必须 www-data 可写。如果项目文件是用 root 账号上传的Apache 可能没权限读取。配置命令sudo chown -R www-data:www-data /var/www/html/项目名 sudo chmod -R 755 /var/www/html/项目名 sudo chmod -R 775 /var/www/html/项目名/uploadsMySQL 在 Linux 上安装后默认 root 只能通过 unix socket 登录用 TCP/IP 远程连会报 Access denied。本地项目连接不受影响但如果你用 Navicat 等工具调试就需要注意。解决方法是创建一个新的数据库用户并授予对应的数据库权限CREATE USER second_userlocalhost IDENTIFIED BY 复杂的密码; GRANT ALL PRIVILEGES ON second_hand.* TO second_userlocalhost; FLUSH PRIVILEGES;这里强调一下不要用 MySQL 的 root 账号作为网站业务的数据库用户。root 权限太大万一代码里有 SQL 注入漏洞整个数据库都会被拖走包括其他数据库。给网站单独建账号只授予一个库的权限这是最基本的安全隔离。5.3 上线前必须检查的清单很多人项目在本机跑得好好的一上线就各种问题。我总结了一份上线前检查清单配置文件里的数据库密码是否换成了线上的强密码而不是本机的弱密码。error_reporting 是否设置为关闭或者只记录日志不能在页面上输出报错信息否则可能泄露服务器路径和 SQL 语句。uploads 目录里的测试图片是否需要清理。Apache 的 mod_rewrite 是否启用如果需要伪静态 URL否则所有的 .php 链接都会 404。PHP 的 file_uploads 是否开启upload_max_filesize 和 post_max_size 是否满足图片上传需求。网站是否强制使用 HTTPS可以在 Apache 配置里加一个 301 跳转把 HTTP 请求全部转到 HTTPS。VirtualHost *:80 ServerName yourdomain.com Redirect permanent / https://yourdomain.com/ /VirtualHost VirtualHost *:443 ServerName yourdomain.com DocumentRoot /var/www/html/项目名 Directory /var/www/html/项目名 AllowOverride All Require all granted /Directory /VirtualHostSSL 证书直接用免费的 Lets Encryptcertbot 一条命令就能搞定。现在浏览器对非 HTTPS 网站的提示已经非常明显了不配 HTTPS用户的信任度会大打折扣传个密码都心虚。6. 常见问题与排查实录6.1 中文乱码的根因与解决中文乱码是 PHP MySQL 项目里最经典的问题几乎所有新手都会遇到一次。乱码的根源只有一句话从 MySQL 取出来的数据编码和 HTML 页面的展示编码不一致。解决方案从三个方面下手第一数据库本身设置 utf8mb4 编码。建库的时候执行CREATE DATABASE second_hand DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;第二PDO 连接字符串里指定 charsetutf8mb4这就是上面 config.php 里那段代码干的事。第三HTML 页面头部加 meta 标签meta charsetUTF-8如果这几个地方都对了乱码基本消失。还有一个小坑是 PHP 文件本身的编码必须是 UTF-8 无 BOM如果用 Windows 自带的记事本保存可能会存成 UTF-8 with BOM 格式导致页面顶部出现几个不可见的字符刷新页面报“headers already sent”错误这属于编码相关的“疑难杂症”排查起来特别浪费时间。6.2 图片上传失败与路径问题图片上传失败常见的现象是点击提交后没有报错但商品列表里图片位置是空的。这种问题八成是 move_uploaded_file 移动失败原因可能是uploads 目录不存在代码里虽然有 is_dir 判断和 mkdir 创建但 mkdir 可能因为权限不足失败了。uploads 目录存在但不可写Linux 下尤其常见。服务器限制了 POST 数据大小或者上传文件大小PHP 的 upload_max_filesize 默认只有 2M传一张大图就被砍了。表单忘记加 enctypemultipart/form-data这是最基础的坑没有这个属性文件框内容根本不会上传。排查思路是分步打印 $_FILES 数组看 error 字段的值。php 的 $_FILES[cover_image][error] 等于 0 表示正常等于 1 表示超过 upload_max_filesize等于 2 表示超过表单的 MAX_FILE_SIZE等于 3 表示文件只上传了一部分等于 4 表示没有选择文件。根据错误码定位原因是最快的排查路径。还有一个非常容易忽略的问题图片路径的存储。我在代码里存的是相对路径比如 uploads/goods_xxx.jpg。如果页面使用了 rewrite 伪静态或者链接层级比较深直接用这个相对路径可能被浏览器解析错位置。稳妥的做法有两种一种是存储的时候直接存成绝对路径比如 /uploads/xxx.jpg前面加个斜杠表示从域名根目录开始另一种是在展示层统一拼接域名前缀。建议用第一种简单粗暴不出错。6.3 数据库连接失败排查思路数据库连接失败是部署阶段最常见的错误。页面直接报 SQLSTATE[HY000] [1045] Access denied for user或者 SQLSTATE[HY000] [2002] Connection refused两个错误码的解决方向完全不同。1045 是认证失败说明用户名或者密码不对。检查 config.php 里的 DB_USER 和 DB_PASS 是否和 MySQL 实际账号一致注意 MySQL 的用户名其实是“用户名主机名”的组合如果在命令行能用 root 登录但代码里报 1045多半是代码用了 root 但密码不对。2002 是连接不上服务器。检查 MySQL 是否在运行服务器端口是不是默认的 3306如果改了端口DSN 里要拼上。还有一个常见情况MySQL 只监听了本地回环地址监听 127.0.0.1 还是 0.0.0.0 决定了代码里 DB_HOST 应该写什么。如果是同机部署DB_HOST 写 127.0.0.1 最稳省去远程连接权限的问题。6.4 SQL 注入与 XSS 防护最后专门说安全问题。二手交易网站这样的业务系统最常见的攻击方式就是 SQL 注入和 XSS 跨站脚本。SQL 注入的根源是直接把用户输入拼进了 SQL 语句。只要用了 PDO 预处理 参数绑定SQL 注入基本就堵死了。但有个前提预处理只对参数有效表名、字段名、排序方向、LIMIT 的 offset 这种结构性的部分是不能用 ? 占位符绑定的。所以我的代码里涉及到这些地方要么用白名单判断要么用 intval 强制转成整数。这是很多初级开发者容易忽略的盲区用了预处理就在所有查询里放心大胆拼字符串结果还是被注入。XSS 攻击的核心是用户输入的脚本被当成页面代码执行了。最简单的防护是在输出位置做转义用 htmlspecialchars() 把 HTML 特殊字符转成实体。发布商品、留言这些环节输入框可以不做太严格的过滤因为用户可能真的想输入 和 符号但输出到页面的时候一定要转义。建议写一个全局的 e() 函数function e($str) { return htmlspecialchars((string)$str, ENT_QUOTES, UTF-8); }然后在所有模板里输出变量的时候都用这个函数包裹? e($goods[title]) ?这个习惯养成之后XSS 风险会大幅降低。顺便说后台管理界面的模板也要用管理员后台被攻击造成的危害往往更大。7. 一些可以继续扩展的方向项目做完如果能跑通恭喜说明你的 Web 开发基本功已经跨过了一道坎。但如果你还有余力或者想把这个项目做得更出彩下面几个方向可以试试。第一加一个“站内信”功能。目前留言板是公开的二手交易中买卖双方经常需要私密沟通价格和联系方式站内信能把这个流程补全。实现思路是在 messages 表基础上加一个 receiver_id 字段用来标识收件人增加已读未读状态。第二做商品图片的批量上传。封面图解决了但详情页通常需要多角度展示商品瑕疵这是二手交易建立信任的关键。可以用前端的多文件上传组件后台在原来 images 字段的逗号分隔字符串上做追加展示的时候 explode 成数组循环输出img标签。第三增加简单的搜索排序和筛选。比如按价格区间、按成色、按发布时间排序。这些都是商品列表页 SQL 的小改动但用户体验提升是实打实的。当物品数量多了用户不可能一页页翻筛选和排序就是刚需。第四引入 Redis 做缓存。当首页商品列表的数据量变大之后每次都去 MySQL 全表查询会浪费资源。把首页数据缓存到 Redis设置五分钟过期时间访问量大的情况下效果立竿见影。我在实际做这类项目的时候有个体会真正难的不是哪个技术点而是把业务逻辑想清楚之后再一步步把代码落到地上。很多初学者喜欢先研究框架、研究各种高大上的中间件反而忽略了最基础的原生 PHP 和 SQL 能力。这个二手交易网站用到的技术都非常基础但它把一个完整的业务闭环做出来了。如果你能自己独立把这个项目的每个模块都手写一遍再回头去看 ThinkPHP、Laravel 这类框架理解起来会顺畅得多因为你已经知道某个功能在底层是如何实现的框架只是帮你省了重复劳动而已。最后分享一个实际部署中的小教训上线前一定检查服务器时间MySQL 的 NOW() 函数取的是数据库服务器系统时间如果服务器时区不对商品发布时间会相差好几个小时。我当年第一次部署就踩过这个坑商品列表的时间显示和实际差了 8 个小时排查了老半天才发现是 Linux 服务器没设时区。一条 timedatectl set-timezone Asia/Shanghai 就能解决的事愣是浪费了我一个下午。这些“不起眼”的问题往往才是真实项目里最耗时间的部分。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价