PHP MySQL Prepared Statements: Secure, Efficient Queries with MySQLi and PDO
Last updated 3 months, 3 weeks ago | 325 views 75 5

Introduction: Why PHP MySQL Prepared Statements Matter
If you're building dynamic web applications with PHP and MySQL, you're likely using user input to query the database—like login forms, search filters, or form submissions.
This opens up one of the biggest security risks in web development: SQL injection.
Enter prepared statements—a PHP feature that makes your queries:
-
Secure by separating SQL logic from data
-
Efficient by reusing parsed queries
-
Reliable even with special characters (like quotes or backslashes)
Prepared statements are essential for any modern PHP application. In this article, we’ll explore how to use them with MySQLi and PDO, step-by-step.
What Are Prepared Statements?
A prepared statement is a feature where SQL is first compiled with placeholders, and then data is safely bound and executed.
Benefits of Prepared Statements:
-
Prevent SQL injection
-
Improve performance for repeated queries
-
Simplify escaping and quoting
-
Enhance code readability and maintainability
Using Prepared Statements with MySQLi
MySQLi offers both procedural and object-oriented syntax. We'll use object-oriented for cleaner structure.
✅ Basic MySQLi Example
<?php
$conn = new mysqli("localhost", "root", "", "test_db");
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Prepare statement
$stmt = $conn->prepare("SELECT id, email FROM users WHERE name = ?");
$stmt->bind_param("s", $name); // "s" = string
// Set value and execute
$name = "Alice";
$stmt->execute();
// Bind result
$stmt->bind_result($id, $email);
// Fetch results
while ($stmt->fetch()) {
echo "ID: $id, Email: $email<br>";
}
$stmt->close();
$conn->close();
?>
bind_param()
Data Types:
Code | Data Type |
---|---|
i |
Integer |
d |
Double |
s |
String |
b |
Blob (binary) |
Secure Insert Using MySQLi Prepared Statements
<?php
$conn = new mysqli("localhost", "root", "", "test_db");
$stmt = $conn->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->bind_param("ss", $name, $email);
$name = "Bob";
$email = "[email protected]";
$stmt->execute();
echo "User inserted with ID: " . $stmt->insert_id;
$stmt->close();
$conn->close();
?>
Using Prepared Statements with PDO
PDO (PHP Data Objects) offers a more flexible and cleaner API with support for named placeholders and multiple database types.
✅ Basic PDO Example
<?php
$pdo = new PDO("mysql:host=localhost;dbname=test_db", "root", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Prepare and execute
$stmt = $pdo->prepare("SELECT id, email FROM users WHERE name = :name");
$stmt->execute([':name' => 'Alice']);
// Fetch results
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "ID: {$row['id']}, Email: {$row['email']}<br>";
}
?>
Secure Insert with PDO
<?php
$pdo = new PDO("mysql:host=localhost;dbname=test_db", "root", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (:name, :email)");
$stmt->execute([
':name' => 'Charlie',
':email' => '[email protected]'
]);
echo "User inserted. Last ID: " . $pdo->lastInsertId();
?>
✅ PDO is recommended for large applications and frameworks due to its portability and cleaner syntax.
Complete Functional Example: Form + Prepared Statement
<?php
// Assume form submits POST data
$name = $_POST['name'];
$email = $_POST['email'];
$conn = new mysqli("localhost", "root", "", "test_db");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Prepare secure insert
$stmt = $conn->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->bind_param("ss", $name, $email);
if ($stmt->execute()) {
echo "User saved successfully!";
} else {
echo "Insert failed: " . $stmt->error;
}
$stmt->close();
$conn->close();
?>
MySQLi vs PDO for Prepared Statements
Feature | MySQLi | PDO |
---|---|---|
Syntax Style | Procedural / OOP | Object-Oriented |
Database Support | MySQL only | Multiple DB engines |
Placeholders | ? (positional only) |
? or :name (named) |
Error Handling | Manual | Exception-based (try/catch ) |
Bind Parameters | bind_param() with types |
execute() with array |
Tips & Common Pitfalls
✅ Best Practices
-
Always use prepared statements for any user input.
-
Use transactions when performing multiple dependent inserts.
-
Prefer named placeholders in PDO for readability.
-
Check for errors using
try/catch
in PDO orerror
in MySQLi.
❌ Common Mistakes
Mistake | Fix |
---|---|
Mixing variables into SQL manually | Use prepared statements instead |
Forgetting to bind parameters | Always bind before executing |
Using incorrect data types in bind | Match bind_param() types properly (e.g., s for string) |
Not closing statements | Use stmt->close() or unset objects to free resources |
Conclusion: Best Practices and What to Do Next
Using PHP MySQL prepared statements is the smartest way to build secure, scalable, and efficient web applications. Whether you choose MySQLi or PDO, you’ll gain stronger protection against SQL injection and more control over your queries.
Key Takeaways:
-
Always use prepared statements when dealing with user input.
-
PDO is ideal for larger applications and multiple DB types.
-
Prefer named placeholders for clarity in PDO.
-
Test and handle exceptions to prevent silent failures.