This guide shows how to accept data sent from an HTML form with PHP and insert it into a MySQL database, using prepared statements to prevent SQL injection.
🧩 Concept & Data Flow #
When a user submits the form, the data is sent to a PHP script via POST, which then writes it to MySQL. The flow:
| Step | Action |
|---|---|
| 1 | The HTML form sends data to the PHP script via method="post" (action points to insert.php) |
| 2 | PHP reads each field’s value with $_POST |
| 3 | Validate and clean (trim, filter_var) |
| 4 | Bind parameters with a prepared statement and run INSERT |
| 5 | Return a success/failure message and close the connection |
⚠️ The most common mistake: concatenating $_POST values straight into the SQL string. This is how SQL injection happens — any user input must go through a prepared statement.
📋 Setup: Create the Database & Table #
Create the table first, and use the utf8mb4 charset to support Chinese characters:
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 Form Example #
Each input’s name attribute becomes the key PHP reads from $_POST:
<!-- 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>💾 Write to MySQL (procedural 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);
?>The first argument of mysqli_stmt_bind_param() is a type string — one character per column:
| Char | Type | Meaning |
|---|---|---|
| s | string | text (most common) |
| i | integer | whole number |
| d | double | floating-point number |
| b | blob | binary (images, PDFs, etc.) |
🛡️ PDO Version (recommended) #
PDO supports many databases and lets you use named placeholders (:name), which are easier to maintain:
<?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 Quick Comparison #
| Item | mysqli | PDO |
|---|---|---|
| Placeholder | ? (positional) | ? or :name (named) |
| Bind parameters | mysqli_stmt_bind_param() | execute(array) or bindParam() |
| Databases | MySQL only | many databases |
| Error handling | manual checks | can throw Exception (try-catch) |
| Recommendation | fair | ✅ recommended (portable, easier to maintain) |
⚠️ Notes #
| Note | Explanation |
|---|---|
| Always use prepared statements | Concatenating strings is an SQL injection risk; binding parameters is safe |
| Validate input | Check required fields and email format (filter_var) |
Set charset utf8mb4 | Otherwise Chinese characters turn into mojibake |
| Least privilege | Give the DB user only INSERT permission, not root |
| Don’t output raw SQL errors | Log errors and return a generic message to users |
| CSRF protection | Add a token to the form to prevent cross-site request forgery |
| Prevent duplicate submits | Use the PRG pattern (POST-Redirect-GET) to avoid re-inserting on refresh |
🔗 References #
| Source | Link |
|---|---|
| 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 |
