首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 入門

PHP 完整表單驗證實作:資料保持、錯誤顯示與安全處理全流程

     編輯:PHP 入門

PHP 完整表單驗證實作

表單驗證是 Web 應用資料品質的第一道防線。完整的驗證流程包含:伺服器端驗證(必要)、用戶端驗證(體驗優化)、資料保持(避免重輸入)、錯誤顯示(引導修正)、安全防護(XSS/CSRF)。本文實作一個完整範例。

表單需求

  • 姓名(必填,僅字母空格)
  • Email(必填,格式驗證)
  • 網址(選填,URL 格式)
  • 備註(選填,多行文字)
  • 性別(必填,單選)

完整程式碼:form_validation.php

<?php
// 初始化變數
$name = $email = $website = $comment = $gender = '';
$nameErr = $emailErr = $websiteErr = $genderErr = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 1. 統一輸入過濾函數
    function cleanInput($data) {
        $data = trim($data);
        $data = stripslashes($data);
        $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
        return $data;
    }
    
    // 2. 姓名驗證
    if (empty($_POST['name'])) {
        $nameErr = '姓名為必填';
    } else {
        $name = cleanInput($_POST['name']);
        if (!preg_match('/^[a-zA-Z\u4e00-\u9fa5\s]+$/u', $name)) {
            $nameErr = '姓名僅允許中英文字母與空格';
        }
    }
    
    // 3. Email 驗證
    if (empty($_POST['email'])) {
        $emailErr = 'Email 為必填';
    } else {
        $email = cleanInput($_POST['email']);
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $emailErr = 'Email 格式不正確';
        }
    }
    
    // 4. 網址驗證(選填)
    if (!empty($_POST['website'])) {
        $website = cleanInput($_POST['website']);
        if (!filter_var($website, FILTER_VALIDATE_URL)) {
            $websiteErr = '網址格式不正確(需含 https:// 或 https://)';
        }
    }
    
    // 5. 備註(選填)
    if (!empty($_POST['comment'])) {
        $comment = cleanInput($_POST['comment']);
    }
    
    // 6. 性別驗證
    if (empty($_POST['gender'])) {
        $genderErr = '請選擇性別';
    } else {
        $gender = cleanInput($_POST['gender']);
        if (!in_array($gender, ['male', 'female', 'other'])) {
            $genderErr = '無效的性別選項';
        }
    }
    
    // 7. 無錯誤時處理業務邏輯
    if (empty($nameErr) && empty($emailErr) && empty($websiteErr) && empty($genderErr)) {
        // 這裡處理:存資料庫、發送郵件、API 呼叫等
        $success = true;
        // 可選:PRG 模式重導向避免重複提交
        // header('Location: thank_you.php');
        // exit;
    }
}
?>
<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>會員註冊</title>
    <style>
        .error { color: #dc3545; font-size: 0.875rem; margin-top: 0.25rem; display: block; }
        .form-group { margin-bottom: 1rem; }
        label { display: block; margin-bottom: 0.5rem; font-weight: 500; }
        input, textarea, select { width: 100%; max-width: 400px; padding: 0.5rem; border: 1px solid #ced4da; border-radius: 0.25rem; }
        input.error-input, textarea.error-input, select.error-input { border-color: #dc3545; }
        .btn { padding: 0.5rem 1.5rem; background: #0d6efd; color: white; border: none; border-radius: 0.25rem; cursor: pointer; }
        .success-msg { color: #198754; padding: 1rem; background: #d1e7dd; border: 1px solid #badbcc; border-radius: 0.25rem; margin-bottom: 1rem; }
    </style>
</head>
<body>
    <h1>會員註冊</h1>
    
    <?php if (!empty($success)): ?>
        <div class="success-msg">註冊成功!感謝您的加入。</div>
    <?php endif; ?>
    
    <form method="post" action="">
        <!-- CSRF Token -->
        <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?? ''; ?>">
        
        <div class="form-group">
            <label>姓名 <span style="color:red">*</span></label>
            <input type="text" name="name" value="<?php echo $name; ?>" 
                   class="<?php echo $nameErr ? 'error-input' : ''; ?>">
            <?php if ($nameErr): ?><span class="error"><?php echo $nameErr; ?></span><?php endif; ?>
        </div>
        
        <div class="form-group">
            <label>Email <span style="color:red">*</span></label>
            <input type="email" name="email" value="<?php echo $email; ?>"
                   class="<?php echo $emailErr ? 'error-input' : ''; ?>">
            <?php if ($emailErr): ?><span class="error"><?php echo $emailErr; ?></span><?php endif; ?>
        </div>
        
        <div class="form-group">
            <label>個人網址</label>
            <input type="url" name="website" value="<?php echo $website; ?>"
                   class="<?php echo $websiteErr ? 'error-input' : ''; ?>" placeholder="https://example.com">
            <?php if ($websiteErr): ?><span class="error"><?php echo $websiteErr; ?></span><?php endif; ?>
        </div>
        
        <div class="form-group">
            <label>備註</label>
            <textarea name="comment" rows="4" class="<?php echo $commentErr ? 'error-input' : ''; ?>"><?php echo $comment; ?></textarea>
        </div>
        
        <div class="form-group">
            <label>性別 <span style="color:red">*</span></label>
            <div>
                <label><input type="radio" name="gender" value="male" <?php echo $gender === 'male' ? 'checked' : ''; ?>> 男生</label>
                <label><input type="radio" name="gender" value="female" <?php echo $gender === 'female' ? 'checked' : ''; ?>> 女生</label>
                <label><input type="radio" name="gender" value="other" <?php echo $gender === 'other' ? 'checked' : ''; ?>> 其他</label>
            </div>
            <?php if ($genderErr): ?><span class="error"><?php echo $genderErr; ?></span><?php endif; ?>
        </div>
        
        <button type="submit" class="btn">送出註冊</button>
    </form>
</body>
</html>

關鍵技術點解析

1. 資料保持(Sticky Form)

value="<?php echo $name; ?>" 將驗證後/原始輸入回填,避免用戶重複輸入。checked 屬性控制單選/核取方塊狀態。

2. 錯誤訊息關聯顯示

每個欄位下方顯示對應錯誤,error-input 樣式標紅邊框,視覺引導明確。

3. 輸入過濾管線

function cleanInput($data) {
    $data = trim($data);           // 移除首尾空白
    $data = stripslashes($data);   // 移除反斜槓(魔術引號遺留)
    $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); // 編碼 HTML 實體
    return $data;
}

4. 正則表達式驗證

  • 姓名:/^[a-zA-Z\u4e00-\u9fa5\s]+$/u(中英文+空格,u 修飾符支援 Unicode)
  • Email:filter_var($email, FILTER_VALIDATE_EMAIL)(內建 RFC 5322 相容)
  • URL:filter_var($url, FILTER_VALIDATE_URL)(需含 scheme)

5. CSRF 保護

表單隱藏 csrf_token,伺服器驗證 hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])。

6. PRG 模式(Post-Redirect-Get)

成功處理後 header('Location: ...') 重導向,防止重新整理重複提交。

進階:驗證類別封裝

<?php
class FormValidator {
    private $data = [];
    private $errors = [];
    private $rules = [];
    
    public function __construct(array $data) {
        $this->data = $data;
    }
    
    public function rule(string $field, callable $validator, string $message): self {
        $this->rules[$field][] = [$validator, $message];
        return $this;
    }
    
    public function required(string $field, string $message = '此欄位為必填'): self {
        return $this->rule($field, fn($v) => !empty($v), $message);
    }
    
    public function email(string $field, string $message = 'Email 格式錯誤'): self {
        return $this->rule($field, fn($v) => filter_var($v, FILTER_VALIDATE_EMAIL), $message);
    }
    
    public function regex(string $field, string $pattern, string $message): self {
        return $this->rule($field, fn($v) => (bool)preg_match($pattern, $v), $message);
    }
    
    public function inArray(string $field, array $allowed, string $message = '無效選項'): self {
        return $this->rule($field, fn($v) => in_array($v, $allowed, true), $message);
    }
    
    public function validate(): bool {
        foreach ($this->rules as $field => $rules) {
            $value = $this->data[$field] ?? '';
            foreach ($rules as [$validator, $message]) {
                if (!$validator($value)) {
                    $this->errors[$field][] = $message;
                }
            }
        }
        return empty($this->errors);
    }
    
    public function getErrors(): array { return $this->errors; }
    public function getError(string $field): string { return $this->errors[$field][0] ?? ''; }
    public function hasError(string $field): bool { return isset($this->errors[$field]); }
}

// 使用範例
$validator = new FormValidator($_POST);
$validator->required('name', '姓名必填')
          ->regex('name', '/^[a-zA-Z\s]+$/', '僅允許字母空格')
          ->required('email', 'Email必填')
          ->email('email')
          ->required('gender', '請選擇性別')
          ->inArray('gender', ['male','female','other']);

if ($validator->validate()) {
    // 成功
} else {
    $nameErr = $validator->getError('name');
    // ...
}
?>

前端驗證輔助(HTML5 + JavaScript)

伺服器驗證不可省略,前端驗證提升體驗:

<input type="text" name="name" required pattern="[A-Za-z\s]+" title="僅允許字母空格">
<input type="email" name="email" required>
<input type="url" name="website" pattern="https?://.+">

<script>
// 表單送出前攔截
document.querySelector('form').addEventListener('submit', function(e) {
    if (!this.checkValidity()) {
        e.preventDefault();
        this.reportValidity(); // 顯示瀏覽器內建錯誤訊息
    }
});
</script>

最佳實踐檢查清單

  • [ ] 所有輸入經過伺服器端驗證
  • [ ] 錯誤訊息具體、友善、關聯欄位
  • [ ] 資料保持:重新顯示用戶輸入
  • [ ] CSRF Token 保護所有狀態變更表單
  • [ ] 成功後 PRG 重導向
  • [ ] 敏感操作(密碼變更、刪除)需二次確認
  • [ ] 記錄驗證失敗日誌(安全稽核)
  • [ ] 速率限制防暴力提交
  • [ ] 測試邊界值、特殊字元、長字串、SQL 注入嘗試

完整表單驗證不僅是「檢查資料」,更是「引導用戶成功完成任務」的介面設計。結合伺服器嚴格驗證、前端即時回饋、友善錯誤提示、資料保持機制,能大幅提升轉換率與資料品質。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved