本文目录导读:

我来详细介绍一下如何用PHP实现一个博客系统,这里我会提供一个基础的架构说明和核心代码示例。
系统架构设计
目录结构
blog/
├── admin/ # 后台管理
├── api/ # API接口
├── config/ # 配置文件
├── includes/ # 公共函数
├── templates/ # 模板文件
├── uploads/ # 上传文件
├── index.php # 首页
├── post.php # 文章详情
└── db.php # 数据库连接
数据库设计
MySQL表结构
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(100),
role ENUM('admin', 'editor', 'user') DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 文章表
CREATE TABLE posts (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,VARCHAR(200) NOT NULL,
content TEXT NOT NULL,
excerpt VARCHAR(500),
slug VARCHAR(200) UNIQUE,
status ENUM('draft', 'published', 'deleted') DEFAULT 'draft',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 分类表
CREATE TABLE categories (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
slug VARCHAR(50) UNIQUE,
description TEXT
);
-- 文章分类关联表
CREATE TABLE post_categories (
post_id INT,
category_id INT,
PRIMARY KEY (post_id, category_id),
FOREIGN KEY (post_id) REFERENCES posts(id),
FOREIGN KEY (category_id) REFERENCES categories(id)
);
-- 标签表
CREATE TABLE tags (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
slug VARCHAR(50) UNIQUE
);
-- 文章标签关联表
CREATE TABLE post_tags (
post_id INT,
tag_id INT,
PRIMARY KEY (post_id, tag_id),
FOREIGN KEY (post_id) REFERENCES posts(id),
FOREIGN KEY (tag_id) REFERENCES tags(id)
);
-- 评论表
CREATE TABLE comments (
id INT PRIMARY KEY AUTO_INCREMENT,
post_id INT NOT NULL,
user_id INT,
author_name VARCHAR(100),
author_email VARCHAR(100),
content TEXT NOT NULL,
status ENUM('pending', 'approved', 'spam') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (post_id) REFERENCES posts(id)
);
核心代码实现
config/database.php
<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'blog');
define('DB_USER', 'root');
define('DB_PASS', '');
try {
$pdo = new PDO(
"mysql:host=" . DB_HOST . ";dbname=" . DB_NAME,
DB_USER,
DB_PASS,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")
);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
die("数据库连接失败: " . $e->getMessage());
}
?>
includes/functions.php
<?php
// 获取所有文章
function getPosts($page = 1, $limit = 10) {
global $pdo;
$offset = ($page - 1) * $limit;
$stmt = $pdo->prepare("
SELECT p.*, u.username,
GROUP_CONCAT(DISTINCT c.name) as categories
FROM posts p
LEFT JOIN users u ON p.user_id = u.id
LEFT JOIN post_categories pc ON p.id = pc.post_id
LEFT JOIN categories c ON pc.category_id = c.id
WHERE p.status = 'published'
GROUP BY p.id
ORDER BY p.created_at DESC
LIMIT :limit OFFSET :offset
");
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 获取单篇文章
function getPost($slug) {
global $pdo;
$stmt = $pdo->prepare("
SELECT p.*, u.username
FROM posts p
LEFT JOIN users u ON p.user_id = u.id
WHERE p.slug = :slug AND p.status = 'published'
");
$stmt->execute(['slug' => $slug]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// 添加评论
function addComment($post_id, $author_name, $author_email, $content) {
global $pdo;
$stmt = $pdo->prepare("
INSERT INTO comments (post_id, author_name, author_email, content, status)
VALUES (:post_id, :author_name, :author_email, :content, 'pending')
");
return $stmt->execute([
'post_id' => $post_id,
'author_name' => $author_name,
'author_email' => $author_email,
'content' => $content
]);
}
// 生成URL友好的slug
function createSlug($string) {
$string = strtolower($string);
$string = preg_replace('/[^a-z0-9-]/', '-', $string);
$string = preg_replace('/-+/', '-', $string);
return trim($string, '-');
}
?>
index.php (首页)
<?php
require_once 'config/database.php';
require_once 'includes/functions.php';
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$posts = getPosts($page);
?>
<!DOCTYPE html>
<html>
<head>我的博客</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
<header>
<h1><a href="/">我的博客</a></h1>
<nav>
<a href="/">首页</a>
<?php if(isset($_SESSION['user_id'])): ?>
<a href="admin/">后台管理</a>
<a href="logout.php">退出</a>
<?php else: ?>
<a href="login.php">登录</a>
<?php endif; ?>
</nav>
</header>
<main>
<?php foreach($posts as $post): ?>
<article class="post">
<h2><a href="post.php?slug=<?php echo $post['slug']; ?>">
<?php echo htmlspecialchars($post['title']); ?>
</a></h2>
<div class="meta">
作者:<?php echo $post['username']; ?> |
发布于:<?php echo date('Y-m-d', strtotime($post['created_at'])); ?>
</div>
<p class="excerpt">
<?php echo $post['excerpt'] ?: mb_substr(strip_tags($post['content']), 0, 200) . '...'; ?>
</p>
<a href="post.php?slug=<?php echo $post['slug']; ?>" class="read-more">阅读全文</a>
</article>
<?php endforeach; ?>
</main>
</div>
</body>
</html>
admin/create-post.php (创建文章)
<?php
session_start();
require_once '../config/database.php';
require_once '../includes/functions.php';
// 验证登录
if(!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
if($_SERVER['REQUEST_METHOD'] == 'POST') {
$title = $_POST['title'];
$content = $_POST['content'];
$slug = createSlug($title);
$excerpt = mb_substr(strip_tags($content), 0, 200);
$stmt = $pdo->prepare("
INSERT INTO posts (user_id, title, content, excerpt, slug, status)
VALUES (:user_id, :title, :content, :excerpt, :slug, :status)
");
$status = isset($_POST['publish']) ? 'published' : 'draft';
if($stmt->execute([
'user_id' => $_SESSION['user_id'],
'title' => $title,
'content' => $content,
'excerpt' => $excerpt,
'slug' => $slug,
'status' => $status
])) {
$post_id = $pdo->lastInsertId();
// 处理分类
if(isset($_POST['categories'])) {
foreach($_POST['categories'] as $category_id) {
$stmt = $pdo->prepare("INSERT INTO post_categories (post_id, category_id) VALUES (?, ?)");
$stmt->execute([$post_id, $category_id]);
}
}
header('Location: posts.php?message=文章创建成功');
exit;
}
}
?>
<!DOCTYPE html>
<html>
<head>创建文章 - 后台管理</title>
<link rel="stylesheet" href="../css/admin.css">
</head>
<body>
<div class="admin-container">
<h1>创建新文章</h1>
<form method="POST" action="">
<div class="form-group">
<label for="title">文章标题</label>
<input type="text" name="title" id="title" required>
</div>
<div class="form-group">
<label for="content">文章内容</label>
<textarea name="content" id="content" rows="20" required></textarea>
</div>
<div class="form-group">
<label>分类</label>
<?php
$categories = $pdo->query("SELECT * FROM categories")->fetchAll();
foreach($categories as $category): ?>
<label>
<input type="checkbox" name="categories[]" value="<?php echo $category['id']; ?>">
<?php echo $category['name']; ?>
</label>
<?php endforeach; ?>
</div>
<div class="form-actions">
<button type="submit" name="draft" class="btn btn-secondary">保存草稿</button>
<button type="submit" name="publish" class="btn btn-primary">发布</button>
</div>
</form>
</div>
<!-- 使用CKEditor或TinyMCE作为富文本编辑器 -->
<script src="https://cdn.ckeditor.com/ckeditor5/39.0.1/classic/ckeditor.js"></script>
<script>
ClassicEditor
.create(document.querySelector('#content'))
.catch(error => {
console.error(error);
});
</script>
</body>
</html>
功能模块
基础功能
- ✅ 文章CRUD(创建、读取、更新、删除)
- ✅ 分类管理
- ✅ 标签管理
- ✅ 评论系统
- ✅ 用户管理
- ✅ 后台管理界面
进阶功能
- 📝 搜索引擎优化(SEO)
- 📝 RSS订阅
- 📝 文章搜索
- 📝 图片上传
- 📝 Markdown支持
- 📝 草稿与发布
安全建议
// 1. SQL注入防护 - 使用预处理语句
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
// 2. XSS防护 - 转义输出
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
// 3. CSRF防护 - 使用Token
session_start();
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
// 4. 密码加密
$hashed_password = password_hash($password, PASSWORD_BCRYPT);
// 5. 文件上传验证
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
if(in_array($_FILES['file']['type'], $allowed_types)) {
// 处理上传
}
推荐使用框架
对于更复杂的博客系统,建议使用PHP框架:
- Laravel - 最流行的PHP框架
- Symfony - 企业级框架
- WordPress - 专门的博客系统(虽然是用PHP写的,但本身就是一个完整的CMS)
这个基础实现已经包含了博客系统的核心功能,你可以根据需求逐步添加更多特性,如RSS、搜索、标签云、文章统计等。