本文說明如何用 PHP 接收 HTML 表單(HTML form)送出的資料,再寫入(INSERT)MySQL 資料庫,並以 Prepared Statement(預處理語句)避免 SQL injection。
🧩 概念與資料流程 #
當用戶在網頁表單輸入資料並按送出,資料會經 POST 方法送到 PHP 腳本,再由 PHP 寫入 MySQL。整個流程:
| 步驟 | 動作 |
|---|---|
| 1 | HTML 表單用 method="post" 把資料送到 PHP 腳本(action 指向 insert.php) |
| 2 | PHP 用 $_POST 讀取各欄位的值 |
| 3 | 驗證(validate)+ 清理(trim、filter_var) |
| 4 | 用 Prepared Statement 綁定參數,執行 INSERT |
| 5 | 回傳成功/失敗訊息,關閉連線 |
⚠️ 最常見的錯誤:直接把 $_POST 的值拼進 SQL 字串(string concatenation)。這是 SQL injection(SQL 注入)的溫床,任何來自用戶的輸入都必須用 Prepared Statement 處理。
📋 前置準備:建立資料庫與資料表 #
先建立資料表,記得用 utf8mb4 編碼支援中文:
CREATE DATABASE IF NOT EXISTS demo
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE TABLE contacts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(190) NOT NULL,
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;📝 HTML 表單範例 #
每個 input 的 name 屬性,就是 PHP 讀取時 $_POST 的鍵(key):
<!-- index.html: the form sends data to insert.php via POST -->
<form action="insert.php" method="post">
<label>Name: <input type="text" name="name" required></label><br>
<label>Email: <input type="email" name="email" required></label><br>
<label>Message: <textarea name="message" required></textarea></label><br>
<button type="submit">Submit</button>
</form>💾 寫入 MySQL(mysqli 程序式 + Prepared Statement) #
<?php
// insert.php — write form data into MySQL (mysqli, prepared statement)
$conn = mysqli_connect('localhost', 'app_user', 'password', 'demo');
if (!$conn) {
die('Connection failed: ' . mysqli_connect_error());
}
mysqli_set_charset($conn, 'utf8mb4'); // support Chinese, avoid mojibake
// 1. Read and validate form data
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$message = trim($_POST['message'] ?? '');
if ($name === '' || $message === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
exit('Invalid input'); // stop if any required field is missing
}
// 2. Prepare an INSERT statement with ? placeholders
$stmt = mysqli_prepare($conn, 'INSERT INTO contacts (name, email, message) VALUES (?, ?, ?)');
// 3. Bind variables: "sss" = three strings, in order
mysqli_stmt_bind_param($stmt, 'sss', $name, $email, $message);
// 4. Execute and check the result
if (mysqli_stmt_execute($stmt)) {
$new_id = mysqli_insert_id($conn); // id of the newly inserted row
echo "Saved successfully. New record id: $new_id";
} else {
echo 'Error: ' . mysqli_stmt_error($stmt);
}
mysqli_stmt_close($stmt);
mysqli_close($conn);
?>mysqli_stmt_bind_param() 的第一個參數是型別字串(type string),每個字元代表一個欄位的資料型別:
| 字元 | 型別 | 說明 |
|---|---|---|
| s | string | 字串(最常用) |
| i | integer | 整數 |
| d | double | 浮點數 |
| b | blob | 二進位(圖片、PDF 等) |
🛡️ PDO 版本(更推薦) #
PDO 支援多種資料庫,且可用具名佔位符(named placeholder,:name),更易維護:
<?php
// insert.php — write form data into MySQL (PDO, recommended)
$dsn = 'mysql:host=localhost;dbname=demo;charset=utf8mb4';
$pdo = new PDO($dsn, 'app_user', 'password', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // throw exceptions on error
PDO::ATTR_EMULATE_PREPARES => false, // use native prepared statements
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// Read and validate form data
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$message = trim($_POST['message'] ?? '');
if ($name === '' || $message === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
exit('Invalid input');
}
// Prepared statement with named placeholders
$sql = 'INSERT INTO contacts (name, email, message) VALUES (:name, :email, :message)';
$stmt = $pdo->prepare($sql);
$stmt->execute([
':name' => $name,
':email' => $email,
':message' => $message,
]);
$new_id = (int) $pdo->lastInsertId(); // id of the newly inserted row
echo "Saved successfully. New record id: $new_id";
?>🔀 mysqli vs PDO 快速對照 #
| 項目 | mysqli | PDO |
|---|---|---|
| 佔位符(placeholder) | ?(位置型) | ? 或 :name(具名) |
| 綁定參數 | mysqli_stmt_bind_param() | execute(array) 或 bindParam() |
| 支援資料庫 | 只支援 MySQL | 多種資料庫 |
| 錯誤處理 | 手動檢查 | 可拋 Exception(try-catch) |
| 推薦程度 | 一般 | ✅ 更推薦(跨庫、易維護) |
⚠️ 注意事項 #
| 注意事項 | 說明 |
|---|---|
| 必用 Prepared Statement | 直接拼字串有 SQL injection 風險,綁定參數才安全 |
| 驗證輸入 | 檢查 required 欄位、email 格式(filter_var) |
設 charset utf8mb4 | 否則中文會變亂碼 |
| 最小權限 | 資料庫用戶只給 INSERT 權限,不要用 root |
| 不輸出 raw SQL error | 錯誤只寫 log,回傳通用訊息給用戶 |
| CSRF 防護 | 表單加 token,防止跨站請求偽造 |
| 防重複提交 | 用 PRG pattern(POST-Redirect-GET),避免 refresh 重複 insert |
🔗 參考連結 #
| 來源 | 連結 |
|---|---|
| PHP Manual — mysqli Prepared Statements | php.net |
| PHP Manual — PDO Prepared Statements | php.net |
| W3Schools — PHP MySQL Prepared Statements | w3schools.com |
| Tutorial Republic — Prepared Statements | tutorialrepublic.com |
| IONOS — Insert HTML form into MySQL | ionos.com |
