项目概述

这是一个基于 PHP 和 MySQL 的简单教程网站,它包含以下核心功能:

教程网网站源码php
(图片来源网络,侵删)
  • 后台管理:管理员可以登录后台,管理教程分类、发布/编辑/删除教程文章。
  • 前台展示:访客可以浏览教程列表、查看教程详情、按分类筛选。
  • 用户界面:包含一个简单的后台管理界面和响应式的前台展示界面。

技术栈

  • 后端语言: PHP 7.4+
  • 数据库: MySQL 5.7+
  • 前端技术: HTML, CSS, Bootstrap 5 (用于快速构建美观的响应式界面)
  • 服务器: Apache 或 Nginx

项目结构

tutorial-website/
├── admin/                 # 后台管理目录
│   ├── index.php         # 后台登录页面
│   ├── dashboard.php     # 后台仪表盘
│   ├── categories/       # 分类管理
│   │   └── manage.php
│   ├── tutorials/        # 教程管理
│   │   ├── list.php      # 教程列表
│   │   ├── add.php       # 添加教程
│   │   └── edit.php      # 编辑教程
│   └── logout.php        # 退出登录
├── assets/                # 静态资源目录
│   ├── css/
│   │   └── style.css     # 自定义样式
│   └── js/
│       └── main.js       # 自定义脚本
├── config.php             # 数据库配置文件
├── database.sql           # 数据库初始化脚本
├── functions.php          # 公共函数库
├── header.php             # 公共头部
├── footer.php             # 公共底部
├── index.php              # 网站首页(展示教程列表)
├── tutorial.php           # 教程详情页
└── category.php           # 分类页

数据库设计 (database.sql)

您需要创建一个数据库,然后执行以下 SQL 脚本来创建必要的表。

-- 创建数据库 (如果不存在)
CREATE DATABASE IF NOT EXISTS tutorial_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 使用数据库
USE tutorial_db;
-- 创建分类表
CREATE TABLE `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `slug` varchar(100) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
  PRIMARY KEY (`id`),
  UNIQUE KEY `slug` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 创建教程表
CREATE TABLE `tutorials` (
  `id` int(11) NOT NULL AUTO_INCREMENT, varchar(255) NOT NULL,
  `slug` varchar(255) NOT NULL,
  `content` text NOT NULL,
  `category_id` int(11) NOT NULL,
  `author` varchar(100) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (`id`),
  UNIQUE KEY `slug` (`slug`),
  KEY `category_id` (`category_id`),
  CONSTRAINT `tutorials_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 插入一些示例数据
INSERT INTO `categories` (`id`, `name`, `slug`) VALUES
(1, 'PHP编程', 'php-programming'),
(2, '前端开发', 'frontend-development'),
(3, '数据库', 'database');
INSERT INTO `tutorials` (`id`, `title`, `slug`, `content`, `category_id`, `author`) VALUES
(1, 'PHP入门教程:Hello World', 'php-hello-world', '<p>欢迎来到PHP的世界!</p><p>让我们从最经典的 "Hello, World!" 程序开始。</p><pre><code>&lt;?php<br>echo &quot;Hello, World!&quot;;<br>?&gt;</code></pre>', 1, '管理员'),
(2, 'HTML与CSS基础', 'html-css-basics', '<p>HTML用于构建网页的结构,CSS用于美化网页的样式。</p><p>这是一个段落。</p><p>这是另一个段落。</p>', 2, '管理员'),
(3, 'MySQL基础查询', 'mysql-basic-query', '<p>MySQL是一种关系型数据库管理系统。</p><p>最常用的查询语句是 SELECT:</p><pre><code>SELECT * FROM users;</code></pre>', 3, '管理员');

核心文件源码

config.php - 数据库配置

<?php
// 数据库连接配置
define('DB_HOST', 'localhost');
define('DB_USER', 'root'); // 您的数据库用户名
define('DB_PASS', '');     // 您的数据库密码
define('DB_NAME', 'tutorial_db'); // 您的数据库名
// 连接数据库
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
// 检查连接
if ($conn->connect_error) {
    die("数据库连接失败: " . $conn->connect_error);
}
// 设置字符集
$conn->set_charset("utf8mb4");
// 后台登录信息 (实际项目中应使用更安全的方式,如 session 和 hashed password)
define('ADMIN_USERNAME', 'admin');
define('ADMIN_PASSWORD', 'password123');
?>

functions.php - 公共函数

<?php
// 引入配置文件
require_once 'config.php';
// 获取所有分类
function getCategories() {
    global $conn;
    $sql = "SELECT * FROM categories ORDER BY name ASC";
    $result = $conn->query($sql);
    return $result->fetch_all(MYSQLI_ASSOC);
}
// 根据 ID 获取分类
function getCategoryById($id) {
    global $conn;
    $stmt = $conn->prepare("SELECT * FROM categories WHERE id = ?");
    $stmt->bind_param("i", $id);
    $stmt->execute();
    $result = $stmt->get_result();
    return $result->fetch_assoc();
}
// 获取所有教程
function getTutorials($category_id = null, $limit = 10, $offset = 0) {
    global $conn;
    $sql = "SELECT t.*, c.name as category_name 
            FROM tutorials t 
            LEFT JOIN categories c ON t.category_id = c.id";
    $params = [];
    $types = "";
    if ($category_id) {
        $sql .= " WHERE t.category_id = ?";
        $params[] = $category_id;
        $types .= "i";
    }
    $sql .= " ORDER BY t.created_at DESC LIMIT ? OFFSET ?";
    $params[] = $limit;
    $params[] = $offset;
    $types .= "ii";
    $stmt = $conn->prepare($sql);
    $stmt->bind_param($types, ...$params);
    $stmt->execute();
    $result = $stmt->get_result();
    return $result->fetch_all(MYSQLI_ASSOC);
}
// 根据 slug 获取单个教程
function getTutorialBySlug($slug) {
    global $conn;
    $stmt = $conn->prepare("SELECT t.*, c.name as category_name FROM tutorials t LEFT JOIN categories c ON t.category_id = c.id WHERE t.slug = ?");
    $stmt->bind_param("s", $slug);
    $stmt->execute();
    $result = $stmt->get_result();
    return $result->fetch_assoc();
}
// 生成 URL 友好的 slug
function createSlug($string) {
    $string = strtolower($string);
    $string = preg_replace("/[^a-z0-9\s-]/", "", $string);
    $string = preg_replace("/[\s-]+/", " ", $string);
    $string = trim($string);
    $string = str_replace(" ", "-", $string);
    return $string;
}
?>

header.php - 公共头部

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">我的教程网</title>
    <!-- Bootstrap 5 CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="index.php">我的教程网</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav ms-auto">
                    <li class="nav-item">
                        <a class="nav-link" href="index.php">首页</a>
                    </li>
                    <?php
                    $categories = getCategories();
                    foreach ($categories as $category) {
                        echo "<li class='nav-item'><a class='nav-link' href='category.php?id={$category['id']}'>{$category['name']}</a></li>";
                    }
                    ?>
                </ul>
            </div>
        </div>
    </nav>
    <div class="container mt-4">

footer.php - 公共底部

    </div> <!-- .container -->
    <!-- Bootstrap 5 JS Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <script src="assets/js/main.js"></script>
</body>
</html>

index.php - 首页(教程列表)

<?php
require_once 'header.php';
require_once 'functions.php';
$tutorials = getTutorials();
?>
<h1 class="mb-4">最新教程</h1>
<div class="row">
    <?php if (count($tutorials) > 0): ?>
        <?php foreach ($tutorials as $tutorial): ?>
            <div class="col-md-6 col-lg-4 mb-4">
                <div class="card h-100">
                    <div class="card-body">
                        <h5 class="card-title">
                            <a href="tutorial.php?slug=<?php echo htmlspecialchars($tutorial['slug']); ?>" class="text-decoration-none">
                                <?php echo htmlspecialchars($tutorial['title']); ?>
                            </a>
                        </h5>
                        <p class="card-text text-muted small">
                            分类: <a href="category.php?id=<?php echo $tutorial['category_id']; ?>"><?php echo htmlspecialchars($tutorial['category_name']); ?></a>
                        </p>
                        <p class="card-text">
                            <?php echo substr(strip_tags($tutorial['content']), 0, 150) . '...'; ?>
                        </p>
                        <a href="tutorial.php?slug=<?php echo htmlspecialchars($tutorial['slug']); ?>" class="btn btn-primary btn-sm">阅读更多</a>
                    </div>
                    <div class="card-footer text-muted small">
                        作者: <?php echo htmlspecialchars($tutorial['author']); ?> | 发布于: <?php echo date('Y-m-d', strtotime($tutorial['created_at'])); ?>
                    </div>
                </div>
            </div>
        <?php endforeach; ?>
    <?php else: ?>
        <div class="col-12">
            <p class="text-center">暂无教程。</p>
        </div>
    <?php endif; ?>
</div>
<?php require_once 'footer.php'; ?>

tutorial.php - 教程详情页

<?php
require_once 'header.php';
require_once 'functions.php';
$slug = $_GET['slug'] ?? '';
if (empty($slug)) {
    echo "<div class='alert alert-danger'>未指定教程。</div>";
    require_once 'footer.php';
    exit();
}
$tutorial = getTutorialBySlug($slug);
if (!$tutorial) {
    echo "<div class='alert alert-danger'>找不到该教程。</div>";
    require_once 'footer.php';
    exit();
}
?>
<div class="row">
    <div class="col-lg-8">
        <article>
            <h1><?php echo htmlspecialchars($tutorial['title']); ?></h1>
            <p class="text-muted">
                分类: <a href="category.php?id=<?php echo $tutorial['category_id']; ?>"><?php echo htmlspecialchars($tutorial['category_name']); ?></a> |
                作者: <?php echo htmlspecialchars($tutorial['author']); ?> |
                发布于: <?php echo date('Y-m-d H:i', strtotime($tutorial['created_at'])); ?>
            </p>
            <hr>
            <div class="content">
                <?php echo $tutorial['content']; ?>
            </div>
        </article>
    </div>
    <div class="col-lg-4">
        <div class="card">
            <div class="card-header">
                <h5 class="card-title mb-0">所有分类</h5>
            </div>
            <div class="card-body">
                <ul class="list-group list-group-flush">
                    <?php
                    $categories = getCategories();
                    foreach ($categories as $category) {
                        $active = ($category['id'] == $tutorial['category_id']) ? 'active' : '';
                        echo "<li class='list-group-item {$active} d-flex justify-content-between align-items-center'>";
                        echo "<a href='category.php?id={$category['id']}'>{$category['name']}</a>";
                        echo "<span class='badge bg-secondary rounded-pill'>" . getTutorials($category['id'], 1, 0)[0]['total_tutorials'] ?? '0' . "</span>"; // 注意:这里需要修改函数以获取总数
                        echo "</li>";
                    }
                    ?>
                </ul>
            </div>
        </div>
    </div>
</div>
<?php require_once 'footer.php'; ?>

admin/index.php - 后台登录页

<?php
session_start();
if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true) {
    header('Location: dashboard.php');
    exit;
}
$error = '';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    if (isset($_POST['username']) && isset($_POST['password'])) {
        if ($_POST['username'] === ADMIN_USERNAME && $_POST['password'] === ADMIN_PASSWORD) {
            $_SESSION['admin_logged_in'] = true;
            header('Location: dashboard.php');
            exit;
        } else {
            $error = '用户名或密码错误!';
        }
    }
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">后台登录 - 我的教程网</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body { background-color: #f8f9fa; }
        .login-form { max-width: 400px; margin: 50px auto; padding: 20px; border-radius: 10px; box-shadow: 0 0 20px rgba(0,0,0,0.1); }
    </style>
</head>
<body>
    <div class="container">
        <div class="login-form">
            <h2 class="text-center mb-4">后台管理系统</h2>
            <?php if (!empty($error)): ?>
                <div class="alert alert-danger"><?php echo $error; ?></div>
            <?php endif; ?>
            <form action="index.php" method="post">
                <div class="mb-3">
                    <label for="username" class="form-label">用户名</label>
                    <input type="text" class="form-control" id="username" name="username" required>
                </div>
                <div class="mb-3">
                    <label for="password" class="form-label">密码</label>
                    <input type="password" class="form-control" id="password" name="password" required>
                </div>
                <button type="submit" class="btn btn-primary w-100">登录</button>
            </form>
        </div>
    </div>
</body>
</html>

admin/dashboard.php - 后台仪表盘

<?php
session_start();
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
    header('Location: index.php');
    exit;
}
require_once '../header.php'; // 注意路径
?>
<h1>仪表盘</h1>
<p>欢迎回来, 管理员!</p>
<hr>
<div class="row">
    <div class="col-md-4">
        <div class="card text-white bg-primary mb-3">
            <div class="card-header">教程管理</div>
            <div class="card-body">
                <a href="tutorials/list.php" class="btn btn-light w-100">管理教程</a>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="card text-white bg-success mb-3">
            <div class="card-header">分类管理</div>
            <div class="card-body">
                <a href="categories/manage.php" class="btn btn-light w-100">管理分类</a>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="card text-white bg-danger mb-3">
            <div class="card-header">退出登录</div>
            <div class="card-body">
                <a href="logout.php" class="btn btn-light w-100">安全退出</a>
            </div>
        </div>
    </div>
</div>
<?php require_once '../footer.php'; ?>

如何部署和使用

  1. 环境准备: 确保您的电脑上已经安装了 PHP、MySQL 和 Apache/Nginx。
  2. 创建数据库: 使用 MySQL 管理工具(如 phpMyAdmin)创建一个数据库,并将 database.sql 文件中的 SQL 语句导入。
  3. 修改配置: 打开 config.php 文件,填入您的数据库用户名、密码和数据库名。
  4. 放置文件: 将整个 tutorial-website 文件夹放置到您的 Web 服务器根目录(Apache 的 htdocs 或 Nginx 的 html 目录)。
  5. 访问网站:
    • 前台: 在浏览器中访问 http://localhost/tutorial-website/
    • 后台: 在浏览器中访问 http://localhost/tutorial-website/admin/,默认用户名和密码在 config.php 中定义(admin / password123)。
  6. 扩展功能: 您可以基于这个框架继续开发,
    • 实现真正的用户注册和登录系统。
    • 使用 PDO 代替 MySQLi 以获得更好的数据库抽象。
    • 将密码哈希存储(如使用 password_hash)。
    • 添加评论系统。
    • 实现富文本编辑器(如 TinyMCE)来编写教程内容。

重要提示与安全改进

这份源码是一个教学示例,为了简洁,省略了许多生产环境中至关重要的安全措施,在实际项目中,请务必注意:

  1. SQL 注入: 虽然在 functions.php 中使用了 preparebind_param 来防止,但所有接收用户输入的地方都需要进行严格的验证和过滤。
  2. 密码安全: 后台密码是明文存储的,绝对不能用于生产环境,请使用 PHP 内置的 password_hash()password_verify() 函数来处理密码。
  3. Session 管理: 后台的 session 管理很简单,可以增加超时机制和更安全的 session 配置。
  4. 文件上传: 如果允许上传图片,需要严格校验文件类型、大小,并存储在非 Web 可直接访问的目录。
  5. XSS 防护: 在 echo 输出用户内容时,使用了 htmlspecialchars,这是防止 XSS 攻击的基本做法。

希望这份源码能对您有所帮助!

教程网网站源码php
(图片来源网络,侵删)