How to Connect PHP to MySQL Using PDO (Step by Step)

Connecting PHP to a MySQL database is the first step in almost every PHP project, and PDO (PHP Data Objects) is the modern, recommended way to do it. This guide shows you exactly how to connect using PDO, run queries safely, handle errors properly, and avoid the common mistakes, with complete, working code you can use right away.

Why PDO and not the old mysql_ functions? The original mysql_ functions are removed from modern PHP. The two current options are PDO and MySQLi. PDO is preferred because it works with many databases (not just MySQL) and has a clean, consistent way to run safe, parameterized queries. This guide uses PDO throughout.
What you’ll learn
  1. What you need before you start
  2. The PDO connection (complete code)
  3. Understanding each part of the connection
  4. Running a SELECT query safely
  5. Inserting data with prepared statements
  6. Update and delete
  7. Error handling done right
  8. Common mistakes and fixes

1. What you need before you start

To follow along you need PHP installed (most commonly through XAMPP, WAMP, or a similar local server), a running MySQL server, and a database to connect to. For the examples, assume a database called test_db with a simple users table containing id, name, and email columns. Adjust the names to match your own database.

2. The PDO connection (complete code)

Here is the complete, correct way to open a PDO connection to MySQL. Everything else in this guide builds on it.

// database connection using PDO
$host = 'localhost';
$dbname = 'test_db';
$username = 'root';
$password = '';  // often empty on local XAMPP

try {
  $pdo = new PDO(
    "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
    $username,
    $password
  );
  // make PDO throw exceptions on errors
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  // return rows as clean associative arrays
  $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

  echo "Connected successfully";
} catch (PDOException $e) {
  echo "Connection failed: " . $e->getMessage();
}

3. Understanding each part of the connection

That code is short but every line matters. Here is what each part does:

  • The DSN ("mysql:host=$host;dbname=$dbname;charset=utf8mb4") is the data source name, it tells PDO which database type, which host, which database, and which character set to use. Including charset=utf8mb4 ensures proper handling of all characters, including emoji and many languages.
  • The try/catch wraps the connection so that if it fails, you handle the error gracefully instead of crashing. PDO throws a PDOException on failure.
  • ATTR_ERRMODE = ERRMODE_EXCEPTION tells PDO to throw an exception whenever a query fails. This is important, without it, errors can pass silently and cause confusing bugs.
  • ATTR_DEFAULT_FETCH_MODE = FETCH_ASSOC makes query results come back as associative arrays (keyed by column name), which is the cleanest format to work with.
Set those two attributes on every connection. Exception error mode saves you hours of debugging, and the default fetch mode keeps your result-handling code clean. They are the mark of a properly configured PDO connection.

4. Running a SELECT query safely

To read data, you run a query and loop over the results. For a query with no user input, a simple query() is fine:

// fetch all users
$stmt = $pdo->query("SELECT id, name, email FROM users");
foreach ($stmt as $row) {
  echo $row['name'] . " - " . $row['email'] . "
"
; }

But the moment user input is involved, for example fetching one user by an ID that came from a form or URL, you must use a prepared statement instead, never insert the value directly into the query.

// fetch one user by id (safe)
$id = $_GET['id'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch();
if ($user) {
  echo $user['name'];
}

5. Inserting data with prepared statements

Inserting data almost always involves user input, so always use a prepared statement. The ? placeholders are filled by the values you pass to execute(), and PDO handles escaping them safely.

// insert a new user (safe)
$name = 'Asha';
$email = 'asha@example.com';

$stmt = $pdo->prepare(
  "INSERT INTO users (name, email) VALUES (?, ?)"
);
$stmt->execute([$name, $email]);

echo "New user added with ID: " . $pdo->lastInsertId();

Note lastInsertId(), it returns the auto-generated id of the row you just inserted, which is often useful (for example, to immediately use that new record).

6. Update and delete

Update and delete follow the exact same prepared-statement pattern:

// update a user's email
$stmt = $pdo->prepare("UPDATE users SET email = ? WHERE id = ?");
$stmt->execute(['new@example.com', 5]);
echo $stmt->rowCount() . " row(s) updated";

// delete a user
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([5]);

rowCount() tells you how many rows were affected, useful for confirming an update or delete actually changed something.

7. Error handling done right

Because you set ERRMODE_EXCEPTION, any failed query throws a PDOException. Wrap database operations that might fail in try/catch so you can handle problems cleanly:

try {
  $stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
  $stmt->execute([$name, $email]);
} catch (PDOException $e) {
  // log the real error; show a friendly message
  echo "Something went wrong. Please try again.";
}
Never show raw database errors to users in production. Messages like $e->getMessage() can reveal your database structure to attackers. Show a friendly message to the user, and log the real error to a file for yourself instead.

8. Common mistakes and fixes

  • “Connection failed” / access denied: wrong username or password. On local XAMPP the user is usually root with an empty password.
  • Putting variables directly in the SQL string: this causes SQL injection. Always use ? placeholders and execute().
  • Forgetting the charset: leaving out charset=utf8mb4 can cause garbled characters. Always include it.
  • Not setting exception error mode: without it, failed queries fail silently and waste your debugging time.
  • Reusing a closed connection: define your connection once in a file and include it where needed, rather than reconnecting everywhere.
A clean approach for real projects: put the connection code in a separate file like db.php and include it wherever you need the database, so you have a single connection setup used everywhere. This is exactly how the projects on CodeZips are structured, browse the ready-to-run PHP projects with full source code to see the PDO connection used in complete, working applications.

Frequently asked questions

Should I use PDO or MySQLi?

Both work and both support safe prepared statements. PDO is generally preferred because it supports many database systems (not just MySQL) and has a cleaner, more consistent interface. If you might ever switch databases or want one consistent style, PDO is the better choice.

What is a DSN in PDO?

The DSN (Data Source Name) is the string that tells PDO how to connect, the database type, host, database name, and charset, for example mysql:host=localhost;dbname=test_db;charset=utf8mb4.

How does PDO prevent SQL injection?

By using prepared statements with placeholders. When you pass values through execute(), PDO sends them separately from the SQL command, so user input is always treated as data and never as part of the query.

Why is my connection failing on localhost?

The most common causes are MySQL not running, or wrong credentials. On a fresh XAMPP install, the username is usually root and the password is empty. Make sure MySQL is started in your control panel.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top