Why Use PDO?

PDO provides a consistent interface for accessing databases in PHP. It supports multiple database systems and offers features such as prepared statements, which help prevent SQL injection attacks. Below are some key advantages of using PDO:

FeaturePDOMySQLi
Database SupportMultiple (MySQL, SQLite, etc.)MySQL only
Named ParametersYesNo
Prepared StatementsYesYes
Error HandlingException-basedProcedural or Object-oriented
TransactionsYesYes

Establishing a Database Connection

To start using PDO, you need to establish a connection to your database. Here’s an example of how to do this securely:

<?php
$host = '127.0.0.1';
$db   = 'my_database';
$user = 'my_user';
$pass = 'my_password';
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
    throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?>

Explanation:

  • Error Mode: Setting PDO::ATTR_ERRMODE to PDO::ERRMODE_EXCEPTION allows you to catch exceptions for error handling.
  • Fetch Mode: PDO::ATTR_DEFAULT_FETCH_MODE is set to PDO::FETCH_ASSOC for associative arrays, which is often easier to work with.
  • Emulated Prepares: Setting PDO::ATTR_EMULATE_PREPARES to false ensures that prepared statements are executed natively by the database.

Using Prepared Statements

Prepared statements are a crucial feature of PDO that help prevent SQL injection. Here’s an example of how to use them:

<?php
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$email = '[email protected]';
$stmt->execute(['email' => $email]);
$user = $stmt->fetch();

if ($user) {
    echo "User found: " . htmlspecialchars($user['name']);
} else {
    echo "No user found.";
}
?>

Explanation:

  • Placeholder: The :email is a named placeholder that is bound to the actual value in the execute method.
  • HTML Escaping: Use htmlspecialchars() to prevent XSS when outputting user data.

Handling Transactions

Transactions are essential for maintaining data integrity, especially when multiple related queries need to succeed or fail together. Here’s how to implement transactions with PDO:

<?php
try {
    $pdo->beginTransaction();

    $stmt1 = $pdo->prepare('INSERT INTO accounts (user_id, balance) VALUES (:user_id, :balance)');
    $stmt1->execute(['user_id' => 1, 'balance' => 100]);

    $stmt2 = $pdo->prepare('UPDATE users SET last_login = NOW() WHERE id = :user_id');
    $stmt2->execute(['user_id' => 1]);

    $pdo->commit();
} catch (\Exception $e) {
    $pdo->rollBack();
    echo "Failed: " . $e->getMessage();
}
?>

Explanation:

  • Transaction Control: Use beginTransaction(), commit(), and rollBack() to manage your transaction.
  • Error Handling: In case of an exception, the transaction is rolled back to maintain data integrity.

Query Optimization Techniques

  1. Indexing: Ensure that the columns used in WHERE clauses are indexed to speed up query execution.
  2. Limit Results: Use LIMIT to restrict the number of rows returned when only a subset is needed.
  3. Select Only Required Columns: Avoid using SELECT *. Specify only the columns you need.

Example of optimized query:

<?php
$stmt = $pdo->prepare('SELECT id, name FROM users WHERE status = :status LIMIT 10');
$stmt->execute(['status' => 'active']);
$activeUsers = $stmt->fetchAll();

foreach ($activeUsers as $user) {
    echo htmlspecialchars($user['name']) . "<br>";
}
?>

Explanation:

  • Column Specification: Specifying id and name instead of * reduces the amount of data transferred and processed.

Conclusion

By using PDO for database interactions in PHP, you can enhance security, maintainability, and performance. Following best practices such as using prepared statements, managing transactions, and optimizing queries will help you build robust applications.

Learn more with useful resources: