Aller au contenu

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.

➡️ PHP - Documentation MySQLi

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.

➡️ PHP - Documentation PDO

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 :

  1. 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.

  2. Gérer les exceptions : Il est fortement recommandé d'utiliser un bloc try/catch afin 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.
  • $username et $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_EXCEPTION est 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

  1. Préparation de la requête

    La requête contient des placeholders (paramètres), au lieu de valeurs.

  2. Compilation par le serveur SQL

    Le serveur analyse et optimise la requête. (Elle n'est pas encore exécutée.)

  3. Liaison des valeurs

    Les données réelles sont envoyées au serveur.

  4. Exécution de la requête

Placeholders possibles

  • ? → placeholder anonyme
  • :name → placeholder nommé (recommandé, plus lisible)

Exemple

Table user

id name email
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);
}

pdo-prepared-statement.png

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()

➡️ PHP - PDO - 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)
  • 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);
}

pdo-fetch.png

fetchAll()

➡️ PHP - PDO - 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);

pdo-fetchAll.png

fetchColumn()

➡️ PHP - PDO - 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);
}

pdo-fetchColumn.png

Points importants

Modes de récupération

  • PDO::FETCH_ASSOC

    Retourne un tableau associatif (clés = noms des colonnes).

  • PDO::FETCH_NUM

    Retourne un tableau indexé numériquement.

  • PDO::FETCH_BOTH

    Mode par défaut : tableau associatif et tableau numérique.

Performances

  • Utilisez fetch() ou fetchColumn() 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

➡️ PHP - PDO - lastInsertId

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";

pdo-lastInsertId.png

Table user

id name email
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 email
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

  1. Créer un nouvel utilisateur (INSERT dans user)
  2. Récupérer son ID via lastInsertId()
  3. Enregistrer un achat lié à cet utilisateur dans purchase
  4. 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()}";
}

pdo-example.png

Table user

id name email
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 un rollBack() en cas d'échec.
  • Intégrité des données : aucune insertion partielle.