PHP 怎么PHP实战项目

wen PHP项目 3

本文目录导读:

PHP 怎么PHP实战项目

  1. 🚀 实战项目:在线图书管理系统
  2. 📚 进阶实战项目推荐
  3. 🎯 部署与上线
  4. 💡 学习建议

我来给你一个完整的PHP实战项目方案,从基础到进阶,包含真实项目案例和代码示例。

🚀 实战项目:在线图书管理系统

这是一个完整的CRUD项目,涵盖用户认证、数据管理、文件上传等核心功能。

项目结构

book-manager/
├── config/
│   └── database.php
├── includes/
│   ├── auth.php
│   ├── functions.php
│   └── header.php
├── admin/
│   ├── login.php
│   ├── dashboard.php
│   ├── books/
│   │   ├── index.php
│   │   ├── create.php
│   │   ├── edit.php
│   │   └── delete.php
│   └── uploads/
├── assets/
│   ├── css/
│   └── js/
└── index.php

数据库设计

CREATE DATABASE book_manage;
USE book_manage;
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    email VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE books (
    id INT PRIMARY KEY AUTO_INCREMENT,VARCHAR(200) NOT NULL,
    author VARCHAR(100) NOT NULL,
    isbn VARCHAR(13) UNIQUE,
    price DECIMAL(10,2),
    cover_image VARCHAR(255),
    description TEXT,
    published_date DATE,
    created_by INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (created_by) REFERENCES users(id)
);

配置文件

<?php
// config/database.php
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'book_manage');
function getConnection() {
    try {
        $conn = new PDO(
            "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME,
            DB_USER,
            DB_PASS
        );
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $conn;
    } catch(PDOException $e) {
        die("Connection failed: " . $e->getMessage());
    }
}
?>

用户认证系统

<?php
// includes/auth.php
session_start();
function isLoggedIn() {
    return isset($_SESSION['user_id']);
}
function requireLogin() {
    if (!isLoggedIn()) {
        header('Location: /book-manager/admin/login.php');
        exit();
    }
}
function login($username, $password) {
    $conn = getConnection();
    $stmt = $conn->prepare("SELECT id, username, password FROM users WHERE username = ?");
    $stmt->execute([$username]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);
    if ($user && password_verify($password, $user['password'])) {
        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];
        return true;
    }
    return false;
}
function logout() {
    session_destroy();
    header('Location: login.php');
    exit();
}
?>

登录页面

<?php
// admin/login.php
require_once '../config/database.php';
require_once '../includes/auth.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';
    if (login($username, $password)) {
        header('Location: dashboard.php');
        exit();
    } else {
        $error = "用户名或密码错误";
    }
}
?>
<!DOCTYPE html>
<html>
<head>登录 - 图书管理系统</title>
    <link rel="stylesheet" href="../assets/css/style.css">
</head>
<body class="login-page">
    <div class="login-container">
        <h2>图书管理系统登录</h2>
        <?php if (isset($error)): ?>
            <div class="alert alert-danger"><?php echo $error; ?></div>
        <?php endif; ?>
        <form method="POST">
            <div class="form-group">
                <label>用户名</label>
                <input type="text" name="username" required>
            </div>
            <div class="form-group">
                <label>密码</label>
                <input type="password" name="password" required>
            </div>
            <button type="submit" class="btn btn-primary">登录</button>
        </form>
    </div>
</body>
</html>

图书列表(含分页和搜索)

<?php
// admin/books/index.php
require_once '../../config/database.php';
require_once '../../includes/auth.php';
requireLogin();
$conn = getConnection();
$search = $_GET['search'] ?? '';
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 10;
$offset = ($page - 1) * $limit;
// 构建查询
$where = '';
if ($search) {
    $where = "WHERE title LIKE :search OR author LIKE :search";
}
// 获取总数
$countQuery = "SELECT COUNT(*) as total FROM books $where";
$stmt = $conn->prepare($countQuery);
if ($search) {
    $stmt->bindValue(':search', "%$search%");
}
$stmt->execute();
$total = $stmt->fetch(PDO::FETCH_ASSOC)['total'];
$totalPages = ceil($total / $limit);
// 获取图书列表
$query = "SELECT * FROM books $where ORDER BY created_at DESC LIMIT :limit OFFSET :offset";
$stmt = $conn->prepare($query);
if ($search) {
    $stmt->bindValue(':search', "%$search%");
}
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$books = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<?php include '../includes/header.php'; ?>
<div class="container">
    <div class="page-header">
        <h1>图书管理</h1>
        <a href="create.php" class="btn btn-success">新增图书</a>
    </div>
    <!-- 搜索表单 -->
    <form method="GET" class="search-form">
        <input type="text" name="search" value="<?php echo htmlspecialchars($search); ?>" 
               placeholder="搜索标题或作者...">
        <button type="submit" class="btn btn-primary">搜索</button>
    </form>
    <!-- 图书列表 -->
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>封面</th>
                <th>标题</th>
                <th>作者</th>
                <th>价格</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($books as $book): ?>
            <tr>
                <td><?php echo $book['id']; ?></td>
                <td>
                    <?php if ($book['cover_image']): ?>
                        <img src="../uploads/<?php echo $book['cover_image']; ?>" 
                             width="50" height="70" alt="封面">
                    <?php endif; ?>
                </td>
                <td><?php echo htmlspecialchars($book['title']); ?></td>
                <td><?php echo htmlspecialchars($book['author']); ?></td>
                <td>¥<?php echo number_format($book['price'], 2); ?></td>
                <td>
                    <a href="edit.php?id=<?php echo $book['id']; ?>" class="btn btn-sm">编辑</a>
                    <a href="delete.php?id=<?php echo $book['id']; ?>" 
                       class="btn btn-sm btn-danger" 
                       onclick="return confirm('确定删除吗?')">删除</a>
                </td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
    <!-- 分页 -->
    <?php if ($totalPages > 1): ?>
    <nav>
        <ul class="pagination">
            <?php if ($page > 1): ?>
                <li><a href="?page=<?php echo $page - 1; ?>&search=<?php echo urlencode($search); ?>">« 上一页</a></li>
            <?php endif; ?>
            <?php for ($i = 1; $i <= $totalPages; $i++): ?>
                <li class="<?php echo $i === $page ? 'active' : ''; ?>">
                    <a href="?page=<?php echo $i; ?>&search=<?php echo urlencode($search); ?>"><?php echo $i; ?></a>
                </li>
            <?php endfor; ?>
            <?php if ($page < $totalPages): ?>
                <li><a href="?page=<?php echo $page + 1; ?>&search=<?php echo urlencode($search); ?>">下一页 »</a></li>
            <?php endif; ?>
        </ul>
    </nav>
    <?php endif; ?>
</div>

添加图书(含文件上传)

<?php
// admin/books/create.php
require_once '../../config/database.php';
require_once '../../includes/auth.php';
requireLogin();
$conn = getConnection();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $title = $_POST['title'];
    $author = $_POST['author'];
    $isbn = $_POST['isbn'];
    $price = $_POST['price'];
    $description = $_POST['description'];
    $published_date = $_POST['published_date'];
    // 验证
    $errors = [];
    if (empty($title)) $errors[] = "标题不能为空";
    if (empty($author)) $errors[] = "作者不能为空";
    // 处理文件上传
    $coverImage = null;
    if (isset($_FILES['cover_image']) && $_FILES['cover_image']['error'] === 0) {
        $allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
        $fileType = $_FILES['cover_image']['type'];
        if (in_array($fileType, $allowedTypes)) {
            $fileExt = pathinfo($_FILES['cover_image']['name'], PATHINFO_EXTENSION);
            $fileName = time() . '_' . uniqid() . '.' . $fileExt;
            $uploadPath = '../../uploads/' . $fileName;
            if (move_uploaded_file($_FILES['cover_image']['tmp_name'], $uploadPath)) {
                $coverImage = $fileName;
            } else {
                $errors[] = "文件上传失败";
            }
        } else {
            $errors[] = "只允许上传JPEG、PNG、GIF格式的图片";
        }
    }
    if (empty($errors)) {
        $stmt = $conn->prepare("INSERT INTO books (title, author, isbn, price, description, published_date, cover_image, created_by) 
                                VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
        $stmt->execute([
            $title, $author, $isbn, $price, $description, 
            $published_date, $coverImage, $_SESSION['user_id']
        ]);
        header('Location: index.php?message=success');
        exit();
    }
}
?>
<?php include '../includes/header.php'; ?>
<div class="container">
    <h2>新增图书</h2>
    <?php if (!empty($errors)): ?>
        <div class="alert alert-danger">
            <ul>
                <?php foreach ($errors as $error): ?>
                    <li><?php echo $error; ?></li>
                <?php endforeach; ?>
            </ul>
        </div>
    <?php endif; ?>
    <form method="POST" enctype="multipart/form-data">
        <div class="form-group">
            <label>书名 *</label>
            <input type="text" name="title" value="<?php echo $_POST['title'] ?? ''; ?>" required>
        </div>
        <div class="form-group">
            <label>作者 *</label>
            <input type="text" name="author" value="<?php echo $_POST['author'] ?? ''; ?>" required>
        </div>
        <div class="form-group">
            <label>ISBN</label>
            <input type="text" name="isbn" value="<?php echo $_POST['isbn'] ?? ''; ?>">
        </div>
        <div class="form-group">
            <label>价格</label>
            <input type="number" step="0.01" name="price" value="<?php echo $_POST['price'] ?? ''; ?>">
        </div>
        <div class="form-group">
            <label>出版日期</label>
            <input type="date" name="published_date" value="<?php echo $_POST['published_date'] ?? ''; ?>">
        </div>
        <div class="form-group">
            <label>封面图片</label>
            <input type="file" name="cover_image" accept="image/*">
        </div>
        <div class="form-group">
            <label>简介</label>
            <textarea name="description" rows="4"><?php echo $_POST['description'] ?? ''; ?></textarea>
        </div>
        <button type="submit" class="btn btn-primary">保存</button>
        <a href="index.php" class="btn btn-secondary">返回</a>
    </form>
</div>

公共函数库

<?php
// includes/functions.php
function sanitizeInput($data) {
    return htmlspecialchars(strip_tags(trim($data)));
}
function redirect($url) {
    header("Location: $url");
    exit();
}
function formatPrice($price) {
    return '¥' . number_format($price, 2);
}
function generateCSRFToken() {
    if (!isset($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}
function verifyCSRFToken($token) {
    return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
?>

安全实践

// 密码哈希处理
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 验证码示例
session_start();
$randomCode = rand(1000, 9999);
$_SESSION['captcha'] = $randomCode;
// 创建简单的验证码图片
header('Content-Type: image/png');
$image = imagecreatetruecolor(120, 40);
$bg = imagecolorallocate($image, 255, 255, 255);
$textColor = imagecolorallocate($image, 0, 0, 0);
imagefilledrectangle($image, 0, 0, 120, 40, $bg);
imagestring($image, 5, 30, 10, $randomCode, $textColor);
imagepng($image);
imagedestroy($image);

前端样式

/* assets/css/style.css */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    line-height: 1.6;
    color: #333;
    background-color: #f5f5f5;
}
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 30px;
    padding-bottom: 20px;
    border-bottom: 1px solid #dee2e6;
}
.btn {
    display: inline-block;
    padding: 8px 16px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    transition: all 0.3s;
}
.btn-primary {
    background-color: #007bff;
    color: white;
}
.btn-success {
    background-color: #28a745;
    color: white;
}
.btn-danger {
    background-color: #dc3545;
    color: white;
}
.btn-secondary {
    background-color: #6c757d;
    color: white;
    text-decoration: none;
}
.table {
    width: 100%;
    background-color: white;
    border-collapse: collapse;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.table th,
.table td {
    padding: 12px;
    text-align: left;
    border-bottom: 1px solid #dee2e6;
}
.table th {
    background-color: #f8f9fa;
    font-weight: 600;
}
.form-group {
    margin-bottom: 20px;
}
.form-group label {
    display: block;
    margin-bottom: 5px;
    font-weight: 500;
}
.form-group input,
.form-group textarea,
.form-group select {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid #ced4da;
    border-radius: 4px;
    font-size: 14px;
}
.alert {
    padding: 12px 20px;
    margin-bottom: 20px;
    border-radius: 4px;
}
.alert-danger {
    background-color: #f8d7da;
    color: #721c24;
    border: 1px solid #f5c6cb;
}
.alert-success {
    background-color: #d4edda;
    color: #155724;
    border: 1px solid #c3e6cb;
}
.pagination {
    display: flex;
    list-style: none;
    padding: 20px 0;
    justify-content: center;
}
.pagination li {
    margin: 0 5px;
}
.pagination a,
.pagination .active {
    display: block;
    padding: 8px 12px;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    text-decoration: none;
    color: #007bff;
}
.pagination .active {
    background-color: #007bff;
    color: white;
}

📚 进阶实战项目推荐

在线商城系统

  • 商品管理(SKU、库存、分类)
  • 购物车功能
  • 订单管理
  • 支付集成(微信/支付宝)
  • 优惠券系统

博客/CMS系统

  • 文章管理(Markdown编辑)
  • 用户权限(管理员/编辑/作者)
  • 评论系统
  • 分类标签
  • SEO优化

API开发项目

// RESTful API示例
// api/books.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$method = $_SERVER['REQUEST_METHOD'];
$conn = getConnection();
switch ($method) {
    case 'GET':
        // 获取图书列表
        $stmt = $conn->query("SELECT * FROM books");
        $books = $stmt->fetchAll(PDO::FETCH_ASSOC);
        echo json_encode(['status' => 'success', 'data' => $books]);
        break;
    case 'POST':
        // 创建图书
        $data = json_decode(file_get_contents('php://input'), true);
        $stmt = $conn->prepare("INSERT INTO books (title, author) VALUES (?, ?)");
        $stmt->execute([$data['title'], $data['author']]);
        echo json_encode(['status' => 'success', 'id' => $conn->lastInsertId()]);
        break;
}

🎯 部署与上线

生产环境配置

# .htaccess 安全配置
Options -Indexes
ServerSignature Off
# 防止URL注入
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9/]+)$ index.php?route=$1 [QSA,L]
# 保护配置文件
<FilesMatch "\.(env|ini|log)$">
    Order allow,deny
    Deny from all
</FilesMatch>

性能优化

// 使用Redis缓存
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 检查缓存
$cached = $redis->get('books_list');
if ($cached) {
    $books = json_decode($cached, true);
} else {
    // 查询数据库
    $stmt = $conn->query("SELECT * FROM books");
    $books = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // 设置缓存,10分钟过期
    $redis->setex('books_list', 600, json_encode($books));
}

💡 学习建议

  1. 从简单开始:先完成一个简单的CRUD系统
  2. 逐步增加功能:慢慢加入用户认证、文件上传、分页等特性
  3. 代码规范:使用PSR-4自动加载规范,确保代码整洁
  4. 安全第一:从开始就注意SQL注入、XSS攻击防护
  5. 版本控制:使用Git管理代码,方便回滚和协作

这个实战项目涵盖了PHP开发的绝大多数核心概念,建议你按照项目结构逐步实现,并在理解的基础上尝试添加新功能(如API接口、数据导出、批量操作等),这将帮助你快速提升PHP实战能力。

抱歉,评论功能暂时关闭!