Sous-solP PHPPièce 6/7Avancé

Base de données avec PDO et Composer

Se connecter à une base avec PDO, écrire des requêtes préparées contre l'injection SQL, puis organiser le projet avec Composer et son autoload.

Ce que tu vas apprendre

  • Te connecter à une base de données avec PDO et lire des lignes.
  • Écrire des requêtes préparées pour empêcher l'injection SQL.
  • Installer des bibliothèques et charger tes classes avec Composer.

PDO : un accès unique aux bases

PDO (PHP Data Objects) est l'interface standard de PHP pour parler à une base de données. Le même code fonctionne avec plusieurs systèmes ; seule l'adresse de connexion change.

Pour rester simple, ce chapitre utilise SQLite, une base stockée dans un seul fichier, sans serveur à installer. L'extension SQLite de PDO doit être activée dans ton installation de PHP.

Se connecter

db.php
<?php
declare(strict_types=1);

$pdo = new PDO("sqlite:recettes.db", null, null, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

Le premier argument est le DSN, qui indique le type de base et sa position. Les deux null sont l'utilisateur et le mot de passe, inutiles avec SQLite.

Les deux options règlent le comportement : en cas d'erreur SQL, PHP lève une exception visible au lieu de se taire, et chaque ligne est renvoyée sous forme de tableau associatif.

Créer une table

<?php
$pdo->exec("
    CREATE TABLE IF NOT EXISTS recettes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        titre TEXT NOT NULL,
        duree INTEGER NOT NULL
    )
");

exec exécute une requête fixe, sans donnée venue de l'extérieur.

L'injection SQL

Voici ce qu'il ne faut jamais écrire :

<?php
// DANGEREUX
$sql = "SELECT * FROM recettes WHERE titre = '" . $_GET["titre"] . "'";

Si le visiteur envoie ' OR '1'='1, la requête devient vraie pour toutes les lignes. Avec d'autres textes, il peut lire d'autres tables ou tout effacer. C'est une injection SQL : la donnée saisie est interprétée comme du code SQL.

Ne colle jamais une valeur venue de l'utilisateur dans une chaîne SQL. Aucun nettoyage manuel n'est aussi fiable qu'une requête préparée.

La requête préparée

Une requête préparée sépare la requête, envoyée d'abord avec des emplacements vides, des valeurs, transmises ensuite. La base ne confond plus jamais une valeur avec du SQL.

<?php
$stmt = $pdo->prepare("SELECT * FROM recettes WHERE duree <= :max");
$stmt->execute(["max" => 45]);

$rapides = $stmt->fetchAll();

:max est un emplacement nommé. execute le remplit avec la valeur du tableau. fetchAll renvoie toutes les lignes ; fetch n'en renvoie qu'une.

Ajouter une ligne

Voici l'enregistrement qui complète le formulaire du chapitre précédent :

<?php
$stmt = $pdo->prepare(
    "INSERT INTO recettes (titre, duree) VALUES (:titre, :duree)"
);
$stmt->execute(["titre" => $titre, "duree" => $dureeNombre]);

$id = $pdo->lastInsertId();

lastInsertId donne l'identifiant de la ligne qui vient d'être créée. Le résultat s'affiche ensuite avec foreach, en pensant à htmlspecialchars.

Une requête préparée protège les valeurs, pas les noms de tables ou de colonnes. Si le nom d'une colonne vient de l'utilisateur, compare-le à une liste de noms autorisés.

Composer : le gestionnaire de dépendances

Composer installe les bibliothèques PHP dont ton projet a besoin et charge tes propres classes. Il se télécharge sur getcomposer.org. Dans ton dossier de projet :

composer init
composer require monolog/monolog

La première commande crée composer.json, qui décrit le projet. La seconde télécharge une bibliothèque dans le dossier vendor/ et l'inscrit dans le fichier.

L'autoload PSR-4

Plutôt que d'écrire un require pour chaque fichier, Composer sait charger une classe à partir de son nom. On associe un espace de noms à un dossier :

composer.json
{
    "autoload": {
        "psr-4": { "App\\": "src/" }
    }
}

Avec cette règle, la classe App\Recette se trouve dans src/Recette.php. Après avoir modifié cette section, régénère l'autoloader :

composer dump-autoload

Dans ton point d'entrée, une seule ligne suffit ensuite :

index.php
<?php
require __DIR__ . "/vendor/autoload.php";

use App\Recette;

$r = new Recette("Crêpes", 30);
Mets vendor/ dans ton .gitignore. Il se reconstruit avec composer install à partir de composer.json et composer.lock.

Étapes suivantes

Tu sais maintenant afficher, recevoir et enregistrer des données de façon sûre. Pour aller plus loin, tu peux :

  1. ranger l'accès à la base dans une classe, par exemple RecetteRepository ;
  2. ajouter des mots de passe hachés avec password_hash ;
  3. découvrir un framework comme Laravel ou Symfony, qui reposent sur Composer.

À retenir

  • PDO est l'interface standard de PHP vers les bases de données ; le DSN sqlite:fichier.db désigne une base SQLite.
  • Active PDO::ERRMODE_EXCEPTION pour que les erreurs SQL soient signalées.
  • Ne construis jamais du SQL en collant une valeur utilisateur : utilise prepare puis execute.
  • fetchAll renvoie toutes les lignes, fetch une seule.
  • Composer installe les bibliothèques (composer require) dans vendor/.
  • L'autoload PSR-4 relie un espace de noms à un dossier ; require "vendor/autoload.php" suffit à tout charger.

Pour aller plus loin

Sources officielles

Ensuite

Projet
Lignes de code
Niveau
Sous-sol
Appartement
PHP
Pièce
6/7
Plan n°
php/06.pdo-et-composer
Lignes de code · fiches rédigées à partir des documentations officielles, sources citées en bas de chaque page.