Aller au contenu

Serveur - PHP Base de données

MySQL et POSTGRESQL sont des bases de données implémentant le langage de requête SQL. Cette partie suppose connue les principes des bases de données relationnelles.

Il existe des outils libres et gratuits développés par la communauté des programmeurs libres : phpMyAdmin et phpPGAdmin qui permettent l’administration des bases de données MySQL et POSTGRESQL avec PHP.

Attention

Les exemples de cette page utilisent une base de données MySQL, en mode procédural. Dans le cas d’une base Postgres, vérifier les fonctions correspondantes et pensez à charger le module pgsl de PHP.

Connexion#

Pour se connecter à une base depuis PHP, il faut spécifier un nom de serveur, un nom d’utilisateur, un mot de passe et un nom de base.

Les fonctions de connexion Mysqli :

  • mysqli_connect($server, $user, $password, $base) : se connecte au serveur $server en tant qu’utilisateur $user avec le mot de passe $password sur la base $base. Retourne l’identifiant de connexion si succès, FALSE sinon.
  • mysqli_close([$id]) : permet de fermer la connexion

Connexion simple

<?php
$link = mysqli_connect('localhost', 'my_user', 'my_password', 'my_db');

if (!$link) {
  die('Erreur de connexion');
} else {
  echo 'Succès... ';
}
?>

Afin de se prémunir des éventuels problèmes d’encodage entre PHP et la base de données, il est également préférable d’ajouter

<?php
mysqli_set_charset($link, "utf8");
?>

On pourra avantageusement intégrer ce code dans un fichier que l’on pourra joindre par include(). C’est aussi un moyen de sécuriser le mot de passe de connexion.

Requêtes#

Pour envoyer une requête à une base de donnée :

  • mysqli_query($link, $sql) : prends en paramètre le lien de la connexion et une chaîne de caractères qui contient la requête écrite en SQL. Retourne TRUE en cas de résultat, FALSE si échec.

Les requêtes les plus couramment utilisées sont :

  • CREATE (création d’une table)
  • SELECT (sélection)
  • INSERT (insertion)
  • UPDATE (mise à jour des données)
  • DELETE (suppression)
  • ALTER (modification d’une table)

Envoi d'une requête

<?php
$results = mysqli_query($link, "SELECT name, mail FROM users WHERE id=$id");
?>

Cet exemple sélectionne le nom et l’adresse mail de l’utilisateur qui a pour id la valeur de la variable $id.

Récupération des résultats#

Le résultat $results est de type mysql_result. Pour afficher son contenu, il suffit alors d’itérer sur ce résultat, tel un tableau associatif. Les clés du tableau étant les valeurs des champs extraits dans la requête SQL.

Dans notre cas, $result contient deux clés, $result["name"] et $result["mail"]  :

Récupération des résultats

<?php
foreach ($results as $result) {
  // $result est un tableau associatif
  echo $result["name"], $result["mail"];
}
?>
Autres méthodes de récupération des résultats

Autres méthodes de récupération des résultats#

Il existe d’autres méthodes pour récupérer les résultats d’une requête  :

  • mysqli_fetch_row($result) : retourne un tableau simple. Les clés du tableau étant des indices sans lien avec les champs de la requête SQL
  • mysqli_fetch_assoc($result) : retourne un tableau associatif. Les clés du tableau étant les valeurs des champs de la requête SQL
  • mysqli_fetch_object($result) : retourne un objet. Les attributs de l’objet correspondent aux champs de la requête SQL

Dans les 3 cas, les fonctions retournent false s’il n’y a plus aucune ligne, leur utilisation est donc basée sur une boucle while :

<?php
// solution préconisee: extraire des tableaux associatifs.
$requete = "SELECT * FROM users";
if ($result = mysqli_query($link, $requete)) {
  while ($ligne = mysqli_fetch_assoc($result)) {
    // $ligne est un tableau associatif
    // contient par exemple $ligne["id"], $ligne["nom"], etc.
  }
} else {
  echo "Erreur de requête de base de données.";
}
?>
<?php
// tableau simple
$requete = "SELECT * FROM users";
if ($result = mysqli_query($link, $requete)) {
  while ($ligne = mysqli_fetch_row($result)) {
    // $ligne est un tableau simple
    // contient par exemple $ligne[0], $ligne[1], etc.
  }
} else {
  echo "Erreur de requête de base de données.";
}
?>
<?php
// objet
$requet = "SELECT * FROM users";
if ($result = mysqli_query($link, $requete)) {
  while ($ligne = mysqli_fetch_object($result)) {
    // $ligne est un objet
    // contient par exemple $ligne->id, $ligne->nom, etc.
  }
} else {
  echo "Erreur de requête de base de données.";
}
?>

Dans le cas de requêtes préparées, la fonction est mysqli_stmt_fetch($requete), mais il faut d'abord lier des variables aux résultats avec mysqli_stmt_bind_result($requete, $var1, $var2)

Alternative à mysqli_stmt_get_result

<?php

// on lie des variables aux resultats
mysqli_stmt_bind_result($requete, $id, $nom_departement);

// on parcours et on affiche les variables
while (mysqli_stmt_fetch($requete)) {
  echo $id, $nom_departement;
}

?>

Exercice - BDD#

  • créez la connection à la base avec mysqli_connect
Données de connexion à la BDD geobase

Voici les infos pour la base de données MySQL

  • serveur : u2.ensg.eu
    • base : geobase
    • utilisateur : geo
    • mot de passe : '' (chaine de caractères vide)
    • tables (d’autres champs ne sont pas précisés ici):
      • regions (champs insee, nom)
      • departements (champs insee, region_insee, nom)
      • communes (champs insee, departement_insee, nom, surface, geometry)
  • ou export disponible

Attention, les clés étrangères sont basées sur le champ insee, qui est une chaine de caractères.

  • stockez cette connexion de manière globale dans Flight
<?php
// stocker une variable globale
Flight::set('db', $var);
// récupérer la variable
Flight::get('db');
?>
  • créez une nouvelle route /departements
  • ajoutez une requête qui sélectionne les nom et numéro insee de tous les départements français ( champs : insee, nom, de la table departements)
  • créez un tableau associatif des départements, et envoyez à la vue departements.php
  • affichez le contenu total dans un tableau HTML. Nombre de lignes égal le nombre de départements, nombre de colonnes égal le nombre de champs (double boucle)

Ensuite :

  • créez un formulaire pour lister les régions
    • un menu de sélection (<select> et <option>)
    • chaque option doit être extraite de la base (table regions, champs insee et nom)
  • lors de la validation du formulaire, récupérez l’identifiant de la region pour modifier la requête SQL et ainsi n’afficher que les départements de cette région (utilisez le champ region_insee de la table departements)
  • enfin, ajoutez l’attribut selected sur la balise <option> précédemment choisie

Voir plus bas :

  • assurez-vous que vos requêtes ne soient pas soumises aux injections SQL

Sécurité - Injection SQL#

Dans le cas où l’on exécuterait une requête basée sur des données utilisateurs (récupérées dans l’URL par exemple), notre code est exposé à une faille de type injection SQL.

Démonstration d’une faille de type injection SQL

<?php
$results = mysqli_query($link, "SELECT * FROM departements WHERE id_region=" . $_GET['id_region']);
?>

Ici on sélectionne un département dont l’id de région est l’id passé dans l’URL. On aura donc une URL de type code.php?id_region=8. Mais que se passe t’il si l’utilisateur modifie cette URL vers code.php?id=8 OR true; DROP DATABASE; ? Et bien, la requête sera exécutée ! O_o (en fonction des droits de l’utilisateur quand même)

Pour contrer cela, il faut alors utiliser le concept de requêtes préparées :

  • mysqli_prepare($link, $sql) ou chaque paramètre de la requête SQL est remplacé par un ?
  • mysqli_stmt_bind_param($requete, $types, $vars) pour lier des variables dans notre requête
    • types est une chaine de caractères qui définit le type pour chaque variables: "s" pour string, "i" pour integer, "ss" pour deux string etc.
  • mysqli_stmt_execute($requete) pour exécuter la requête
  • mysqli_stmt_get_result($requete) pour récupérer les résultats en mysqli_result

Utilisation de requêtes préparées

<?php
// requete preparee
$requete = mysqli_prepare($link, "SELECT * FROM departements WHERE id_region = ?");

// lie des donnees à la requete (dans l’ordre)
$id = $_GET['id_region'];
mysqli_stmt_bind_param($requete, "i", $id);

// execution
mysqli_stmt_execute($requete);

// recuperation des resultats
$results = mysqli_stmt_get_result($requete);
?>

Pour plus d’infos, voir la documentation officielle de la classe mysqli_stmt.

Si vous utilisez PDO et Flight, voir le dernier exemple du tutoriel de Flight.