本文将详细介绍如何使用 PHP 和 MySQL 搭建一个功能完善的在线游戏网站,并附上完整的代码。目前,该网站已收录 2000+ 游戏,支持分类筛选、搜索、分页、随机推荐等功能。

在线预览链接1:https://game.haiyong.site/
预览链接2(新网址):https://peergame.cn/

在这里插入图片描述

项目概述

1. 功能特点

  • 游戏展示:支持按分类、热门、随机等方式展示游戏。
  • 分类筛选:提供多种游戏分类,用户可以根据兴趣筛选游戏。
  • 搜索功能:支持按游戏名称搜索。
  • 分页功能:游戏列表支持分页显示,提升用户体验。
  • 点击统计:记录每个游戏的点击次数,方便统计热门游戏。
  • 响应式设计:适配 PC 和移动端,提供良好的浏览体验。

2. 技术栈

  • 前端:HTML、CSS、Bootstrap、JavaScript
  • 后端:PHP
  • 数据库:MySQL

@TOC

一、数据库设计与连接

1. 表结构

数据库包含以下表:

1.1 games

存储游戏的基本信息。

字段名类型说明
idINT游戏 ID
nameVARCHAR(255)游戏名称
descriptionTEXT游戏描述
game_urlVARCHAR(255)游戏链接
image_urlVARCHAR(255)游戏封面图链接
clicksINT点击次数

1.2 categories

存储游戏分类信息。

字段名类型说明
idINT分类 ID
category_nameVARCHAR(255)分类名称

1.3 game_category

存储游戏与分类的关联关系。

字段名类型说明
game_idINT游戏 ID
category_idINT分类 ID

2.使用 PDO 连接 MySQL

PHP 的 PDO(PHP Data Objects)是一个轻量级的数据库访问抽象层,支持多种数据库(如 MySQL、PostgreSQL 等)。以下是连接 MySQL 的代码:

<?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 获取分类和搜索条件

$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 分页设置

$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 查询

$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 为:

    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
        AND c.category_name = '动作'
        AND g.name LIKE '%冒险%'
        GROUP BY g.id
    ) AS subquery

2.2.2 执行查询并获取总游戏数

$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 热门游戏逻辑

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 其他分类逻辑

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())。
  • 使用 LIMITOFFSET 实现分页。

示例:

  • 如果用户选择“动作”分类并搜索“冒险”,则:

    • 查询符合条件的游戏。
    • 按随机顺序排序。
    • 根据当前页码和每页数量,返回对应的游戏数据。

三、前端游戏列表展示

使用 Bootstrap 实现响应式布局,展示游戏列表。

<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 的预处理语句(preparebindValue)防止 SQL 注入。
  • 示例:

    $stmt = $pdo->prepare("SELECT * FROM games WHERE id = :id");
    $stmt->bindValue(':id', $id, PDO::PARAM_INT);
    $stmt->execute();

4.2 防止 XSS 攻击

  • 使用 htmlspecialchars 函数对用户输入的数据进行转义。
  • 示例:

    echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');

4.3 隐藏敏感信息

  • 不要将数据库账号密码直接写在代码中,可以使用环境变量或配置文件。
  • 示例:

    $username = getenv('DB_USERNAME');
    $password = getenv('DB_PASSWORD');

六、完整代码下载

1.海拥资源网下载:https://code.haiyong.site/2041/
2.添加博主v:wh18363免费获取


海拥
13 声望6 粉丝