PHP 表單處理 (Form Handling)

PHP 可以接收和處理 HTML 表單提交的資料。

基本表單

<form action="process.php" method="POST">
    <label>名稱:<input type="text" name="name"></label>
    <label>Email:<input type="email" name="email"></label>
    <button type="submit">送出</button>
</form>
<?php
// process.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name = $_POST['name'] ?? '';
    $email = $_POST['email'] ?? '';
    
    echo "名稱:$name<br>";
    echo "Email:$email";
}
?>

GET vs POST

特性GETPOST
資料位置URL 查詢字串HTTP 請求主體
資料大小有限制(約 2KB)較大
安全性較低(會顯示在 URL)較高
適用場合搜尋、篩選登入、表單提交

表單驗證

<?php
$errors = [];
$data = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 清理輸入
    $name = trim($_POST['name'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $age = $_POST['age'] ?? '';
    
    // 驗證名稱
    if (empty($name)) {
        $errors['name'] = '名稱不能為空';
    } elseif (strlen($name) < 2) {
        $errors['name'] = '名稱至少 2 個字元';
    }
    
    // 驗證 Email
    if (empty($email)) {
        $errors['email'] = 'Email 不能為空';
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors['email'] = 'Email 格式不正確';
    }
    
    // 驗證年齡
    if (!empty($age)) {
        $age = filter_var($age, FILTER_VALIDATE_INT);
        if ($age === false || $age < 0 || $age > 150) {
            $errors['age'] = '請輸入有效的年齡';
        }
    }
    
    // 如果沒有錯誤
    if (empty($errors)) {
        $data = ['name' => $name, 'email' => $email, 'age' => $age];
        // 處理資料...
    }
}
?>

<form method="POST">
    <div>
        <label>名稱:</label>
        <input type="text" name="name" value="<?= htmlspecialchars($name ?? '') ?>">
        <?php if (isset($errors['name'])): ?>
            <span class="error"><?= $errors['name'] ?></span>
        <?php endif; ?>
    </div>
    
    <div>
        <label>Email:</label>
        <input type="email" name="email" value="<?= htmlspecialchars($email ?? '') ?>">
        <?php if (isset($errors['email'])): ?>
            <span class="error"><?= $errors['email'] ?></span>
        <?php endif; ?>
    </div>
    
    <button type="submit">送出</button>
</form>

不同的輸入類型

<?php
// 單選框
$gender = $_POST['gender'] ?? '';

// 複選框
$hobbies = $_POST['hobbies'] ?? [];

// 下拉選單
$country = $_POST['country'] ?? '';

// 文字區域
$message = $_POST['message'] ?? '';
?>

<form method="POST">
    <!-- 單選框 -->
    <label><input type="radio" name="gender" value="male"> 男</label>
    <label><input type="radio" name="gender" value="female"> 女</label>
    
    <!-- 複選框 -->
    <label><input type="checkbox" name="hobbies[]" value="reading"> 閱讀</label>
    <label><input type="checkbox" name="hobbies[]" value="gaming"> 遊戲</label>
    <label><input type="checkbox" name="hobbies[]" value="sports"> 運動</label>
    
    <!-- 下拉選單 -->
    <select name="country">
        <option value="">請選擇</option>
        <option value="tw">台灣</option>
        <option value="us">美國</option>
        <option value="jp">日本</option>
    </select>
    
    <!-- 文字區域 -->
    <textarea name="message"></textarea>
    
    <button type="submit">送出</button>
</form>

CSRF 保護

<?php
session_start();

// 產生 Token
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// 驗證 Token
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!isset($_POST['csrf_token']) || 
        $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        die('CSRF 驗證失敗');
    }
}
?>

<form method="POST">
    <input type="hidden" name="csrf_token" 
           value="<?= $_SESSION['csrf_token'] ?>">
    <!-- 其他欄位 -->
</form>

XSS 防護

<?php
// 永遠使用 htmlspecialchars 輸出使用者資料
$name = $_POST['name'] ?? '';
?>

<!-- 安全輸出 -->
<p>名稱:<?= htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?></p>

<!-- 危險:直接輸出 -->
<!-- <p>名稱:<?= $name ?></p> -->