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);
  }
?>
This site keeps one shared 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.

3.1 Vulnerable - never do this
<?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);
?>
3.2 Safe - prepared statement with bind_param
<?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 typeMeaning
sstring
iinteger
ddouble (float)
bblob (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.

Total tracked visits so far: 126409
BrowserVisits
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

IssueProblemFix
Concatenating user input into SQLSQL injectionUse prepare() + bind_param()
Not checking connect_errorConfusing fatal errors laterCheck right after connecting and fail fast
Ignoring query errorsSilent failuresCheck $conn->error / catch exceptions
Leaving connections openWastes DB resourcesCall $conn->close() when done
Running write queries on page loadAccidental data changesOnly SELECT on GET requests; guard writes behind POST + validation
Next: Continue with JSON - encoding and decoding data, the format most APIs (including this site's own AJAX calls) speak.