本文将详细介绍如何使用 PHP 和 MySQL 搭建一个功能完善的在线游戏网站,并附上完整的代码。目前,该网站已收录 2000+ 游戏,支持分类筛选、搜索、分页、随机推荐等功能。
在线预览链接1:game.haiyong.site/
预览链接2(新网址):peergame.cn/
项目概述
1. 功能特点
- 游戏展示:支持按分类、热门、随机等方式展示游戏。
- 分类筛选:提供多种游戏分类,用户可以根据兴趣筛选游戏。
- 搜索功能:支持按游戏名称搜索。
- 分页功能:游戏列表支持分页显示,提升用户体验。
- 点击统计:记录每个游戏的点击次数,方便统计热门游戏。
- 响应式设计:适配 PC 和移动端,提供良好的浏览体验。
2. 技术栈
- 前端:HTML、CSS、Bootstrap、JavaScript
- 后端:PHP
- 数据库:MySQL
@TOC
一、数据库设计与连接
1. 表结构
数据库包含以下表:
1.1 games
表
存储游戏的基本信息。
字段名 | 类型 | 说明 |
---|---|---|
id |
INT | 游戏 ID |
name |
VARCHAR(255) | 游戏名称 |
description |
TEXT | 游戏描述 |
game_url |
VARCHAR(255) | 游戏链接 |
image_url |
VARCHAR(255) | 游戏封面图链接 |
clicks |
INT | 点击次数 |
1.2 categories
表
存储游戏分类信息。
字段名 | 类型 | 说明 |
---|---|---|
id |
INT | 分类 ID |
category_name |
VARCHAR(255) | 分类名称 |
1.3 game_category
表
存储游戏与分类的关联关系。
字段名 | 类型 | 说明 |
---|---|---|
game_id |
INT | 游戏 ID |
category_id |
INT | 分类 ID |
2.使用 PDO 连接 MySQL
PHP 的 PDO(PHP Data Objects)是一个轻量级的数据库访问抽象层,支持多种数据库(如 MySQL、PostgreSQL 等)。以下是连接 MySQL 的代码:
php
<?php
// 数据库连接配置
$host = "localhost"; // 数据库地址
$dbname = "your_database"; // 数据库名称
$username = "your_username"; // 数据库用户名
$password = "your_password"; // 数据库密码
try {
// 创建 PDO 实例
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
// 设置错误模式为异常模式
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
// 捕获异常并输出错误信息
die("数据库连接失败: " . $e->getMessage());
}
?>
关键点
PDO
:PHP 的数据库访问抽象层,支持多种数据库。ATTR_ERRMODE
:设置错误处理模式,ERRMODE_EXCEPTION
表示抛出异常。try-catch
:捕获数据库连接异常,避免直接暴露错误信息。
二、数据处理
以下是对这段代码的详细展开说明,涵盖分类筛选、搜索、分页、排序等功能的实现逻辑。
2.1.处理分类和搜索
2.1.1 获取分类和搜索条件
php
$category_filter = isset($_GET['category']) ? trim($_GET['category']) : '';
$searchQuery = isset($_GET['search']) ? trim($_GET['search']) : '';
功能:
- 从 URL 参数中获取用户选择的分类(
category
)和搜索关键词(search
)。 - 使用
trim()
函数去除多余的空格。
示例:
- 如果用户访问
?category=动作&search=冒险
,则:$category_filter = "动作"
$searchQuery = "冒险"
2.1.2 分页设置
php
$gamesPerPage = 20; // 每页显示的游戏数量
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1; // 当前页码
$offset = ($page - 1) * $gamesPerPage; // 计算偏移量
功能:
- 设置每页显示的游戏数量(
$gamesPerPage
)。 - 从 URL 参数中获取当前页码(
page
),默认为第 1 页。 - 计算数据库查询的偏移量(
$offset
),用于分页。
示例:
- 如果用户访问
?page=3
,则:$page = 3
$offset = (3 - 1) * 20 = 40
,表示从第 41 条记录开始查询。
2.2 获取总游戏数
2.2.1 动态生成 SQL 查询
php
$query = "SELECT COUNT(*) FROM (
SELECT g.id
FROM games g
LEFT JOIN game_category gc ON g.id = gc.game_id
LEFT JOIN categories c ON gc.category_id = c.id
WHERE 1=1";
if ($category_filter && $category_filter !== '热门') {
$query .= " AND c.category_name = :category";
}
if ($searchQuery) {
$query .= " AND g.name LIKE :search";
}
$query .= " GROUP BY g.id
) AS subquery";
功能:
- 动态生成 SQL 查询,统计符合条件的游戏总数。
- 如果用户选择了分类(且分类不是"热门"),则添加分类筛选条件。
- 如果用户输入了搜索关键词,则添加搜索条件。
- 使用
GROUP BY g.id
去重,避免重复统计。
示例:
-
如果用户选择分类为"动作"并搜索"冒险",则生成的 SQL 为:
sqlSELECT COUNT(*) FROM ( SELECT g.id FROM games g LEFT JOIN game_category gc ON g.id = gc.game_id LEFT JOIN categories c ON gc.category_id = c.id WHERE 1=1 AND c.category_name = '动作' AND g.name LIKE '%冒险%' GROUP BY g.id ) AS subquery
2.2.2 执行查询并获取总游戏数
php
$stmt = $pdo->prepare($query);
if ($category_filter && $category_filter !== '热门') {
$stmt->bindValue(':category', $category_filter, PDO::PARAM_STR);
}
if ($searchQuery) {
$stmt->bindValue(':search', "%$searchQuery%", PDO::PARAM_STR);
}
$stmt->execute();
$totalGames = $stmt->fetchColumn();
$totalPages = ceil($totalGames / $gamesPerPage);
功能:
- 使用
PDO
预处理语句执行查询,防止 SQL 注入。 - 绑定分类和搜索参数。
- 获取符合条件的游戏总数(
$totalGames
)。 - 计算总页数(
$totalPages
),用于分页显示。
示例:
- 如果总游戏数为 100,每页显示 20 个游戏,则:
$totalPages = ceil(100 / 20) = 5
3.1 获取游戏数据
3.1.1 热门游戏逻辑
php
if ($category_filter === '热门') {
// 热门游戏按点击次数排序
$all_games_query = "SELECT g.*, g.clicks FROM games g
LEFT JOIN game_category gc ON g.id = gc.game_id
LEFT JOIN categories c ON gc.category_id = c.id
WHERE 1=1";
if ($searchQuery) {
$all_games_query .= " AND g.name LIKE :search";
}
$all_games_query .= " GROUP BY g.id";
$all_games_stmt = $pdo->prepare($all_games_query);
if ($searchQuery) {
$all_games_stmt->bindValue(':search', "%$searchQuery%", PDO::PARAM_STR);
}
$all_games_stmt->execute();
$all_games = $all_games_stmt->fetchAll(PDO::FETCH_ASSOC);
// 按访问次数排序
usort($all_games, function ($a, $b) {
return $b['clicks'] - $a['clicks'];
});
// 分页处理
$games = array_slice($all_games, $offset, $gamesPerPage);
}
功能:
- 如果用户选择"热门"分类,则按点击次数(
clicks
)从高到低排序。 - 支持搜索功能。
- 使用
array_slice
实现分页。
示例:
- 如果用户选择"热门"分类并搜索"冒险",则:
- 查询所有符合条件的游戏。
- 按点击次数排序。
- 根据当前页码和每页数量,截取对应的游戏数据。
3.1.2 其他分类逻辑
php
else {
// 其他分类的逻辑
$query = "SELECT g.*, g.clicks FROM games g
LEFT JOIN game_category gc ON g.id = gc.game_id
LEFT JOIN categories c ON gc.category_id = c.id
WHERE 1=1";
if ($category_filter) {
$query .= " AND c.category_name = :category";
}
if ($searchQuery) {
$query .= " AND g.name LIKE :search";
}
$query .= " GROUP BY g.id";
// 随机排序
if (empty($category_filter) || ($category_filter && $category_filter !== '热门')) {
$query .= " ORDER BY RAND(:seed)";
} else {
$query .= " ORDER BY g.id ASC";
}
$query .= " LIMIT :offset, :limit";
$stmt = $pdo->prepare($query);
if ($category_filter) {
$stmt->bindValue(':category', $category_filter, PDO::PARAM_STR);
}
if ($searchQuery) {
$stmt->bindValue(':search', "%$searchQuery%", PDO::PARAM_STR);
}
if (empty($category_filter) || ($category_filter && $category_filter !== '热门')) {
$stmt->bindValue(':seed', $seed, PDO::PARAM_INT);
}
$stmt->bindValue(":offset", $offset, PDO::PARAM_INT);
$stmt->bindValue(":limit", $gamesPerPage, PDO::PARAM_INT);
$stmt->execute();
$games = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
功能:
- 如果用户选择其他分类,则按分类筛选游戏。
- 支持搜索功能。
- 如果没有选择分类或选择了非"热门"分类,则使用随机排序(
ORDER BY RAND()
)。 - 使用
LIMIT
和OFFSET
实现分页。
示例:
- 如果用户选择"动作"分类并搜索"冒险",则:
- 查询符合条件的游戏。
- 按随机顺序排序。
- 根据当前页码和每页数量,返回对应的游戏数据。
三、前端游戏列表展示
使用 Bootstrap 实现响应式布局,展示游戏列表。
html
<div class="row">
<?php if (empty($games)): ?>
<p class="text-center">未找到相关游戏</p>
<?php else: ?>
<?php foreach ($games as $game): ?>
<div class="col-6 col-sm-6 col-md-4 col-lg-3 mb-4">
<div class="card h-100">
<a href="<?= htmlspecialchars($game['game_url']) ?>" target="_blank">
<img src="<?= htmlspecialchars($game['image_url']) ?>"
class="card-img-top game-img"
alt="<?= htmlspecialchars($game['name']) ?>">
</a>
<div class="card-body d-flex flex-column">
<h5 class="card-title"><?= htmlspecialchars($game['name']) ?></h5>
<p class="card-text flex-grow-1"><?= htmlspecialchars($game['description']) ?></p>
<div class="d-flex justify-content-between align-items-center">
<a href="<?= htmlspecialchars($game['game_url']) ?>"
class="btn btn-primary"
target="_blank">
开始游戏
</a>
<p class="mb-0">访问次数: <?= $game['clicks'] ?? 0 ?></p>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
关键点:
- 响应式布局 :使用 Bootstrap 的栅格系统(
col-6 col-sm-6 col-md-4 col-lg-3
),适配不同屏幕尺寸。 - 游戏卡片:每张卡片包含游戏封面、名称、描述、访问次数和开始游戏按钮。
- 安全性 :使用
htmlspecialchars
函数防止 XSS 攻击。
四、安全性注意事项
4.1 防止 SQL 注入
-
使用
PDO
的预处理语句(prepare
和bindValue
)防止 SQL 注入。 -
示例:
php$stmt = $pdo->prepare("SELECT * FROM games WHERE id = :id"); $stmt->bindValue(':id', $id, PDO::PARAM_INT); $stmt->execute();
4.2 防止 XSS 攻击
-
使用
htmlspecialchars
函数对用户输入的数据进行转义。 -
示例:
phpecho htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
4.3 隐藏敏感信息
-
不要将数据库账号密码直接写在代码中,可以使用环境变量或配置文件。
-
示例:
php$username = getenv('DB_USERNAME'); $password = getenv('DB_PASSWORD');
六、完整代码下载
1.从csdn下载:download.csdn.net/download/qq... 2.海拥资源网下载:code.haiyong.site/2041/ 3.联系博主免费获取