3.1MySQL & PDO : connexion et requêtes préparées
L'essentiel en 30 secondes
- PDO est l'interface moderne : connexion objet unifiée (new PDO), multi-bases, erreurs en exceptions avec ERRMODE_EXCEPTION.
- La requête préparée est non négociable : marqueurs :nom ou ?, données passées à execute() — la base les traite comme données pures, jamais comme du code.
- Le CRUD se lit en quatre verbes : INSERT / SELECT / UPDATE / DELETE, résultats via fetch() (une ligne) ou fetchAll() (toutes).
Mini-cas filé — Riad sauve sa boutique d'une injection
Riad, développeur web à Constantine, gère les commandes d'une boutique en concaténant l'email saisi dans son SELECT — un test avec un guillemet piégé lui renvoie toute la table clients. Il réécrit tout en PDO : connexion centralisée dans un fichier config.php avec ERRMODE_EXCEPTION, requêtes prepare() + execute() à marqueurs nommés, fetch/fetchAll pour lire. Même boutique, base désormais impénétrable par ce vecteur.
Ce même cas revient dans le cours, le schéma, le script et le défi 48h.
1. MySQL, PDO et connexion centralisée
Une application réelle stocke durablement ses données : MySQL (ou MariaDB), base relationnelle, organise les données en tables (colonnes typées), lignes (enregistrements), interrogées en SQL : SELECT (lire), INSERT (créer), UPDATE (modifier), DELETE (supprimer) — les quatre opérations du CRUD.
L'approche moderne est PDO (PHP Data Objects) : interface unifiée et objet fonctionnant avec plusieurs bases (portabilité), requêtes préparées élégantes à marqueurs nommés, et gestion d'erreurs par exceptions en mode PDO::ERRMODE_EXCEPTION (try/catch propre). Réflexes de Riad : centraliser la connexion (un seul fichier config/connexion réutilisé, jamais une connexion par page) et ne jamais versionner les identifiants — ils vivent hors du code (variables d'environnement), jamais en dur.
2. Requêtes préparées : prepare + execute, toujours
Le danger : construire du SQL en concaténant des données utilisateur ('SELECT … WHERE email = ' . $email) crée une faille critique d'injection SQL — l'attaquant injecte du SQL exécuté par la base (lecture, modification, destruction). La solution est la requête préparée : on prépare avec des marqueurs (? ou nommés :email) — $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email') — puis on exécute en passant les données séparément — $stmt->execute(['email' => $email]) — et on récupère avec fetch() ou fetchAll().
La règle est absolue : toute requête avec des données variables DOIT être préparée ; ne JAMAIS concaténer de données dans le SQL. Riad l'applique au CRUD entier : INSERT d'une commande, SELECT des clients, UPDATE du statut, DELETE d'un brouillon — chaque execute() reçoit ses valeurs, le SQL ne contient que des marqueurs.
🔎 Exemple concret — Connexion PDO et SELECT préparé
Riad cherche un client par email, en toute sécurité.
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$stmt = $pdo->prepare('SELECT id, nom, email FROM clients WHERE email = :email');
$stmt->execute(['email' => $emailSaisi]);
$client = $stmt->fetch();
Lecture : connexion avec exceptions, marqueur :email, données dans execute(), une ligne via fetch().
Leçon : même avec un email piégé, la base ne voit que de la donnée — pas de code exécuté.
Vocabulaire de la section
- Base de données / MySQL
- Système stockant durablement les données ; MySQL/MariaDB : base relationnelle (tables, lignes, colonnes) interrogée en SQL (SELECT/INSERT/UPDATE/DELETE = CRUD).
Exemple simple : La table commandes de la boutique de Riad.
Ce que ce n'est pas : un tableau PHP vidé à chaque fin de requête.
Réflexe : stocker en base dès que la donnée doit durer.
- PDO
- PHP Data Objects : interface moderne, unifiée et objet pour se connecter aux bases et exécuter des requêtes ; multi-bases, exceptions, marqueurs nommés.
Exemple simple : La connexion centralisée de Riad avec ERRMODE_EXCEPTION.
Ce que ce n'est pas : les fonctions mysql_* obsolètes et supprimées.
Réflexe : toujours PDO avec ERRMODE_EXCEPTION.
- Injection SQL
- Faille critique : des données concaténées dans le SQL sont interprétées comme du code exécuté (lecture, modification, destruction de la base).
Exemple simple : Le guillemet piégé qui vidait la table clients de Riad.
Ce que ce n'est pas : une simple coquille d'affichage sans gravité.
Réflexe : ne jamais concaténer de donnée dans du SQL.
- Requête préparée
- Requête à marqueurs (? ou :nom) : prepare() envoie le code, execute() envoie les données séparément — la base les traite comme données pures.
Exemple simple : prepare('… WHERE email = :email') puis execute(['email' => $emailSaisi]).
Ce que ce n'est pas : une concaténation '…' . $email dans la chaîne SQL.
Réflexe : prepare + execute pour chaque requête à données variables.
- CRUD / fetch
- Create (INSERT), Read (SELECT), Update (UPDATE), Delete (DELETE) ; lecture via fetch() (une ligne) ou fetchAll() (toutes).
Exemple simple : Riad crée, lit, modifie puis supprime une commande test.
Ce que ce n'est pas : un seul SELECT copié-collé sans écriture.
Réflexe : maîtriser les quatre verbes avant tout framework.
En pratique — Persister des données avec MySQL et PDO
- Créez une base et une table dans MySQL (via phpMyAdmin) ; connectez-vous depuis PHP avec PDO (hôte, base, utilisateur, mot de passe hors du code).
- Réalisez un CRUD avec des REQUÊTES PRÉPARÉES : INSERT (créer), SELECT (lire, fetch/fetchAll), UPDATE (modifier), DELETE (supprimer).
- Utilisez systématiquement des marqueurs (:nom ou ?) et passez les données via execute() — JAMAIS de concaténation de données dans le SQL.
- Gérez les erreurs en try/catch (ERRMODE_EXCEPTION) et vérifiez qu'une entrée piégée ne casse plus rien.
Script prêt à dire — présenter son accès base PDO (30 s)
« Les commandes sont en MySQL via PDO : connexion centralisée avec exceptions, CRUD complet en requêtes préparées à marqueurs nommés, lecture en fetch et fetchAll. Testé avec une entrée piégée : la base la traite comme une simple donnée, l'injection est impossible. »
À dire en revue ; montrer un prepare/execute et le test piégé.
Modèle copier-coller — PDO + CRUD préparé
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// CREATE
$stmt = $pdo->prepare('INSERT INTO clients (nom, email) VALUES (:nom, :email)');
$stmt->execute(['nom' => $nom, 'email' => $email]);
// READ
$stmt = $pdo->prepare('SELECT id, nom, email FROM clients WHERE email = :email');
$stmt->execute(['email' => $email]);
$client = $stmt->fetch();
$tous = $pdo->query('SELECT id, nom FROM clients')->fetchAll();
// UPDATE + DELETE
$stmt = $pdo->prepare('UPDATE clients SET nom = :nom WHERE id = :id');
$stmt->execute(['nom' => $nom, 'id' => $id]);
$stmt = $pdo->prepare('DELETE FROM clients WHERE id = :id');
$stmt->execute(['id' => $id]);
Indépendant
Un CRUD clients en PDO préparé suffit à fiabiliser les devis stockés.
PME à Constantine
Riad centralise la connexion de la boutique et prépare chaque requête du back-office.
Équipe produit
Imposer un fichier de connexion unique pour des requêtes cohérentes entre modules.
Débutant
Cette semaine : une table, une connexion PDO et un SELECT préparé lu en fetch().
✓ Bonnes pratiques
- Centraliser la connexion PDO (un seul endroit, réutilisé partout).
- Préparer systématiquement : marqueurs + execute(), jamais de concaténation.
- Activer ERRMODE_EXCEPTION et lire avec fetch/fetchAll selon le besoin.
✗ Pièges fréquents
- Concaténer des données dans le SQL, même « juste pour tester » (injection critique).
- Créer une connexion PDO dans chaque fichier (duplication, identifiants dispersés).
- Utiliser les fonctions mysql_* obsolètes au lieu de PDO.
Points clés à retenir
- Une application réelle STOCKE ses données dans une BASE. MySQL/MariaDB (relationnelle : tables, lignes) interrogée en SQL (SELECT/INSERT/UPDATE/DELETE = CRUD).
- PDO = l'interface MODERNE et sûre pour se connecter et exécuter des requêtes (multi-bases, exceptions, marqueurs nommés).
- CENTRAL : les REQUÊTES PRÉPARÉES. Concaténer des données = faille CRITIQUE d'INJECTION SQL. Marqueurs (:nom / ?) + données dans execute().
- RÈGLE ABSOLUE : toute requête avec données variables DOIT être préparée ; fetch/fetchAll pour lire — compétence centrale du PHP avec base.
Défi 48h — appliquez demain
Branchez 1 PDO avec exceptions et faites un CRUD INSERT + SELECT préparés testé avec une entrée piégée.
- Je distingue prepare() (code) de execute() (données).
- Mon CRUD est entièrement préparé, sans concaténation.
- Ma connexion est centralisée, erreurs en exceptions.
Questions fréquentes
Qu'est-ce que l'injection SQL, et pourquoi les requêtes préparées sont-elles la solution ?
L'injection SQL est une attaque où des données envoyées par l'attaquant (champ, paramètre d'URL) se retrouvent exécutées comme du SQL parce que vous les avez concaténées dans la requête : contournement de connexion, lecture ou destruction de tables. La cause : mélanger code et données dans une même chaîne. La requête préparée sépare les deux : prepare() envoie le code avec des marqueurs, execute() envoie les données à part — la base les traite comme données pures, même piégées. C'est LA protection, à appliquer toujours, sans exception.
Pourquoi utiliser PDO, et comment structurer l'accès à la base ?
PDO est l'approche moderne : abstraction multi-bases (même API pour MySQL, PostgreSQL…), requêtes préparées élégantes à marqueurs nommés, API objet cohérente, erreurs en exceptions (try/catch). Pour structurer : centralisez la connexion (un fichier config/connexion ou une classe, réutilisé partout), identifiants hors du dépôt (environnement), et réutilisez le même PDO dans tout le code — fini la connexion recréée par page.
Que sépare une requête préparée PDO ?