Aller au contenu principal

Utilisation d'une BD

Utilisation d'une base de données avec PHP

Notions théoriques

Lorsque vous développez une application Web en PHP, il est fréquent que vous ayez besoin de stocker et de récupérer des informations dans une base de données.

PHP offre plusieurs façons de se connecter à une base de données et d'exécuter des requêtes SQL : https://www.php.net/manual/fr/refs.database.php

Une des meilleures pratiques pour interagir avec une base de données est d'utiliser les requêtes préparées.

Les requêtes préparées permettent de sécuriser les requêtes SQL en évitant les injections SQL, et elles permettent aussi d'optimiser les performances de la base de données en compilant la requête une seule fois, puis en l'exécutant plusieurs fois avec des paramètres différents.

remarque

Le terme officiel est "requête préparée paramétrée" mais les développeurs utilisent très souvent le terme "requête préparée".

astuce

Les requêtes préparées permettent d'éviter les injections SQL, qui sont une forme courante d'attaque contre les applications Web.

Pour en savoir plus sur les requêtes préparées avec PHP : https://www.php.net/manual/fr/pdo.prepared-statements.php

Avec une requête préparée, vous créez une requête SQL avec des "marqueurs de position" pour les valeurs que vous voulez insérer. Ensuite, vous passez les valeurs réelles à la requête préparée, qui s'occupe de les insérer en toute sécurité dans la requête.

info

Les requêtes SQL préparées paramétrées présentent plusieurs avantages importants :

  1. Sécurité : L'avantage le plus important des requêtes préparées est qu'elles peuvent aider à prévenir les attaques d'injection SQL. En séparant le code SQL des données, elles empêchent un attaquant de modifier la structure de la requête SQL.

  2. Performance : Si vous exécutez une requête SQL plusieurs fois avec différents paramètres, l'utilisation de requêtes préparées peut améliorer les performances. La requête SQL n'a besoin d'être compilée qu'une seule fois, mais peut être exécutée plusieurs fois avec différentes valeurs de paramètres.

  3. Confort : Les requêtes préparées simplifient le code en évitant la nécessité d'échapper manuellement et de formater les données pour l'inclusion dans une requête SQL.

  4. Prévention des erreurs de formatage : Comme les requêtes préparées gèrent automatiquement le formatage des données, elles aident à prévenir les erreurs qui pourraient survenir lors de l'inclusion manuelle de données dans une requête SQL. Par exemple, elles évitent les problèmes liés à l'échappement incorrect des guillemets.

  5. Intégrité des données : Les requêtes préparées garantissent que les données sont traitées comme de simples données et non comme une partie du code SQL, ce qui aide à maintenir l'intégrité des données.

astuce

Les requêtes SQL préparées paramétrées sont préconisées car elles offrent :

  • une meilleure sécurité,
  • une meilleure performance
  • et une plus grande facilité d'utilisation.

Exemple pratique

Imaginons que nous voulons insérer un nouveau guerrier dans notre base de données.

Nous pouvons utiliser une requête préparée pour le faire.

// Connexion à la base de données
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);

// Préparation de la requête
$stmt = $dbh->prepare("INSERT INTO fighters (name, strength, defense) VALUES (:name, :strength, :defense)");

//Déclaration des variables
$name = "Miss Fortune";
$strength = 100;
$defense = 50;

// Liaison des paramètres
$stmt->bindParam(':name', $name);
$stmt->bindParam(':strength', $strength);
$stmt->bindParam(':defense', $defense);

// Exécution de la requête
$stmt->execute();
astuce

Si l'erreur "Driver not found" s'affiche, vous pouvez consulter le tutoriel sur Etapes à suivre pour activer une extension PHP.

Faut-il ajouter une ligne de code pour clore la connexion ?

Dans PHP, il n'est pas strictement nécessaire de fermer explicitement la connexion à la base de données lorsque vous utilisez PDO.

L'objet PDO sera automatiquement détruit à la fin du script, ce qui ferme la connexion. Cela est dû au mécanisme de gestion des ressources de PHP, où les ressources sont libérées et les connexions fermées lorsque les objets sont détruits, ce qui se produit lorsque le script se termine ou lorsqu'un objet n'est plus référencé.

Cependant, si vous souhaitez vous assurer que la connexion est fermée immédiatement après avoir terminé avec la base de données, vous pouvez le faire en attribuant null à l'objet PDO, comme ceci :

$dbh = null;

Cela peut être utile dans des scripts à longue exécution ou lorsque vous utilisez des transactions et que vous voulez vous assurer que la connexion est fermée avant de commencer une autre transaction ou avant de faire d'autres opérations qui ne nécessitent pas de connexion à la base de données.

Dans notre exemple, la connexion sera fermée automatiquement à la fin du script, donc ajouter une ligne de code pour fermer explicitement la connexion n'est pas nécessaire, à moins que vous n'ayez des raisons spécifiques de le faire.

Dans cet exemple :

  • :name, :strength et :defense sont des marqueurs de position
  • nous utilisons la méthode bindParam() pour lier les valeurs réelles à ces marqueurs
  • et nous exécutons la requête avec la méthode execute().
remarque

A la place de bindParam(), il est possible d'utiliser la fonction bindValue().

  • bindParam() remplace le marqueur par une variable, dont la valeur peut être modifiée pour exécuter plusieurs fois une même requête préparée et avoir des résultats différents à chaque fois.
  • bindValue() remplace le marqueur par une valeur.

Voici un exemple simple pour illustrer les 2 méthodes :

$req = $pdo->prepare('SELECT * FROM users WHERE id = :id');

// Utilisation de bindParam()
$id = 1;
$req->bindParam(':id', $id, PDO::PARAM_INT);
$id = 2; // La valeur de $id est modifiée avant l'exécution
$req->execute(); // La requête utilisera la valeur 2 pour :id

// Utilisation de bindValue()
$id = 3;
$req->bindValue(':id', $id, PDO::PARAM_INT);
$id = 4; // La valeur de $id est modifiée après bindValue()
$req->execute(); // La requête utilisera la valeur 3 pour :id

Test de mémorisation/compréhension


Qu'est-ce qu'une requête préparée ?


Pourquoi utiliseriez-vous des requêtes préparées ?


Comment liez-vous une valeur à un marqueur de position dans une requête préparée ?



TP pour réfléchir et résoudre des problèmes

Dans ce TP, nous allons créer une petite partie de notre jeu de combat.

Nous allons créer une table pour stocker nos guerriers et une fonction pour ajouter un nouveau guerrier à la table.

Étapes à suivre

  1. Créez une nouvelle base de données tp_game.

  2. Dans la base de données tp_game, créez une table fighters

    Cette table devra avoir les colonnes suivantes :

    • id : un identifiant unique pour chaque guerrier. Il s'agit d'un entier qui s'auto-incrémente.
    • name : le nom du guerrier. Il s'agit d'une chaîne de caractères.
    • strength : la force du guerrier. Il s'agit d'un entier.
    • defense : la défense du guerrier. Il s'agit également d'un entier.
  3. Dans votre répertoire "Documents" créez le répertoire tp_game

  4. Dans le répertoire game créez le fichier main.php

  5. Dans le fichier main.php, définissez une fonction addFighter

    • La fonction addFighter devra prendre 3 paramètres : name, strength et defense.
    • Ajoutez la ligne : print("Tentative de connexion au serveur de BD" . "\n"); avant la connexion au serveur.
    • Ajoutez la ligne : print("La connexion a réussie" . "\n"); après la connexion au serveur.
  6. Ajoutez un nouveau guerrier à la table fighters en utilisant une requête préparée

    Dans la fonction addFighter, préparez une requête SQL pour insérer un nouveau guerrier dans la table fighters. Utilisez des marqueurs de position pour les valeurs que vous voulez insérer (:name, :strength et :defense).

    Ensuite, liez les paramètres de votre fonction (name, strength et defense) aux marqueurs de position de votre requête préparée en utilisant la méthode bindParam().

    Enfin, exécutez la requête préparée pour insérer le nouveau guerrier dans la base de données.

  7. Testez votre fonction addFighter

    Pour tester votre fonction addFighter, appelez-la avec quelques valeurs

    print("Démarrage du programme" . "\n");
    addFighter("Lara", 90, 80);
    addFighter("Mario", 95, 85);
    addFighter("Toto", 88, 82);
    print("Fin du programme" . "\n");

    et vérifiez ensuite dans votre base de données si les nouveaux guerriers ont bien été ajoutés dans la table fighters.

astuce

Si l'erreur "Driver not found" s'affiche, vous pouvez consulter le tutoriel sur Etapes à suivre pour activer une extension PHP.

Corrigé

Une solution