3.1 · MySQL & PDO : connexion et requêtes préparées

Niveau 3 · Bases de données & POO

3.1MySQL & PDO : connexion et requêtes préparées

Objectif : persister des données avec MySQL et PDO : connexion, CRUD complet avec requêtes préparées, récupération des résultats — l'étape qui transforme un site en application réelle et sûre.
Temps estimé : 13 min · lecture autonome · Si vous n'avez que 5 min : lisez « L'essentiel en 30s » + le schéma.

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.

Erreur d'interprétation fréquente : « Les requêtes préparées ne servent que pour les formulaires, et concaténer dépanne en test ». Non. Toute donnée variable (formulaire, URL, cookie, import) concaténée ouvre une injection, et le code « de test » finit toujours en production. Prepare + execute s'applique à chaque requête avec données, sans exception.
La requête préparée PDO Le code et les données voyagent séparément Connexion PDO Centralisée,ERRMODE_EXCEPTIONprepare() SQL àmarqueurs :nomexecute() Donnéespassées séparémentfetch / fetchAll Résultatslus en sûreté
Le code et les données voyagent séparément
Version texte du schéma (SEO / lecteurs d'écran / traduction) : 1) Connexion : Riad centralise son PDO avec exceptions activées, identifiants hors du code. 2) Préparation : son SELECT porte un marqueur :email, sans aucune donnée dedans. 3) Exécution : l'email saisi part dans execute(), traité comme donnée pure même s'il contient du SQL piégé. 4) Lecture : fetch() rend la ligne. Leçon : séparer code et données rend l'injection impossible.

🔎 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

  1. 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).
  2. Réalisez un CRUD avec des REQUÊTES PRÉPARÉES : INSERT (créer), SELECT (lire, fetch/fetchAll), UPDATE (modifier), DELETE (supprimer).
  3. Utilisez systématiquement des marqueurs (:nom ou ?) et passez les données via execute() — JAMAIS de concaténation de données dans le SQL.
  4. Gérez les erreurs en try/catch (ERRMODE_EXCEPTION) et vérifiez qu'une entrée piégée ne casse plus rien.
Vous persistez des données avec MySQL et PDO : connexion, CRUD complet avec REQUÊTES PRÉPARÉES (sécurité), récupération des résultats — l'étape qui transforme un site en application réelle et sûre.

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.

Checklist sortie de section
  • 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.
Auto-évaluation 1-5 : chaque requête à données variables est-elle préparée ?mes identifiants sont-ils hors du code versionné ?

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.

Vérifiez votre compréhension

Que sépare une requête préparée PDO ?

Ressources supplémentaires (optionnel, pour aller plus loin)

Examen du niveau 3Questions inédites et durcies pour valider vos acquis