PHP and MySQL
Most real apps need to remember data between requests: users, products, orders, comments.
PHP talks to a MySQL database using the mysqli extension. This page covers
connecting, running SELECT queries, protecting your app from
SQL injection with prepared statements, the shape of
INSERT/UPDATE/DELETE statements, and a real,
read-only live demo pulled straight from this site's own database.
1) Connecting to MySQL with mysqli
Create a connection with new mysqli($host, $user, $pass, $db). Always check
$conn->connect_error right after so a bad connection fails loudly instead
of causing confusing errors later.
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "coding_with_sonu";
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
?>
connection.php at the project root. It picks the
right host/user/password based on a single $environment flag ("uat" or "prod")
and hands back a ready-to-use $conn object, so every page just does
require_once __DIR__ . '/../connection.php'; instead of repeating credentials.
2) Running a Basic SELECT Query
For simple, fixed queries with no user input, $conn->query() is enough.
Loop over the result with fetch_assoc() until it returns null.
<?php
$result = $conn->query("SELECT id, name, email FROM users");
while ($row = $result->fetch_assoc()) {
echo $row['name'] . " - " . $row['email'] . "<br>";
}
?>
3) Prepared Statements: Preventing SQL Injection
SQL injection happens when untrusted input (like a form field) is glued directly into a SQL string. An attacker can craft input that changes the meaning of your query - for example, reading data they shouldn't see, or bypassing a login check entirely. A prepared statement sends the query structure and the data separately, so user input is always treated as data, never as SQL code.
<?php
// VULNERABLE: user input concatenated directly into SQL
$username = $_GET['username'];
$sql = "SELECT * FROM users WHERE username = '" . $username . "'";
// If $username is: admin' --
// the query becomes: SELECT * FROM users WHERE username = 'admin' --'
// the -- comments out the rest, so it matches "admin" with no password check!
$result = $conn->query($sql);
?>
<?php
// SAFE: the ? placeholder holds the spot; bind_param() supplies the value
$username = $_GET['username'];
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo $row['username'];
}
$stmt->close();
?>
| bind_param type | Meaning |
|---|---|
s | string |
i | integer |
d | double (float) |
b | blob (binary data) |
4) INSERT, UPDATE and DELETE (Syntax Only)
Writing data follows the same prepared-statement pattern. These examples are shown for reference only - the live demo further down this page never modifies data, it only reads.
<?php
// INSERT
$stmt = $conn->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->bind_param("ss", $name, $email);
$stmt->execute();
// UPDATE
$stmt = $conn->prepare("UPDATE users SET email = ? WHERE id = ?");
$stmt->bind_param("si", $email, $id);
$stmt->execute();
// DELETE
$stmt = $conn->prepare("DELETE FROM users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
?>
5) Always Close the Connection
Once you're done with the database on a given request, close the connection:
<?php
$conn->close();
?>
6) Live Demo: Real Visitor Stats (Read-Only)
This is a genuinely live query. It uses this site's shared connection.php
and a prepared statement to run a read-only SELECT against the real
visitor_logs table that this site's visitor-tracking include writes to on
every page view. No INSERT, UPDATE or DELETE is
ever run here.
| Browser | Visits |
|---|---|
| Chrome | 63059 |
| Other | 35820 |
| Safari | 20733 |
| Firefox | 6652 |
| Opera | 143 |
<?php
$stmt = $conn->prepare(
"SELECT browser, COUNT(*) AS cnt FROM visitor_logs GROUP BY browser ORDER BY cnt DESC LIMIT 5"
);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// $row['browser'], $row['cnt']
}
?>
7) Best Practices & Common Mistakes
| Issue | Problem | Fix |
|---|---|---|
| Concatenating user input into SQL | SQL injection | Use prepare() + bind_param() |
Not checking connect_error | Confusing fatal errors later | Check right after connecting and fail fast |
| Ignoring query errors | Silent failures | Check $conn->error / catch exceptions |
| Leaving connections open | Wastes DB resources | Call $conn->close() when done |
| Running write queries on page load | Accidental data changes | Only SELECT on GET requests; guard writes behind POST + validation |