本文目录导读:

我来帮你了解如何使用 PHP 构建一个 Wiki 系统,这里提供一个从简单到完整方案的全景介绍:
使用现成的 Wiki 系统
MediaWiki(最流行)
# 需要环境:PHP 7.4+, MySQL, Composer composer create-project mediawiki/core my-wiki
DokuWiki(轻量级)
# 只需要 PHP,不需要数据库 wget https://download.dokuwiki.org/src/dokuwiki/dokuwiki-stable.tgz tar -xzf dokuwiki-stable.tgz
Tiki Wiki(功能丰富)
composer create-project tikiwiki/tiki my-tiki
从零开发简单 Wiki
基础架构设计
// config.php
<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'wiki_db');
define('DB_USER', 'root');
define('DB_PASS', '');
define('BASE_URL', 'http://localhost/wiki');
define('UPLOAD_DIR', __DIR__ . '/uploads/');
// 数据库连接
function db_connect() {
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
return $conn;
}
?>
创建数据库表
// install.php
<?php
$conn = db_connect();
$sql = "
CREATE TABLE IF NOT EXISTS pages (
id INT AUTO_INCREMENT PRIMARY KEY,VARCHAR(255) NOT NULL UNIQUE,
content MEDIUMTEXT,
slug VARCHAR(255) UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS revisions (
id INT AUTO_INCREMENT PRIMARY KEY,
page_id INT,
content MEDIUMTEXT,
user_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (page_id) REFERENCES pages(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE,
password_hash VARCHAR(255),
email VARCHAR(100),
role ENUM('admin', 'editor', 'viewer') DEFAULT 'viewer',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP NULL
);
CREATE TABLE IF NOT EXISTS categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) UNIQUE,
description TEXT,
parent_id INT NULL
);
CREATE TABLE IF NOT EXISTS pages_categories (
page_id INT,
category_id INT,
PRIMARY KEY (page_id, category_id)
);
CREATE TABLE IF NOT EXISTS tags (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) UNIQUE
);
CREATE TABLE IF NOT EXISTS pages_tags (
page_id INT,
tag_id INT,
PRIMARY KEY (page_id, tag_id)
);
CREATE TABLE IF NOT EXISTS search_terms (
id INT AUTO_INCREMENT PRIMARY KEY,
term VARCHAR(255) UNIQUE,
page_id INT,
weight FLOAT DEFAULT 1.0
);
";
$conn->query($sql);
?>
页面管理类
// Page.php
<?php
class Page {
private $conn;
private $table = 'pages';
public function __construct() {
$this->conn = db_connect();
}
// 创建页面
public function create($title, $content, $category_ids = [], $tags = []) {
$slug = $this->createSlug($title);
$stmt = $this->conn->prepare("INSERT INTO $this->table (title, content, slug) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $title, $content, $slug);
if ($stmt->execute()) {
$page_id = $stmt->insert_id;
$this->addCategories($page_id, $category_ids);
$this->addTags($page_id, $tags);
$this->updateSearchIndex($page_id, $content);
return $page_id;
}
return false;
}
// 获取页面
public function getPage($id = null, $slug = null) {
if ($id) {
$sql = "SELECT * FROM $this->table WHERE id = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("i", $id);
} else if ($slug) {
$sql = "SELECT * FROM $this->table WHERE slug = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $slug);
}
$stmt->execute();
$result = $stmt->get_result();
return $result->fetch_assoc();
}
// 更新页面
public function update($id, $content, $category_ids = [], $tags = []) {
$sql = "UPDATE $this->table SET content = ? WHERE id = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("si", $content, $id);
if ($stmt->execute()) {
// 保存修订历史
$this->saveRevision($id, $content);
$this->updateCategories($id, $category_ids);
$this->updateTags($id, $tags);
$this->updateSearchIndex($id, $content);
return true;
}
return false;
}
// 删除页面
public function delete($id) {
$this->conn->begin_transaction();
try {
// 删除相关数据
$this->conn->query("DELETE FROM pages_categories WHERE page_id = $id");
$this->conn->query("DELETE FROM pages_tags WHERE page_id = $id");
$this->conn->query("DELETE FROM revisions WHERE page_id = $id");
$this->conn->query("DELETE FROM search_terms WHERE page_id = $id");
// 删除页面
$sql = "DELETE FROM $this->table WHERE id = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("i", $id);
$stmt->execute();
$this->conn->commit();
return true;
} catch (Exception $e) {
$this->conn->rollback();
return false;
}
}
// 搜索页面
public function search($query) {
$sql = "SELECT p.*, MATCH(p.content, p.title) AS relevance
FROM $this->table p
WHERE MATCH(p.content, p.title) AGAINST (?)
ORDER BY relevance DESC LIMIT 10";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $query);
$stmt->execute();
$result = $stmt->get_result();
return $result->fetch_all(MYSQLI_ASSOC);
}
// 创建 URL 别名
private function createSlug($title) {
$slug = strtolower($title);
$slug = preg_replace('/[^a-z0-9-]/', '-', $slug);
$slug = preg_replace('/-+/', '-', $slug);
$slug = trim($slug, '-');
return $slug;
}
// 保存修订
private function saveRevision($page_id, $content) {
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : 1;
$stmt = $this->conn->prepare("INSERT INTO revisions (page_id, content, user_id) VALUES (?, ?, ?)");
$stmt->bind_param("isi", $page_id, $content, $user_id);
$stmt->execute();
}
// 更新索引
private function updateSearchIndex($page_id, $content) {
// 清除旧索引
$this->conn->query("DELETE FROM search_terms WHERE page_id = $page_id");
// 提取关键词(简化示例)
$words = str_word_count(strip_tags($content), 1);
$stop_words = ['the', 'a', 'an', 'and', 'or', 'but'];
$word_count = array_count_values($words);
foreach ($word_count as $word => $count) {
if (!in_array($word, $stop_words) && strlen($word) > 3) {
$weight = $count / max(1, count($words));
$stmt = $this->conn->prepare("INSERT IGNORE INTO search_terms (term, page_id, weight) VALUES (?, ?, ?)");
$stmt->bind_param("sid", $word, $page_id, $weight);
$stmt->execute();
}
}
}
// 关联分类
private function addCategories($page_id, $category_ids) {
if (empty($category_ids)) return;
foreach ($category_ids as $cat_id) {
$stmt = $this->conn->prepare("INSERT IGNORE INTO pages_categories (page_id, category_id) VALUES (?, ?)");
$stmt->bind_param("ii", $page_id, $cat_id);
$stmt->execute();
}
}
// 添加标签
private function addTags($page_id, $tags) {
if (empty($tags)) return;
foreach ($tags as $tag) {
// 获取或创建标签
$stmt = $this->conn->prepare("INSERT IGNORE INTO tags (name) VALUES (?)");
$stmt->bind_param("s", $tag);
$stmt->execute();
$tag_id = $this->conn->insert_id ?: $this->getTagId($tag);
$stmt = $this->conn->prepare("INSERT IGNORE INTO pages_tags (page_id, tag_id) VALUES (?, ?)");
$stmt->bind_param("ii", $page_id, $tag_id);
$stmt->execute();
}
}
private function getTagId($name) {
$sql = "SELECT id FROM tags WHERE name = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $name);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
return $row['id'] ?? 0;
}
}
?>
前端控制器
// router.php
<?php
// 简单的路由器
class Router {
private $routes = [];
public function get($path, $callback) {
$this->routes['GET'][$path] = $callback;
}
public function post($path, $callback) {
$this->routes['POST'][$path] = $callback;
}
public function resolve() {
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
foreach ($this->routes[$method] ?? [] as $route => $callback) {
if ($route === $path) {
return call_user_func($callback);
}
}
http_response_code(404);
echo "404 Not Found";
}
}
$router = new Router();
// 页面路由
$router->get('/wiki', function() {
$page = new Page();
$pages = $page->getAll();
include 'templates/home.php';
});
$router->get('/wiki/page/{id}', function($id) {
$page = new Page();
$data = $page->getPage($id);
include 'templates/page-view.php';
});
// 创建页面
$router->get('/wiki/create', function() {
include 'templates/page-create.php';
});
$router->post('/wiki/create', function() {
$page = new Page();
$page->create($_POST['title'], $_POST['content']);
header("Location: /wiki/page/" . $page->getLastId());
});
// 编辑页面
$router->get('/wiki/edit/{id}', function($id) {
$page = new Page();
$data = $page->getPage($id);
include 'templates/page-edit.php';
});
$router->post('/wiki/edit/{id}', function($id) {
$page = new Page();
$page->update($id, $_POST['content']);
header("Location: /wiki/page/$id");
});
// 搜索
$router->get('/wiki/search', function() {
$page = new Page();
$results = $page->search($_GET['q'] ?? '');
include 'templates/search-results.php';
});
$router->resolve();
?>
使用框架构建
使用 Laravel(推荐)
composer create-project laravel/laravel wiki-app
模型
// app/Models/Page.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Page extends Model {
protected $fillable = ['title', 'content', 'slug', 'category_id'];
public function categories() {
return $this->belongsToMany(Category::class);
}
public function tags() {
return $this->belongsToMany(Tag::class);
}
public function revisions() {
return $this->hasMany(Revision::class);
}
public function user() {
return $this->belongsTo(User::class);
}
public function searchableAs() {
return 'wiki_pages';
}
}
?>
控制器
// app/Http/Controllers/WikiController.php
<?php
namespace App\Http\Controllers;
use App\Models\Page;
use Illuminate\Http\Request;
class WikiController extends Controller {
public function index() {
$pages = Page::latest()->paginate(10);
return view('wiki.index', compact('pages'));
}
public function show($slug) {
$page = Page::where('slug', $slug)->firstOrFail();
return view('wiki.show', compact('page'));
}
public function create() {
return view('wiki.create');
}
public function store(Request $request) {
$validated = $request->validate([
'title' => 'required|unique:pages',
'content' => 'required',
]);
$page = Page::create($validated);
return redirect()->route('wiki.show', $page->slug);
}
public function edit($slug) {
$page = Page::where('slug', $slug)->firstOrFail();
return view('wiki.edit', compact('page'));
}
public function update(Request $request, $slug) {
$page = Page::where('slug', $slug)->firstOrFail();
$validated = $request->validate([
'title' => 'required|unique:pages,title,'.$page->id,
'content' => 'required',
]);
$page->update($validated);
return redirect()->route('wiki.show', $page->slug);
}
public function search(Request $request) {
$query = $request->input('q');
$pages = Page::where('title', 'LIKE', "%{$query}%")
->orWhere('content', 'LIKE', "%{$query}%")
->get();
return view('wiki.search', compact('pages', 'query'));
}
}
?>
推荐学习资源
-
官方文档:
- PHP 官方文档:https://www.php.net/manual/
- Laravel 文档:https://laravel.com/docs
-
学习路径:
- 基础:变量、数组、函数、OOP
- 进阶:MVC 架构、路由、中间件
- 实践:CRUD 应用、用户认证
-
工具推荐:
- IDE:PhpStorm、VS Code + PHP Intelephense
- 调试:Xdebug
- 包管理:Composer
-
完整 Wiki 系统推荐:
- MediaWiki(资料最多)
- DokuWiki(轻量简单)
- BookStack(界面美观)
建议先使用现成系统了解 Wiki 功能,再根据需求决定是否自己开发,如果自己开发,推荐使用 Laravel 框架,开发效率更高,需要更详细的代码示例吗?
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播