Connecting to a Database with PDO

First lesson of the PHP and Databases (PDO) course, free to read: about 15 minutes, 3 exercises and 3 quiz questions. This is the catalogue's base lesson; in the student space, it is rewritten for your profile.

Objectives

  • Understand the role of PDO and database drivers
  • Write a DSN for SQLite, MySQL and PostgreSQL
  • Configure PDO to throw exceptions and return associative arrays

PDODSN and connection optionsPDOException

A web application almost always stores its data in a database: accounts, products, orders… In PHP, the modern and standard way to access it is PDO (PHP Data Objects). PDO offers the same API whatever the database management system (DBMS): MySQL/MariaDB, PostgreSQL, SQLite, SQL Server… Each DBMS is handled by a driver (pdo_mysql, pdo_pgsql, pdo_sqlite). You can check which ones are installed with php -m.

The DSN: the database’s address

You create a connection by instantiating the PDO class with a DSN (Data Source Name), then optionally a user, a password and options.

// In-memory SQLite: ideal for learning and for tests
$pdo = new PDO('sqlite::memory:');

// SQLite in a file
$pdo = new PDO('sqlite:' . __DIR__ . '/app.sqlite');

// MySQL / MariaDB: always specify charset=utf8mb4
$pdo = new PDO('mysql:host=127.0.0.1;port=3306;dbname=boutique;charset=utf8mb4', 'app', 'secret');

// PostgreSQL
$pdo = new PDO('pgsql:host=127.0.0.1;port=5432;dbname=boutique', 'app', 'secret');

The essential options

$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // an SQL error throws an exception
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // rows as associative arrays
    PDO::ATTR_EMULATE_PREPARES   => false,                  // real prepared statements (MySQL)
];
$pdo = new PDO($dsn, $user, $password, $options);

Since PHP 8.0, the default error mode is already ERRMODE_EXCEPTION, but writing it explicitly makes the code readable and robust. Without exceptions, a faulty query would simply return false, and the error would go unnoticed.

Handling a connection failure

try {
    $pdo = new PDO($dsn, getenv('DB_USER'), getenv('DB_PASSWORD'), $options);
} catch (PDOException $e) {
    error_log($e->getMessage());          // we log the details
    http_response_code(500);
    exit('Service temporarily unavailable.'); // neutral message for the user
}

The message of a PDOException can reveal the host or database name: you log it, you don’t display it. Likewise, the password must never appear in versioned code: read it from an environment variable (getenv(), $_ENV) or a .env file ignored by Git.

Running a first statement

For a statement with no parameter and no result (creating a table, for example), exec() is enough. It returns the number of affected rows.

$pdo->exec('CREATE TABLE produits (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    nom TEXT NOT NULL,
    prix REAL NOT NULL
)');

Good to know: since PHP 8.4, PDO::connect($dsn, ...) returns a driver-specific subclass (Pdo\Sqlite, Pdo\Mysql…). new PDO(...) remains valid and is what you will encounter most often. PHP 8.5, however, deprecates driver-specific constants declared on PDO (for example PDO::SQLITE_ATTR_OPEN_FLAGS): use those of the subclass (Pdo\Sqlite::ATTR_OPEN_FLAGS, Pdo\Mysql::ATTR_...).

In summary: a correct DSN, explicit options, credentials outside the code and discreet error handling. This is the foundation of the rest of the course.

Exercises

Solutions and tests stay on the server. In the student space, your code is run and checked, and the tutor gives hints before the solution.

  1. CodeDifficulty 1Server sandbox2 hints

    Create a configured connection

    Write a function connecter(): PDO that returns an in-memory SQLite connection configured with:

    • the error mode PDO::ERRMODE_EXCEPTION;
    • the default fetch mode PDO::FETCH_ASSOC.
    function connecter(): PDO
    {
        // À compléter
    }
  2. CodeDifficulty 2Server sandbox2 hints

    Create and fill a table

    Write a function initialiser(PDO $pdo): void that:

    1. creates a clients table (id INTEGER PRIMARY KEY AUTOINCREMENT, nom TEXT NOT NULL, ville TEXT NOT NULL);
    2. inserts three customers: Alice (Lyon), Bruno (Lille), Chloé (Lyon).

    Use exec().

    function initialiser(PDO $pdo): void
    {
        // CREATE TABLE puis INSERT
    }
  3. Multiple choiceDifficulty 11 hint

    Choose the right DSN

    Which DSN connects to the MySQL database boutique on the local machine with full encoding (accents and emojis)?

    • mysql:host=127.0.0.1;dbname=boutique;charset=utf8mb4
    • mysql://127.0.0.1/boutique?charset=utf8
    • sqlite:boutique;charset=utf8mb4
    • mysql:host=127.0.0.1;database=boutique

End-of-lesson quiz

  1. With PDO::ERRMODE_EXCEPTION, what happens if an SQL query contains a syntax error?

    • The method silently returns false
    • A PDOException is thrown
    • PHP displays a warning and continues
  2. Which method should you use to run a CREATE TABLE with no parameter?

    • exec()
    • fetch()
    • lastInsertId()
  3. Where should you store the database password?

    • In the PHP code, as a constant
    • In an environment variable or an unversioned .env file
    • In an HTML comment

The rest of the course

  1. Reading Data: query, fetch and fetchAll15 min
  2. Prepared Statements and SQL Injection20 min
  3. Insert, Update, Delete and Transactions20 min
  4. Organizing Data Access: Entities and Repository20 min

PHP and Databases (PDO) · Intermediate · 3 h