PHP 完整表单实例

PHP 完整表单实例中文讲解

以下是一个完整的 PHP 表单处理示例,涵盖了用户输入的收集、验证、清理、安全处理以及数据存储,适用于用户注册场景。示例包括 HTML 表单、服务器端验证(包括必需字段、邮箱和 URL 验证)、错误提示、数据回显、CSRF 保护和数据库交互。这个实例旨在展示 PHP 表单处理的全面流程,结合最佳实践和安全性。

功能概述

  • 表单字段:姓名(必需)、邮箱(必需,格式验证)、网站 URL(必需,格式验证)、密码(必需,最小长度 6 位)。
  • 验证:检查必需字段、验证邮箱和 URL 格式、清理输入数据。
  • 安全性:防止 XSS(跨站脚本攻击)、SQL 注入和 CSRF(跨站请求伪造)。
  • 数据存储:将验证通过的数据保存到 MySQL 数据库。
  • 用户体验:回显用户输入、显示友好错误提示。

完整代码示例

1. 数据库准备

假设使用 MySQL 数据库,创建名为 test 的数据库和 users 表:

CREATE DATABASE test;
USE test;
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE,
    website VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
2. HTML 表单 (register.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
    <style>
        .error { color: red; }
        label { display: inline-block; width: 100px; }
        input[type="text"], input[type="email"], input[type="url"], input[type="password"] {
            width: 200px; margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <h2>用户注册</h2>
    <form action="register.php" method="post">
        <?php
        // 启动会话以生成 CSRF 令牌
        session_start();
        if (empty($_SESSION['csrf_token'])) {
            $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
        }
        ?>
        <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">

        <label for="name">姓名*:</label>
        <input type="text" id="name" name="name" value="<?php echo isset($_POST['name']) ? htmlspecialchars($_POST['name']) : ''; ?>">
        <br>
        <label for="email">邮箱*:</label>
        <input type="email" id="email" name="email" value="<?php echo isset($_POST['email']) ? htmlspecialchars($_POST['email']) : ''; ?>">
        <br>
        <label for="website">网站 URL*:</label>
        <input type="url" id="website" name="website" value="<?php echo isset($_POST['website']) ? htmlspecialchars($_POST['website']) : ''; ?>">
        <br>
        <label for="password">密码*:</label>
        <input type="password" id="password" name="password">
        <br>
        <input type="submit" value="注册">
        <p>* 表示必需字段</p>
    </form>
</body>
</html>
3. PHP 处理脚本 (register.php)
<?php
// 启动会话以验证 CSRF 令牌
session_start();

// 初始化错误和数据数组
$errors = [];
$data = [];

// 检查请求方法
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 验证 CSRF 令牌
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        $errors[] = "CSRF 验证失败";
    } else {
        // 获取并清理输入
        $name = trim($_POST['name'] ?? '');
        $email = trim($_POST['email'] ?? '');
        $website = trim($_POST['website'] ?? '');
        $password = $_POST['password'] ?? '';

        // 清理输入数据
        $name = filter_var($name, FILTER_SANITIZE_STRING);
        $email = filter_var($email, FILTER_SANITIZE_EMAIL);
        $website = filter_var($website, FILTER_SANITIZE_URL);

        // 验证必需字段
        if (empty($name)) {
            $errors[] = "姓名是必需字段";
        }
        if (empty($email)) {
            $errors[] = "邮箱是必需字段";
        } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $errors[] = "无效的邮箱格式";
        }
        if (empty($website)) {
            $errors[] = "网站 URL 是必需字段";
        } elseif (!filter_var($website, FILTER_VALIDATE_URL)) {
            $errors[] = "无效的 URL 格式";
        } elseif (!preg_match("/^https?:\/\//", $website)) {
            $errors[] = "URL 必须以 http:// 或 https:// 开头";
        }
        if (empty($password)) {
            $errors[] = "密码是必需字段";
        } elseif (strlen($password) < 6) {
            $errors[] = "密码长度至少为 6 位";
        }

        // 如果没有错误,处理数据
        if (empty($errors)) {
            try {
                // 连接数据库
                $pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
                $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

                // 检查邮箱是否已存在
                $stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE email = :email");
                $stmt->execute(['email' => $email]);
                if ($stmt->fetchColumn() > 0) {
                    $errors[] = "邮箱已被注册";
                } else {
                    // 插入数据
                    $stmt = $pdo->prepare("INSERT INTO users (name, email, website, password) VALUES (:name, :email, :website, :password)");
                    $stmt->execute([
                        'name' => $name,
                        'email' => $email,
                        'website' => $website,
                        'password' => password_hash($password, PASSWORD_DEFAULT) // 加密密码
                    ]);
                    echo "注册成功!";
                    // 重置 CSRF 令牌
                    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
                    exit;
                }
            } catch (PDOException $e) {
                $errors[] = "数据库错误: " . $e->getMessage();
            }
        }
    }
}

// 显示错误并回显表单
if (!empty($errors)) {
    echo "<div class='error'><ul>";
    foreach ($errors as $error) {
        echo "<li>$error</li>";
    }
    echo "</ul></div>";
    include 'register.html';
} else {
    // 非 POST 请求,显示表单
    include 'register.html';
}
?>

代码解析

  1. HTML 表单
  • 使用 method="post" 提交数据,enctype 未设置(无文件上传)。
  • 包含 CSRF 令牌的隐藏字段 <input type="hidden" name="csrf_token">
  • 使用 htmlspecialchars() 回显用户输入,防止 XSS 攻击。
  • 添加简单的 CSS 样式,增强用户体验。
  1. PHP 处理逻辑
  • CSRF 保护:验证 CSRF 令牌,确保请求合法。
  • 输入清理:使用 trim() 移除空格,filter_var() 清理邮箱和 URL。
  • 必需字段验证:检查 nameemailwebsitepassword 是否为空。
  • 格式验证
    • 邮箱:filter_var($email, FILTER_VALIDATE_EMAIL)
    • URL:filter_var($website, FILTER_VALIDATE_URL) 和正则表达式确保协议为 http://https://
    • 密码:长度至少 6 位。
  • 唯一性检查:查询数据库,防止重复邮箱。
  • 数据库操作:使用 PDO 预处理语句,防止 SQL 注入;密码使用 password_hash() 加密。
  • 错误处理:集中存储错误,统一显示,并回显表单。
  1. 用户体验
  • 回显用户输入(如 nameemail),避免重复填写。
  • 错误提示清晰(如“无效的邮箱格式”),显示在表单上方。
  • 成功提交后显示确认消息并重置 CSRF 令牌。

配置说明

  • 数据库连接:替换 register.php 中的 userpass 为实际的 MySQL 用户名和密码,数据库名为 test
  • 文件结构
  project/
  ├── register.html
  ├── register.php
  • 服务器环境:确保 PHP 环境支持 PDO 和 MySQL 扩展,运行在支持会话的 Web 服务器(如 Apache 或 Nginx)上。

安全性措施

  • XSS 防护:使用 htmlspecialchars() 转义输出。
  • SQL 注入防护:使用 PDO 预处理语句。
  • CSRF 防护:生成和验证 CSRF 令牌。
  • 密码安全:使用 password_hash() 存储加密密码。

扩展功能

  1. 客户端验证
    在 HTML 中添加 required 和类型属性:
   <input type="email" name="email" required>
   <input type="url" name="website" required>

结合 JavaScript 增强客户端验证,但服务器端验证不可省略。

  1. 文件上传
    添加文件上传字段:
   <input type="file" name="avatar">

PHP 验证:

   if ($_FILES['avatar']['error'] === UPLOAD_ERR_OK) {
       $allowed = ['jpg', 'png'];
       $ext = pathinfo($_FILES['avatar']['name'], PATHINFO_EXTENSION);
       if (!in_array($ext, $allowed)) {
           $errors[] = "只允许 JPG 或 PNG 文件";
       }
       if ($_FILES['avatar']['size'] > 2 * 1024 * 1024) {
           $errors[] = "文件大小不能超过 2MB";
       }
   } else {
       $errors[] = "必须上传头像";
   }
  1. 重定向
    成功注册后重定向到成功页面:
   if (empty($errors)) {
       // 数据库操作后
       header("Location: success.php");
       exit;
   }

最佳实践

  • 验证优先:先验证必需字段,再验证格式,减少不必要开销。
  • 错误管理:使用数组集中存储错误,统一显示。
  • 清理输入:使用 filter_var()trim() 确保数据干净。
  • 安全优先:始终防范 XSS、SQL 注入和 CSRF。
  • 用户友好:提供具体错误提示,回显用户输入。

参考资源

总结

这个完整表单实例展示了 PHP 表单处理的全部流程,从用户输入收集、验证到数据存储,涵盖了必需字段、邮箱和 URL 验证、安全性措施和用户体验优化。开发者可以基于此示例扩展功能,如添加更多字段、集成前端验证或实现复杂业务逻辑,适用于用户注册、资料提交等场景。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注