PDO (PHP Data Objects) is PHP's modern, database-agnostic interface for accessing databases. Its most important feature is prepared statements, which prevent SQL injection — the critical security practice for any database code.
Connecting with PDO
<?php
$pdo = new PDO(
"mysql:host=localhost;dbname=app;charset=utf8mb4",
$username, $password,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // throw exceptions on errors
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // fetch rows as associative arrays
]
);
PDO works across databases (MySQL, PostgreSQL, SQLite, etc.) with the same API. Setting ERRMODE_EXCEPTION makes database errors throw catchable exceptions.
The critical practice: prepared statements (prevent SQL injection)
// ❌ NEVER do this — concatenating user input → SQL INJECTION vulnerability
$result = $pdo->query("SELECT * FROM users WHERE id = " . $_GET['id']);
// input "1 OR 1=1" or "1; DROP TABLE users" → disaster
// ✅ ALWAYS use prepared statements with bound parameters
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ? AND active = ?");
$stmt->execute([$_GET['id'], true]); // parameters bound SAFELY
$user = $stmt->fetch();
// named parameters work too
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
$stmt->execute(["email" => $email]);
Prepared statements send the SQL and the data separately — the database treats parameters as pure data, never as executable SQL. This makes injection impossible, regardless of what the user inputs. This is non-negotiable for any query involving user input.
Fetching results and other operations
$stmt->fetch(); // one row (associative array)
$stmt->fetchAll(); // all rows
$stmt->fetchColumn(); // a single value
// INSERT/UPDATE/DELETE — also use prepared statements
$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->execute([$name, $email]);
$pdo->lastInsertId(); // the new row's ID
Transactions
$pdo->beginTransaction();
try {
$pdo->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?")->execute([$amt, $from]);
$pdo->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?")->execute([$amt, $to]);
$pdo->commit(); // all-or-nothing
} catch (Throwable $e) {
$pdo->rollBack(); // undo on failure
}
Why it matters
Safe database access is one of the most security-critical aspects of PHP development, and PDO with prepared statements is the essential practice every PHP developer must know.
SQL injection — caused by concatenating untrusted user input directly into SQL queries — is one of the most common and devastating web vulnerabilities (allowing attackers to read, modify, or destroy data), and it's entirely preventable. Prepared statements with bound parameters are the solution: by sending the SQL structure and the data separately, the database treats user input as pure data that can never be executed as SQL, making injection impossible regardless of input.
Understanding that you must always use prepared statements for any query involving user input (never string concatenation) is non-negotiable, security-essential knowledge.
Beyond security, PDO's database-agnostic interface, exception-based error handling, flexible result fetching, and transaction support make it the robust, modern way to access databases in PHP.
Since database access is fundamental to most applications and SQL injection is both common and catastrophic, mastering PDO and the absolute discipline of prepared statements is among the most important things a PHP developer must understand — it's the difference between a secure application and one vulnerable to a serious, well-known attack. (Frameworks like Laravel use PDO under the hood with safe query builders/ORMs, but the underlying principle remains.)
