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.
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".
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.
Les requêtes SQL préparées paramétrées présentent plusieurs avantages importants :
-
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.
-
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.
-
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.
-
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.
-
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.
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();
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()
.
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
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
-
Créez une nouvelle base de données
tp_game
. -
Dans la base de données
tp_game
, créez une tablefighters
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.
-
Dans votre répertoire "Documents" créez le répertoire
tp_game
-
Dans le répertoire
game
créez le fichiermain.php
-
Dans le fichier
main.php
, définissez une fonctionaddFighter
- La fonction
addFighter
devra prendre 3 paramètres :name
,strength
etdefense
. - 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.
- La fonction
-
Ajoutez un nouveau guerrier à la table
fighters
en utilisant une requête préparéeDans la fonction
addFighter
, préparez une requête SQL pour insérer un nouveau guerrier dans la tablefighters
. 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
etdefense
) aux marqueurs de position de votre requête préparée en utilisant la méthodebindParam()
.Enfin, exécutez la requête préparée pour insérer le nouveau guerrier dans la base de données.
-
Testez votre fonction
addFighter
Pour tester votre fonction
addFighter
, appelez-la avec quelques valeursprint("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
.
Si l'erreur "Driver not found" s'affiche, vous pouvez consulter le tutoriel sur Etapes à suivre pour activer une extension PHP.
Corrigé
Une solution
Vous devez être connecté pour voir le contenu.