PDO
Dans le contexte de PHP et MySQL, une API (Application Programming Interface) désigne un ensemble de fonctions et de méthodes permettant d'interagir avec une base de données MySQL. Elle constitue une couche d'abstraction entre le code PHP et le système de gestion de base de données (SGBD), permettant d'exécuter des opérations (lecture, écriture, mise à jour, suppression) sans gérer les détails techniques de la communication avec MySQL.
PHP propose deux APIs principales pour se connecter à une base de données MySQL :
MySQLi (MySQL Improved)
- Mode mixte : peut être utilisé en mode orienté objet ou procédural.
- Fonctionnalités avancées : support des transactions, des prepared statements, etc.
- Gestion des erreurs améliorée : facilite le débogage des problèmes de connexion ou d'exécution.
- Spécifique à MySQL : ne fonctionne qu'avec MySQL.
- Compatibilité : nécessite MySQL 4.1.3 ou supérieur.
PDO (PHP Data Objects)
- Interface orientée objet : syntaxe moderne, structurée et uniforme.
- Polyvalence : compatible avec plusieurs SGBD (MySQL, PostgreSQL, SQLite, Oracle, etc.).
- Prepared Statements : réduit drastiquement les risques d'injection SQL en séparant commandes et données.
- Gestion des exceptions : fournit un mécanisme robuste pour capturer et traiter les erreurs.
Tip
PDO est souvent préféré pour sa flexibilité et sa compatibilité avec différents SGBD.
MySQLi reste cependant un bon choix si l'application utilise exclusivement MySQL et souhaite exploiter des fonctionnalités propres à ce SGBD.
Connexion à une base de données avec PDO
➡️ PHP - PDO - Connections and Connection management
Pour établir une connexion avec PDO, il faut :
-
Créer une instance de la classe PDO : Cela initialise la connexion à la base de données via une chaîne DSN (Data Source Name), accompagnée d'un nom d'utilisateur et d'un mot de passe.
-
Gérer les exceptions : Il est fortement recommandé d'utiliser un bloc
try/catchafin de capturer les exceptions en cas d'erreur de connexion.
Exemple : Connexion à une base MySQL
<?php
$host = 'localhost';
$dbname = 'my_db';
$dsn = "mysql:host=$host;dbname=$dbname";
$username = 'username';
$password = 'password';
try {
// Crée une instance PDO pour se connecter à la base de données
$dbh = new PDO($dsn, $username, $password);
// Configure le mode d'erreur pour lancer des exceptions
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
…
} catch (PDOException $e) {
// Capture l'erreur et affiche un message
echo "Error : {$e->getMessage()}";
}
Explications
$host: Nom d'hôte ou adresse IP du serveur MySQL.$dbname: Nom de la base de données.$usernameet$password: Identifiants de connexion au SGBD.$dsn: La chaîne Data Source Name décrit le type de base (MySQL ici), l'hôte et la base de données ciblée.PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION: Configure PDO pour lever une exception à chaque erreur SQL, rendant la détection et le débogage beaucoup plus simples.
Points importants
- Sécurité : Evitez de stocker des identifiants sensibles directement dans le code. Utilisez des variables d'environnement, un fichier
.env, ou un fichier de configuration non accessible publiquement. - Gestion des erreurs : Le mode
ERRMODE_EXCEPTIONest indispensable pour une gestion propre et cohérente des erreurs SQL. - Centralisation : Dans une application plus importante, mettez la logique de connexion dans un fichier dédié ou une classe (ex.
Database.php) pour faciliter la maintenance et la réutilisation.
Requête préparée
➡️ PHP - PDO - Prepared statements and stored procedures
Les requêtes préparées sont un mécanisme essentiel de PDO. Elles permettent d'exécuter des requêtes SQL de manière plus sûre, plus performante, et plus lisible en séparant la logique SQL des données envoyées à la base.
Avantages
Prévention des injections SQL
En utilisant des marqueurs de paramètre au lieu de concaténer directement des valeurs dans une requête, PDO empêche toute tentative d'injection SQL.
Les valeurs liées sont automatiquement échappées et traitées comme des données, jamais comme du code SQL.
Amélioration des performances
Si une requête est exécutée plusieurs fois avec des valeurs différentes :
- Le serveur SQL compile une seule fois la requête.
- Puis il réutilise le plan d'exécution.
Très utile pour les insertions ou recherches répétitives.
Lisibilité & maintenance
Séparer la logique SQL des données rend le code plus propre et réduit les risques d'erreur.
Fonctionnement
-
Préparation de la requête
La requête contient des placeholders (paramètres), au lieu de valeurs.
-
Compilation par le serveur SQL
Le serveur analyse et optimise la requête. (Elle n'est pas encore exécutée.)
-
Liaison des valeurs
Les données réelles sont envoyées au serveur.
-
Exécution de la requête
Placeholders possibles
?→ placeholder anonyme:name→ placeholder nommé (recommandé, plus lisible)
Exemple
Table user
| id | name | |
|---|---|---|
| 1 | Alice Dupont | alice.dupont@example.com |
| 2 | Bob Martin | bob.martin@example.com |
| 3 | Claire Lune | claire.lune@example.com |
| 4 | Daniel Morand | daniel.morand@example.com |
Exemple complet en PDO
<?php
$name = 'Bob Martin';
$email = 'bob.martin@example.com';
$query = 'SELECT * FROM user WHERE name = :name AND email = :email';
// Préparation de la requête (Etapes 1 + 2)
$stmt = $dbh->prepare($query);
// Liaison des valeurs (Etape 3)
$stmt->bindParam(':name', $name);
$stmt->bindParam(':email', $email);
// Exécution de la requête (Etape 4)
$stmt->execute();
// Récupération des résultats
while ($row = $stmt->fetch()) {
var_dump($row);
}

Astuce importante : bindParam() vs bindValue()
bindParam()
- Lie la variable par référence.
- Sa valeur est lue au moment du
execute(). - Utile si la variable est modifiée après la liaison.
bindValue()
- Lie la valeur immédiatement.
- Recommandé dans la plupart des cas, plus simple et plus sûr.
Exemple équivalent avec bindValue() :
<?php
$stmt->bindValue(':name', $name);
$stmt->bindValue(':email', $email);
Récupération des données
Lors de l'utilisation de PDO pour interagir avec une base de données MySQL en PHP, plusieurs méthodes permettent de récupérer les résultats d'une requête. Ces méthodes offrent une flexibilité selon la manière dont les données doivent être traitées.
fetch()
- Récupère la prochaine ligne du jeu de résultats.
- Peut retourner :
- Un tableau associatif (
PDO::FETCH_ASSOC) - Un tableau indexé (
PDO::FETCH_NUM) - Ou les deux (
PDO::FETCH_BOTH)
- Un tableau associatif (
- Souvent utilisée dans une boucle, pour traiter les résultats ligne par ligne.
<?php
$query = 'SELECT * FROM user';
$stmt = $dbh->prepare($query);
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
var_dump($row);
}

fetchAll()
- Récupère toutes les lignes d'un coup.
- Retourne un tableau dont chaque élément correspond à une ligne.
- Utile lorsque toutes les données doivent être traitées simultanément.
<?php
$query = 'SELECT * FROM user';
$stmt = $dbh->prepare($query);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
var_dump($rows);

fetchColumn()
- Récupère une seule colonne de la prochaine ligne du résultat.
- Par défaut, récupère la colonne 0 (première colonne), mais un index peut être spécifié.
- Pratique pour obtenir une seule valeur par ligne (par exemple : une liste d'e-mails).
<?php
$query = 'SELECT * FROM user';
$stmt = $dbh->prepare($query);
$stmt->execute();
while ($column = $stmt->fetchColumn(1)) {
var_dump($column);
}

Points importants
Modes de récupération
-
PDO::FETCH_ASSOCRetourne un tableau associatif (clés = noms des colonnes).
-
PDO::FETCH_NUMRetourne un tableau indexé numériquement.
-
PDO::FETCH_BOTHMode par défaut : tableau associatif et tableau numérique.
Performances
-
Utilisez
fetch()oufetchColumn()pour traiter les résultats ligne par ligne→ plus performant, mémoire minimale
-
Utilisez
fetchAll()uniquement lorsque toutes les données doivent être chargées en mémoire en une seule fois→ pratique, mais peut consommer beaucoup de RAM si le jeu de résultats est volumineux
Récupérer une valeur auto-incrémentée
La méthode lastInsertId() de l'objet PDO permet de récupérer la valeur générée automatiquement par une colonne auto-incrémentée lors du dernier INSERT.
Elle est indispensable lorsqu'on doit réutiliser l'ID d'un enregistrement nouvellement créé.
<?php
$name = 'John Doe';
$email = 'john.doe@example.com';
$query = 'INSERT INTO user (name, email) VALUES (:name, :email)';
$stmt = $dbh->prepare($query);
$stmt->bindParam(':name', $name);
$stmt->bindParam(':email', $email);
$stmt->execute();
// Récupération de l'ID auto-incrémenté
$last_id = $dbh->lastInsertId();
echo "The ID of the newly inserted line is: $last_id";

Table user
| id | name | |
|---|---|---|
| 1 | Alice Dupont | alice.dupont@example.com |
| 2 | Bob Martin | bob.martin@example.com |
| 3 | Claire Lune | claire.lune@example.com |
| 4 | Daniel Morand | daniel.morand@example.com |
| 5 | John Doe | john.doe@example.com |
Transaction
➡️ PHP - PDO - Transactions and auto-commit
Les transactions garantissent qu'un ensemble d'opérations est exécuté entièrement ou pas du tout. En cas d'erreur, toutes les modifications effectuées depuis le début de la transaction peuvent être annulées, évitant ainsi les incohérences.
Elles sont particulièrement utiles lorsqu'un traitement implique plusieurs requêtes liées entre elles.
Exemple : Ajouter un utilisateur et un achat associé
Table user
| id | name | |
|---|---|---|
| 1 | Alice Dupont | alice.dupont@example.com |
| 2 | Bob Martin | bob.martin@example.com |
| 3 | Claire Lune | claire.lune@example.com |
| 4 | Daniel Morand | daniel.morand@example.com |
Table purchase
| id | user_id | date | amount |
|---|---|---|---|
| 1 | 1 | 2023-01-15 | 150.00 |
| 2 | 2 | 2023-01-17 | 200.00 |
| 3 | 1 | 2023-02-01 | 25.00 |
Objectif
- Créer un nouvel utilisateur (
INSERTdansuser) - Récupérer son ID via
lastInsertId() - Enregistrer un achat lié à cet utilisateur dans
purchase - Garantir que les deux opérations réussissent ou qu'aucune n'est appliquée
Code
<?php
$dbh = new PDO($dsn, $username, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
// Début de la transaction
$dbh->beginTransaction();
// Ajouter un utilisateur
$name = 'Eva Petit';
$email = 'eva.petit@example.com';
$query = 'INSERT INTO user (name, email) VALUES (:name, :email)';
$stmt = $dbh->prepare($query);
$stmt->bindParam(':name', $name);
$stmt->bindParam(':email', $email);
$stmt->execute();
$user_id = $dbh->lastInsertId();
// Ajouter un achat
$date = '2023-03-15';
$amount = 100.0;
$query = 'INSERT INTO purchase (user_id, date, amount) VALUES (:user_id, :date, :amount)';
$stmt = $dbh->prepare($query);
$stmt->bindParam(':user_id', $user_id);
$stmt->bindParam(':date', $date);
$stmt->bindParam(':amount', $amount);
$stmt->execute();
// Validation de la transaction
$dbh->commit();
echo 'Successful transaction: User and purchase added.';
} catch (PDOException $e) {
// Annuler la transaction en cas d'erreur
$dbh->rollBack();
echo "Transaction error: {$e->getMessage()}";
}

Table user
| id | name | |
|---|---|---|
| 1 | Alice Dupont | alice.dupont@example.com |
| 2 | Bob Martin | bob.martin@example.com |
| 3 | Claire Lune | claire.lune@example.com |
| 4 | Daniel Morand | daniel.morand@example.com |
| 5 | Eva Petit | eva.petit@example.com |
Table purchase
| id | user_id | date | amount |
|---|---|---|---|
| 1 | 1 | 2023-01-15 | 150.00 |
| 2 | 2 | 2023-01-17 | 200.00 |
| 3 | 1 | 2023-02-01 | 25.00 |
| 4 | 5 | 2023-03-15 | 100.00 |
Points clés
- Transaction : garantit l'atomicité des opérations.
lastInsertId(): indispensable pour enchaîner des insertions dépendantes.- Bloc
try/catch: intercepte les erreurs et déclenche unrollBack()en cas d'échec. - Intégrité des données : aucune insertion partielle.