1. 后台管理:登录、发布新闻、编辑新闻、删除新闻。
  2. 前台展示:列出所有新闻、查看单条新闻详情。
  3. 数据持久化:使用 MySQL 数据库存储数据。

第零步:环境准备

在开始之前,请确保你的电脑上已经安装了以下软件:

php制作简单的cms新闻发布教程
(图片来源网络,侵删)
  1. Web 服务器:如 XAMPP、WAMP 或 MAMP,这些软件包通常集成了 Apache (Web服务器) 和 MySQL (数据库)。
  2. PHP:Web 服务器包通常会包含 PHP。
  3. MySQL:同样,Web 服务器包会包含,你也可以单独安装。

安装好 XAMPP 后,启动 Apache 和 MySQL 服务。


第一步:数据库设计

我们需要一个数据库来存储新闻数据,我们将创建一个名为 news_db 的数据库,并在其中创建一个 articles 表。

  1. 打开你的浏览器,访问 http://localhost/phpmyadmin
  2. 点击“新建”,创建一个数据库,命名为 news_db
  3. news_db 中,点击“新建表”,创建一个名为 articles 的表,包含以下字段:
字段名 类型 长度/值 索引 注释
id INT 11 PRIMARY, AUTO_INCREMENT 新闻的唯一ID
content TEXT
created_at DATETIME 发布时间
updated_at DATETIME 更新时间

SQL 语句(你也可以直接在 phpMyAdmin 中执行):

CREATE DATABASE `news_db`;
USE `news_db`;
CREATE TABLE `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT, varchar(255) NOT NULL,
  `content` text NOT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

第二步:项目文件结构

在 Web 服务器的根目录(对于 XAMPP 是 htdocs)下,创建一个新的文件夹,simple-cms,我们的所有文件都将放在这个文件夹里。

php制作简单的cms新闻发布教程
(图片来源网络,侵删)

最终的文件结构如下:

/simple-cms/
├── admin/                 # 后台管理目录
│   ├── login.php         # 登录页面
│   ├── index.php         # 后台首页(新闻列表)
│   ├── create.php        # 创建新闻页面
│   ├── edit.php          # 编辑新闻页面
│   └── process.php       # 处理增删改操作的逻辑
├── assets/               # 静态资源(CSS, JS, 图片)
│   └── css/
│       └── style.css     # 样式文件
├── config/               # 配置文件
│   └── db.php            # 数据库连接配置
├── index.php             # 前台首页(新闻列表)
├── article.php           # 前台查看单条新闻
└── logout.php            # 退出登录

第三步:数据库连接 (config/db.php)

创建一个文件来管理数据库连接,这样我们可以在所有需要的地方复用它,并且方便修改。

config/db.php

<?php
// 数据库连接配置
$host = 'localhost';
$dbname = 'news_db';
$username = 'root'; // 默认用户名
$password = '';     // 默认密码为空
try {
    // 创建 PDO 实例
    $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password);
    // 设置 PDO 错误模式为异常
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    // 如果连接失败,显示错误信息并停止脚本
    die("数据库连接失败: " . $e->getMessage());
}
?>

第四步:后台管理系统

登录功能 (admin/login.php)

后台需要登录才能访问,我们用一个简单的会话来管理用户状态。

php制作简单的cms新闻发布教程
(图片来源网络,侵删)

admin/login.php

<?php
session_start();
if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true) {
    header('Location: index.php');
    exit;
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 这里是硬编码的用户名和密码,实际项目中应该从数据库验证
    $admin_username = 'admin';
    $admin_password = 'password123';
    if ($_POST['username'] === $admin_username && $_POST['password'] === $admin_password) {
        $_SESSION['admin_logged_in'] = true;
        header('Location: index.php');
        exit;
    } else {
        $error = '用户名或密码错误!';
    }
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">后台登录</title>
    <link rel="stylesheet" href="../assets/css/style.css">
</head>
<body>
    <div class="container">
        <h1>后台管理系统</h1>
        <?php if ($error): ?>
            <p class="error"><?php echo $error; ?></p>
        <?php endif; ?>
        <form action="login.php" method="POST">
            <div>
                <label for="username">用户名:</label>
                <input type="text" id="username" name="username" required>
            </div>
            <div>
                <label for="password">密码:</label>
                <input type="password" id="password" name="password" required>
            </div>
            <button type="submit">登录</button>
        </form>
    </div>
</body>
</html>

后台首页 (admin/index.php)

这是登录后看到的新闻管理列表。

admin/index.php

<?php
session_start();
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
    header('Location: login.php');
    exit;
}
require_once '../config/db.php';
// 获取所有新闻
$stmt = $pdo->query("SELECT id, title, created_at FROM articles ORDER BY created_at DESC");
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">新闻管理</title>
    <link rel="stylesheet" href="../assets/css/style.css">
</head>
<body>
    <div class="container">
        <h1>新闻管理</h1>
        <a href="create.php" class="btn">发布新新闻</a>
        <a href="../index.php" class="btn">返回首页</a>
        <a href="logout.php" class="btn btn-danger">退出登录</a>
        <table>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>标题</th>
                    <th>发布时间</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($articles as $article): ?>
                <tr>
                    <td><?php echo $article['id']; ?></td>
                    <td><?php echo htmlspecialchars($article['title']); ?></td>
                    <td><?php echo $article['created_at']; ?></td>
                    <td>
                        <a href="edit.php?id=<?php echo $article['id']; ?>" class="btn">编辑</a>
                        <a href="process.php?action=delete&id=<?php echo $article['id']; ?>" class="btn btn-danger" onclick="return confirm('确定要删除吗?');">删除</a>
                    </td>
                </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
    </div>
</body>
</html>

创建新闻 (admin/create.php)

一个简单的表单,用于输入新闻标题和内容。

admin/create.php

<?php
session_start();
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
    header('Location: login.php');
    exit;
}
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $title = trim($_POST['title']);
    $content = trim($_POST['content']);
    if (empty($title) || empty($content)) {
        $error = '标题和内容都不能为空!';
    } else {
        require_once '../config/db.php';
        $stmt = $pdo->prepare("INSERT INTO articles (title, content) VALUES (:title, :content)");
        $stmt->execute(['title' => $title, 'content' => $content]);
        header('Location: index.php');
        exit;
    }
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">发布新闻</title>
    <link rel="stylesheet" href="../assets/css/style.css">
</head>
<body>
    <div class="container">
        <h1>发布新新闻</h1>
        <a href="index.php" class="btn">返回列表</a>
        <?php if (isset($error)): ?>
            <p class="error"><?php echo $error; ?></p>
        <?php endif; ?>
        <form action="create.php" method="POST">
            <div>
                <label for="title">标题:</label>
                <input type="text" id="title" name="title" required>
            </div>
            <div>
                <label for="content">内容:</label>
                <textarea id="content" name="content" rows="10" required></textarea>
            </div>
            <button type="submit">发布</button>
        </form>
    </div>
</body>
</html>

编辑新闻 (admin/edit.php)

从数据库中取出已有新闻的数据并填充到表单中。

admin/edit.php

<?php
session_start();
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
    header('Location: login.php');
    exit;
}
require_once '../config/db.php';
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
    die('无效的新闻ID');
}
$id = $_GET['id'];
$stmt = $pdo->prepare("SELECT * FROM articles WHERE id = :id");
$stmt->execute(['id' => $id]);
$article = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$article) {
    die('新闻不存在');
}
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $title = trim($_POST['title']);
    $content = trim($_POST['content']);
    if (empty($title) || empty($content)) {
        $error = '标题和内容都不能为空!';
    } else {
        $stmt = $pdo->prepare("UPDATE articles SET title = :title, content = :content WHERE id = :id");
        $stmt->execute(['title' => $title, 'content' => $content, 'id' => $id]);
        header('Location: index.php');
        exit;
    }
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">编辑新闻</title>
    <link rel="stylesheet" href="../assets/css/style.css">
</head>
<body>
    <div class="container">
        <h1>编辑新闻</h1>
        <a href="index.php" class="btn">返回列表</a>
        <?php if (isset($error)): ?>
            <p class="error"><?php echo $error; ?></p>
        <?php endif; ?>
        <form action="edit.php?id=<?php echo $id; ?>" method="POST">
            <div>
                <label for="title">标题:</label>
                <input type="text" id="title" name="title" value="<?php echo htmlspecialchars($article['title']); ?>" required>
            </div>
            <div>
                <label for="content">内容:</label>
                <textarea id="content" name="content" rows="10" required><?php echo htmlspecialchars($article['content']); ?></textarea>
            </div>
            <button type="submit">更新</button>
        </form>
    </div>
</body>
</html>

处理逻辑 (admin/process.php)

这个文件用于处理删除操作。

admin/process.php

<?php
session_start();
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
    header('Location: login.php');
    exit;
}
require_once '../config/db.php';
if (isset($_GET['action']) && isset($_GET['id'])) {
    $action = $_GET['action'];
    $id = $_GET['id'];
    if ($action === 'delete' && is_numeric($id)) {
        $stmt = $pdo->prepare("DELETE FROM articles WHERE id = :id");
        $stmt->execute(['id' => $id]);
    }
}
header('Location: index.php');
exit;

退出登录 (admin/logout.php)

销毁会话,让用户退出。

admin/logout.php

<?php
session_start();
session_destroy();
header('Location: login.php');
exit;
?>

第五步:前台展示

前台首页 (index.php)

展示所有新闻的列表。

index.php

<?php
require_once 'config/db.php';
// 获取所有新闻,限制数量以便分页(这里简单处理)
$stmt = $pdo->query("SELECT id, title, created_at, LEFT(content, 150) AS short_content FROM articles ORDER BY created_at DESC LIMIT 10");
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">我的新闻网站</title>
    <link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
    <div class="container">
        <h1>我的新闻网站</h1>
        <a href="admin/login.php" class="btn">进入后台</a>
        <div class="news-list">
            <?php foreach ($articles as $article): ?>
            <div class="news-item">
                <h2><a href="article.php?id=<?php echo $article['id']; ?>"><?php echo htmlspecialchars($article['title']); ?></a></h2>
                <p class="short-content"><?php echo htmlspecialchars($article['short_content']) . '...'; ?></p>
                <p class="date">发布于: <?php echo $article['created_at']; ?></p>
            </div>
            <?php endforeach; ?>
        </div>
    </div>
</body>
</html>

新闻详情页 (article.php)

展示单条新闻的完整内容。

article.php

<?php
require_once 'config/db.php';
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
    die('无效的新闻ID');
}
$id = $_GET['id'];
$stmt = $pdo->prepare("SELECT * FROM articles WHERE id = :id");
$stmt->execute(['id' => $id]);
$article = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$article) {
    die('新闻不存在');
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8"><?php echo htmlspecialchars($article['title']); ?></title>
    <link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
    <div class="container">
        <a href="index.php" class="btn">返回首页</a>
        <a href="admin/login.php" class="btn">进入后台</a>
        <article class="news-detail">
            <h1><?php echo htmlspecialchars($article['title']); ?></h1>
            <p class="date">发布于: <?php echo $article['created_at']; ?></p>
            <div class="content">
                <?php echo nl2br(htmlspecialchars($article['content'])); ?>
                <!-- nl2br 将换行符转换为 <br> 标签 -->
            </div>
        </article>
    </div>
</body>
</html>

第六步:添加基本样式 (assets/css/style.css)

为了让我们的网站看起来不那么简陋,添加一些 CSS 样式。

assets/css/style.css

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    line-height: 1.6;
    margin: 0;
    padding: 20px;
    background-color: #f4f4f4;
    color: #333;
}
.container {
    max-width: 800px;
    margin: 0 auto;
    background: #fff;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
h1, h2 {
    color: #0056b3;
}
a {
    color: #007bff;
    text-decoration: none;
}
a:hover {
    text-decoration: underline;
}
.btn {
    display: inline-block;
    background: #007bff;
    color: #fff;
    padding: 8px 15px;
    border-radius: 4px;
    margin-right: 10px;
    margin-bottom: 10px;
}
.btn-danger {
    background: #dc3545;
}
table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 20px;
}
th, td {
    border: 1px solid #ddd;
    padding: 12px;
    text-align: left;
}
th {
    background-color: #f2f2f2;
}
tr:nth-child(even) {
    background-color: #f9f9f9;
}
.news-list .news-item {
    border-bottom: 1px solid #eee;
    padding-bottom: 15px;
    margin-bottom: 15px;
}
.news-list .news-item h2 a {
    color: #0056b3;
}
.news-list .short-content {
    color: #666;
    margin: 10px 0;
}
.news-list .date {
    font-size: 0.9em;
    color: #999;
}
.news-detail h1 {
    border-bottom: 2px solid #eee;
    padding-bottom: 10px;
    margin-bottom: 20px;
}
.news-detail .content {
    white-space: pre-wrap; /* 保留换行和空格 */
}
.error {
    color: #dc3545;
    background: #f8d7da;
    border: 1px solid #f5c6cb;
    padding: 10px;
    border-radius: 4px;
    margin-bottom: 15px;
}
textarea {
    width: 100%;
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-sizing: border-box; /* 确保padding不会影响宽度 */
}

总结与后续改进

恭喜!你已经成功搭建了一个功能简单的 PHP CMS 新闻系统。

回顾一下我们做了什么:

  1. 设计了数据库:用 articles 表存储新闻。
  2. 创建了核心文件:包括数据库连接、后台管理(增删改查)和前台展示。
  3. 使用了会话:保护后台管理页面,只有登录后才能访问。
  4. 加入了基本样式:让界面更美观。

可以进一步改进的方向:

  1. 用户认证:将硬编码的用户名密码改为从数据库验证。
  2. 富文本编辑器:集成如 TinyMCE 或 CKEditor 编辑器,让新闻发布更方便。
  3. 分页:当新闻很多时,前台和后台列表都需要分页显示。
  4. 图片上传:允许上传图片并插入到新闻内容中。
  5. SEO优化:为每个页面设置 <title><meta description>
  6. 更安全的代码:防止 SQL 注入(我们已用 PDO 预处理语句防止)、XSS 攻击(我们已用 htmlspecialchars 防止)、CSRF 攻击(可以添加 Token 验证)。
  7. URL 重写:使用 .htaccessarticle.php?id=1 这样的 URL 变成更友好的 /article/1

这个项目是学习 PHP Web 开发的一个绝佳起点,你可以基于它进行扩展和深化。