<返回目录     Powered by claud/xia兄

第9课: 表单处理与验证

表单处理基础

表单是Web应用中用户与服务器交互的主要方式。PHP提供了多种方式来处理和验证表单数据。

HTTP请求方法

<?php
// 检查请求方法
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    echo "这是POST请求";
} elseif ($_SERVER["REQUEST_METHOD"] == "GET") {
    echo "这是GET请求";
}

// 获取请求URI
echo $_SERVER["REQUEST_URI"];

// 获取用户代理
echo $_SERVER["HTTP_USER_AGENT"];

// 获取客户端IP
echo $_SERVER["REMOTE_ADDR"];
?>

获取表单数据

GET请求数据

<?php
// URL: example.com?name=张三&age=25

// 获取单个参数
$name = $_GET["name"] ?? "默认值";
$age = $_GET["age"] ?? 0;

// 检查参数是否存在
if (isset($_GET["name"])) {
    echo "姓名: " . $_GET["name"];
}

// 获取所有GET参数
print_r($_GET);

// 构建查询字符串
$params = ["page" => 1, "limit" => 10];
$query = http_build_query($params);
// 结果: page=1&limit=10
?>

POST请求数据

<?php
// 获取POST数据
$username = $_POST["username"] ?? "";
$password = $_POST["password"] ?? "";

// 获取所有POST数据
$data = $_POST;

// 检查是否为POST请求
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    // 处理表单提交
    $email = $_POST["email"];
}

// $_REQUEST包含GET、POST和COOKIE
$value = $_REQUEST["field"] ?? "";
?>

处理复选框和多选

<!-- HTML表单 -->
<form method="post">
    <input type="checkbox" name="hobbies[]" value="reading"> 阅读
    <input type="checkbox" name="hobbies[]" value="sports"> 运动
    <input type="checkbox" name="hobbies[]" value="music"> 音乐
</form>

<?php
// PHP处理
if (isset($_POST["hobbies"])) {
    $hobbies = $_POST["hobbies"];  // 数组
    foreach ($hobbies as $hobby) {
        echo $hobby . "\n";
    }
}
?>

表单验证

基本验证

<?php
$errors = [];

// 验证必填字段
if (empty($_POST["name"])) {
    $errors["name"] = "姓名不能为空";
}

// 验证字符串长度
if (strlen($_POST["username"]) < 3) {
    $errors["username"] = "用户名至少3个字符";
}

if (strlen($_POST["password"]) < 6) {
    $errors["password"] = "密码至少6位";
}

// 验证数字范围
$age = $_POST["age"] ?? 0;
if ($age < 18 || $age > 100) {
    $errors["age"] = "年龄必须在18-100之间";
}

// 验证是否为数字
if (!is_numeric($_POST["phone"])) {
    $errors["phone"] = "电话号码必须是数字";
}
?>

使用filter_var验证

<?php
// 验证邮箱
$email = $_POST["email"];
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errors["email"] = "邮箱格式不正确";
}

// 验证URL
$website = $_POST["website"];
if (!filter_var($website, FILTER_VALIDATE_URL)) {
    $errors["website"] = "URL格式不正确";
}

// 验证IP地址
$ip = $_POST["ip"];
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
    $errors["ip"] = "IP地址格式不正确";
}

// 验证整数
$number = $_POST["number"];
if (filter_var($number, FILTER_VALIDATE_INT) === false) {
    $errors["number"] = "必须是整数";
}

// 验证布尔值
$agree = filter_var($_POST["agree"], FILTER_VALIDATE_BOOLEAN);
?>

正则表达式验证

<?php
// 验证手机号(中国)
$phone = $_POST["phone"];
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
    $errors["phone"] = "手机号格式不正确";
}

// 验证身份证号
$idcard = $_POST["idcard"];
if (!preg_match('/^\d{17}[\dXx]$/', $idcard)) {
    $errors["idcard"] = "身份证号格式不正确";
}

// 验证用户名(字母数字下划线)
$username = $_POST["username"];
if (!preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
    $errors["username"] = "用户名只能包含字母、数字和下划线,3-20位";
}

// 验证密码强度
$password = $_POST["password"];
if (!preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/', $password)) {
    $errors["password"] = "密码必须包含大小写字母和数字,至少8位";
}
?>

数据过滤和清理

基本过滤

<?php
// 去除首尾空格
$name = trim($_POST["name"]);

// 去除HTML标签
$content = strip_tags($_POST["content"]);

// 保留特定标签
$html = strip_tags($_POST["html"], '<p><br><strong>');

// 转义HTML特殊字符(防XSS)
$text = htmlspecialchars($_POST["text"], ENT_QUOTES, 'UTF-8');

// 去除反斜杠
$data = stripslashes($_POST["data"]);
?>

使用filter_var过滤

<?php
// 过滤邮箱
$email = filter_var($_POST["email"], FILTER_SANITIZE_EMAIL);

// 过滤URL
$url = filter_var($_POST["url"], FILTER_SANITIZE_URL);

// 过滤整数
$number = filter_var($_POST["number"], FILTER_SANITIZE_NUMBER_INT);

// 过滤浮点数
$price = filter_var($_POST["price"], FILTER_SANITIZE_NUMBER_FLOAT,
    FILTER_FLAG_ALLOW_FRACTION);

// 过滤字符串
$string = filter_var($_POST["text"], FILTER_SANITIZE_STRING);

// 自定义过滤
$clean = filter_var($_POST["data"], FILTER_CALLBACK, [
    "options" => function($value) {
        return strtoupper(trim($value));
    }
]);
?>

防止安全攻击

防止XSS攻击

<?php
// 清理用户输入
function clean_input($data) {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
    return $data;
}

$username = clean_input($_POST["username"]);
$comment = clean_input($_POST["comment"]);

// 输出到HTML时也要转义
echo htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
?>

防止CSRF攻击

<?php
session_start();

// 生成CSRF令牌
if (empty($_SESSION["csrf_token"])) {
    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
}

// 在表单中包含令牌
?>
<form method="post">
    <input type="hidden" name="csrf_token"
           value="<?php echo $_SESSION['csrf_token']; ?>">
    <!-- 其他表单字段 -->
</form>

<?php
// 验证令牌
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    if (!isset($_POST["csrf_token"]) ||
        $_POST["csrf_token"] !== $_SESSION["csrf_token"]) {
        die("CSRF验证失败");
    }
    // 处理表单
}
?>

完整表单处理示例

<?php
session_start();

// 初始化变量
$errors = [];
$success = false;

// 生成CSRF令牌
if (empty($_SESSION["csrf_token"])) {
    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
}

// 处理表单提交
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    // 验证CSRF令牌
    if (!isset($_POST["csrf_token"]) ||
        $_POST["csrf_token"] !== $_SESSION["csrf_token"]) {
        die("安全验证失败");
    }

    // 获取并清理数据
    $name = trim($_POST["name"] ?? "");
    $email = trim($_POST["email"] ?? "");
    $phone = trim($_POST["phone"] ?? "");
    $message = trim($_POST["message"] ?? "");

    // 验证姓名
    if (empty($name)) {
        $errors["name"] = "姓名不能为空";
    } elseif (strlen($name) < 2) {
        $errors["name"] = "姓名至少2个字符";
    }

    // 验证邮箱
    if (empty($email)) {
        $errors["email"] = "邮箱不能为空";
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors["email"] = "邮箱格式不正确";
    }

    // 验证手机号
    if (empty($phone)) {
        $errors["phone"] = "手机号不能为空";
    } elseif (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
        $errors["phone"] = "手机号格式不正确";
    }

    // 验证留言
    if (empty($message)) {
        $errors["message"] = "留言不能为空";
    } elseif (strlen($message) < 10) {
        $errors["message"] = "留言至少10个字符";
    }

    // 如果没有错误,处理数据
    if (empty($errors)) {
        // 转义数据
        $name = htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
        $email = htmlspecialchars($email, ENT_QUOTES, 'UTF-8');
        $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');

        // 保存到数据库或发送邮件
        // ...

        $success = true;

        // 重定向防止重复提交
        header("Location: success.php");
        exit;
    }
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>联系表单</title>
</head>
<body>
    <h1>联系我们</h1>

    <?php if (!empty($errors)): ?>
        <div class="errors">
            <ul>
                <?php foreach ($errors as $error): ?>
                    <li><?php echo $error; ?></li>
                <?php endforeach; ?>
            </ul>
        </div>
    <?php endif; ?>

    <form method="post" action="">
        <input type="hidden" name="csrf_token"
               value="<?php echo $_SESSION['csrf_token']; ?>">

        <label>姓名:</label>
        <input type="text" name="name"
               value="<?php echo htmlspecialchars($_POST['name'] ?? ''); ?>">

        <label>邮箱:</label>
        <input type="email" name="email"
               value="<?php echo htmlspecialchars($_POST['email'] ?? ''); ?>">

        <label>手机:</label>
        <input type="text" name="phone"
               value="<?php echo htmlspecialchars($_POST['phone'] ?? ''); ?>">

        <label>留言:</label>
        <textarea name="message"><?php echo htmlspecialchars($_POST['message'] ?? ''); ?></textarea>

        <button type="submit">提交</button>
    </form>
</body>
</html>

文件上传表单

<!-- HTML表单 -->
<form method="post" enctype="multipart/form-data">
    <input type="file" name="avatar">
    <button type="submit">上传</button>
</form>

<?php
// PHP处理
if (isset($_FILES["avatar"])) {
    $file = $_FILES["avatar"];

    // 检查错误
    if ($file["error"] === UPLOAD_ERR_OK) {
        // 验证文件类型
        $allowed = ["jpg", "jpeg", "png", "gif"];
        $ext = strtolower(pathinfo($file["name"], PATHINFO_EXTENSION));

        if (!in_array($ext, $allowed)) {
            $errors[] = "只允许上传图片文件";
        }

        // 验证文件大小(2MB)
        if ($file["size"] > 2 * 1024 * 1024) {
            $errors[] = "文件大小不能超过2MB";
        }

        if (empty($errors)) {
            $newName = uniqid() . "." . $ext;
            move_uploaded_file($file["tmp_name"], "uploads/" . $newName);
        }
    }
}
?>
安全建议:

实践练习

  1. 注册表单:创建用户注册表单,包含用户名、邮箱、密码验证
  2. 登录系统:实现登录表单,验证用户凭据
  3. 联系表单:创建联系表单,包含CSRF保护
  4. 搜索功能:实现搜索表单,处理GET参数
  5. 文件上传:实现头像上传功能,包含验证
  6. 多步表单:创建多步骤表单,使用Session保存数据
  7. 表单验证类:封装表单验证逻辑为可重用的类
  8. Ajax表单:使用Ajax提交表单,返回JSON响应