Intermédiaire50 min35 XP
Objectifs de cette leçon
- Se connecter à MySQL avec PDO
- Exécuter des requêtes préparées
- Comprendre la sécurité anti-injection SQL
PHP et Bases de Données 🗄️
PHP excelle pour interagir avec les bases de données. Aujourd'hui, on utilise PDO (PHP Data Objects) — l'abstraction de database recommandée.
Pourquoi PDO ?
┌─────────────────────────────────────────┐ │ Ton script PHP │ │ ↓ │ │ PDO (couche abstraction) │ │ ┌──────┼──────┐ │ │ MySQL SQLite PostgreSQL │ │ driver driver driver │ └─────────────────────────────────────────┘
| Critère | mysqli | PDO |
|---|---|---|
| Supporté | MySQL uniquement | 12+ SGBD |
| Requêtes préparées | Oui | Oui |
| Objet-oriented | Partiellement | Oui |
| Facilité d'apprentissage | Moyen | Facile |
Connexion à MySQL
<?php // db.php — Connexion PDO $host = "localhost"; $dbname = "ma_base"; $username = "root"; $password = ""; try { $pdo = new PDO( "mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false ] ); } catch (PDOException $e) { die("Erreur de connexion : " . $e->getMessage()); } ?>
💡 Astuce :
ERRMODE_EXCEPTIONlance une exception en cas d'erreur — c'est indispensable pour le debug.
Requêtes préparées (INSERT)
<?php // Ajouter un utilisateur $stmt = $pdo->prepare(" INSERT INTO users (nom, email, mdp_hash, created_at) VALUES (:nom, :email, :mdp, NOW()) "); $stmt->execute([ ':nom' => "Alice Dupont", ':email' => "alice@example.com", ':mdp' => password_hash("monMdp123", PASSWORD_DEFAULT) ]); echo "Utilisateur créé avec l'ID : " . $pdo->lastInsertId(); ?>
⚠️ Sécurité : Les requêtes préparées empêchent les injections SQL. Ne JAMAIS insérer les données directement dans la requête.
Requêtes préparées (SELECT)
<?php // Récupérer un seul utilisateur $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email"); $stmt->execute([':email' => 'alice@example.com']); $user = $stmt->fetch(); if ($user) { echo $user['nom']; // Alice Dupont echo $user['email']; // alice@example.com } // Récupérer plusieurs résultats $stmt = $pdo->query("SELECT * FROM users ORDER BY created_at DESC"); $users = $stmt->fetchAll(); foreach ($users as $u) { echo $u['nom'] . "\n"; } // Compter les résultats $count = $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn(); echo "Total : $count utilisateurs"; ?>
Update et Delete
<?php // Mettre à jour $stmt = $pdo->prepare(" UPDATE users SET nom = :nom, email = :email WHERE id = :id "); $stmt->execute([ ':nom' => "Alice Martin", ':email' => "alice.martin@example.com", ':id' => 1 ]); echo $stmt->rowCount() . " ligne(s) modifiée(s)"; // Supprimer $stmt = $pdo->prepare("DELETE FROM users WHERE id = :id"); $stmt->execute([':id' => 99]); echo $stmt->rowCount() . " ligne(s) supprimée(s)"; ?>
Transactions
<?php try { $pdo->beginTransaction(); // Transfert bancaire : débiter A, créditer B $stmt = $pdo->prepare("UPDATE comptes SET solde = solde - :montant WHERE id = :id"); $stmt->execute([':montant' => 100, ':id' => 1]); $stmt = $pdo->prepare("UPDATE comptes SET solde = solde + :montant WHERE id = :id"); $stmt->execute([':montant' => 100, ':id' => 2]); $pdo->commit(); echo "Transfert réussi !"; } catch (Exception $e) { $pdo->rollBack(); echo "Transfert annulé : " . $e->getMessage(); } ?>
Modèle de données complet
<?php class UserModel { private PDO $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function findById(int $id): ?array { $stmt = $this->pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([$id]); return $stmt->fetch() ?: null; } public function findByEmail(string $email): ?array { $stmt = $this->pdo->prepare("SELECT * FROM users WHERE email = ?"); $stmt->execute([$email]); return $stmt->fetch() ?: null; } public function create(string $nom, string $email, string $mdp): int { $stmt = $this->pdo->prepare(" INSERT INTO users (nom, email, mdp_hash, created_at) VALUES (?, ?, ?, NOW()) "); $stmt->execute([$nom, $email, password_hash($mdp, PASSWORD_DEFAULT)]); return (int)$this->pdo->lastInsertId(); } public function update(int $id, array $data): bool { $champs = implode(', ', array_map(fn($k) => "$k = ?", array_keys($data))); $stmt = $this->pdo->prepare("UPDATE users SET $champs WHERE id = ?"); return $stmt->execute([...array_values($data), $id]); } public function delete(int $id): bool { $stmt = $this->pdo->prepare("DELETE FROM users WHERE id = ?"); return $stmt->execute([$id]); } public function list(int $page = 1, int $parPage = 20): array { $offset = ($page - 1) * $parPage; $stmt = $this->pdo->prepare(" SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ? "); $stmt->execute([$parPage, $offset]); return $stmt->fetchAll(); } } // Utilisation $userModel = new UserModel($pdo); $nouvelId = $userModel->create("Bob", "bob@example.com", "secret123"); $bob = $userModel->findById($nouvelId); ?>
Migration de la base de données
<?php // migration.php — Créer les tables $pdo->exec("CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, nom VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, mdp_hash VARCHAR(255) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP )"); $pdo->exec("CREATE TABLE IF NOT EXISTS articles ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, titre VARCHAR(255) NOT NULL, contenu TEXT, slug VARCHAR(255) UNIQUE, published BOOLEAN DEFAULT FALSE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE )"); ?>
Comparaison avec JavaScript/Node.js
| Opération | Node.js (mysql2) | PHP (PDO) |
|---|---|---|
| Connexion | createPool() | new PDO() |
| Requête | pool.query() | $pdo->query() |
| Prepared | pool.execute() | $pdo->prepare() + execute() |
| Transaction | conn.beginTransaction() | $pdo->beginTransaction() |
🎯 A retenir : Toujours utiliser PDO avec des requêtes préparées. Ne jamais faire confiance aux entrées utilisateur. Valide, assainis, puis exécute.
Prochaine leçon : Projet final PHP — construis une application complète !
⚡ Simulateur — BDD avec PDO
Étape 1/7📝 Pseudo-code
1<?php
2$pdo = new PDO("mysql:host=localhost;dbname=app", "root", "");
3$stmt = $pdo->query("SELECT * FROM users");
4while ($user = $stmt->fetch()) {
5 echo $user["nom"];
6}
7?>
⚙️ Exécution
📦 Mémoire
Aucune variable
🔀 Flux d'exécution
<?php
3
Début du script
Ouverture du bloc PHP.