mysql_connect
, mysql_query
…pg_connect
, pg_query
…$pdo = new PDO($dsn, $user, $pass);
driver:infos
.
driver
dépend de la BD utilisée. Exemples : mysql
ou pgsql
pgsql:host=localhost;dbname=mydatabase
mysql:host=mysql.info.unicaen.fr;port=3306;dbname=chahir_3;charset=utf8
oci:dbname=//localhost:1521/mydb
ibm:DRIVER={IBM DB2 ODBC DRIVER};DATABASE=accounts; HOSTNAME=1.2.3,4;PORT=23456;PROTOCOL=TCPIP;
Pour MySQL, les informations du DSN ont la forme de couples clef=valeur
séparés par des points-virgules. Attention à ne pas mettre d'espaces !
host
, le nom ou l'IP de la machine sur laquelle tourne le serveur
port
, le port du host sur lequel le serveur MySQL écoute (optionnel, vaut 3306 par défaut)
dbname
, le nom de la base de données à utiliser
charset
, l'encodage de caractères de la base — très important pour éviter certaines failles de sécurité (voir plus loin)
mysql.info.unicaen.fr
https://NUMETU.users.info.unicaen.fr/phpmyadmin
attention, sur phpmyadmin de sandbox et ensweb c'est localhost, mais pour les étu c'est bien mysql.info.unicaen.fr
$pdo->setAttribute($attribute, $value);
, ou
passer les options dans le constructeur avec un tableau associatifPDO::ATTR_ERRMODE
PDO::ERRMODE_SILENT
(option par défaut) : les erreurs sont signalées seulement
par le code d'erreur renvoyé par la fonction ⇒ il faut absolument vérifier le code d'erreur à chaque appel à changer absolument !
PDO::ERRMODE_WARNING
: émet un message de niveau E_WARNING en cas d'erreurPDO::ERRMODE_EXCEPTION
: renvoie une PDOException
avec code et
information sur l'erreurEn pratique : $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
fortement recommandé pour bénéficier de la gestion des exceptions
Isoler les paamètres de connection dans un fichier config.php
<?php
// Paramètres de connexion à la base de données
define('DB_HOST', 'mysql:host=mysql.info.unicaen.fr;port=3306;');
define('DB_NAME', 'dbname=...');
define('DB_USER', '...');
define('DB_PASS', '...');
?>
Et inclure votre fichier de configuration
<?php
// Fonction de connexion à la base de données
function connecter(): ?PDO
{
require_once('../../../../config.php');
// Options de connexion
$options = [
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
];
// Connexion à la base de données
try {
$dsn = DB_HOST . DB_NAME;
$connection = new PDO($dsn, DB_USER, DB_PASS, $options);
return $connection;
} catch (PDOException $e) {
echo "Connexion à MySQL impossible : ", $e->getMessage();
//exit(); // Arrêter l'exécution du script en cas d'échec de connexion
return null;
}
}
?>
Il faut encoder et échapper les valeurs de chaines de caractères
En PHP avec PDO, vous pouvez échapper les données en utilisant la méthode quote() de l'objet PDO. Cette méthode encapsule une chaîne de caractères avec des guillemets simples, tout en échappant les caractères spéciaux
En PHP avec PDO, vous pouvez échapper les données en utilisant la méthode quote() de l'objet PDO. Cette méthode encapsule une chaîne de caractères avec des guillemets simples, tout en échappant les caractères spéciaux
<?php
// Supposons que $connection soit votre objet de connexion à la base de données
// Échapper le nom
$nomEchappe = $connection->quote($nom);
?>
Il y a également la commande mysqli_real_escape_string()
qui est une fonction PHP utilisée pour échapper les caractères spéciaux dans une chaîne de caractères dans une requête SQL.
<?php
// Supposons que $connection soit votre objet de connexion à la base de données
// Échapper le nom
$nomEchappe = mysql_real_escape_string($connection, $nom);
?>
query
Elle renvoie le résultat sous forme d'objet PDOStatement
,
via lequel on peut récupérer les données sous divers formats
<?php
// Exemple d'utilisation pour une requête non préparée
function insererDonnee(PDO $connection, $nomreg, $nbhab)
{
try {
// Échapper et encoder les valeurs des chaînes de caractères
$nomreg_encode = htmlspecialchars($nomreg, ENT_QUOTES);
$nbhab_encode = htmlspecialchars($nbhab, ENT_QUOTES);
// Échapper les valeurs de chaînes de caractères
$nomregEchappe = $connection->quote($nomreg_encode);
$nbhabEchappe = $connection->quote($nbhab_encode);
// Construction de la requête d'insertion
$requete = "INSERT INTO Regions (nomreg, nbhab)
VALUES ($nomregEchappe, $nbhabEchappe)";
// Exécution de la requête d'insertion
$connection->exec($requete);
// Afficher le nombre de lignes affectées
echo 'Nombre de lignes affectées :' . $connection->rowCount();
} catch (PDOException $e) {
echo "Erreur lors de l'insertion des données : ", $e->getMessage();
}
}
// Utilisation de la fonction connecter() pour établir une connexion à la BD
if ($pdo !== null) {
try {
/* requête select */
$requete = 'SELECT nomreg, nbhab FROM Regions;';
$stmt = $pdo->query($requete);
/* récupération de la ligne courante */
$ligne = $stmt->fetch();
var_export($ligne);
/* requête insert */
$nomreg = "...";
$nbhab = "...";
insererDonnee($pdo, $nomreg, $nbhab);
} catch (PDOException $e) {
echo "Erreur : ", $e->getMessage();
}
}
?>
PDOStatement
obtenus suite à un SELECT
ont en particulier deux méthodes permettant de récupérer les données :
fetch
ne renvoie qu'une seule ligne de résultatfetchAll
renvoie un tableau avec tous les résultats attention, ne pas utiliser s'il y a potentiellement beaucoup de résultats !
fetch
:
PDO::FETCH_ASSOC
: tableau indexé avec les noms de colonnesPDO::FETCH_BOTH
(défaut) : retourne un tableau indexé par les noms de colonnes mais aussi par les numéros de colonnesPDO::FETCH_OBJ
: retourne un objet anonyme avec les propriétés qui correspondent aux noms des colonnesPDO::FETCH_INTO
: met à jour une instance existante de la classe demandéefetchAll
fetch
et fetchAll
<?php
$stmt = $pdo->query("SELECT nomreg, nbhab FROM Regions");
$tableau = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($tableau as $ligne) {
/* les champs correspondant à des indices */
echo "Nom :" . $ligne['nomreg'];
echo "Population:" . $ligne['nbhab'];
}
$stmt = $pdo->query("SELECT nomreg, nbhab FROM Regions");
/* on peut préciser le mode globalement : */
$stmt->setFetchMode(PDO::FETCH_OBJ);
/* utilisation classique de fetch() :
on récupère une ligne à la fois, tant qu'il en reste */
while (($ligne = $stmt->fetch()) !== false) {
/* les champs correspondent à des attributs */
echo "Nom :" . $ligne->nomreg;
echo "Population:" . $ligne->nbhab;
}
?>
La méthode query
n'est à utiliser que
pour faire des requêtes « constantes », c'est-à-dire avec une chaîne de caractères
littérale, sans variables
Ne pas respecter cette consigne rend votre site vulnérable aux injections SQL
demo : si on met une apostrophe dans un champ ? et que peut-on faire de plus ?
<?php
// Fonction pour supprimer une donnée dans la table Regions
function supprimerDonnee(PDO $connection, $id)
{
try {
// Échapper l'ID
$idEchappe = $connection->quote($id);
// Construction de la requête de suppression
$requete = "DELETE FROM Regions WHERE id = $idEchappe";
// Exécution de la requête de suppression
$connection->exec($requete);
// Afficher le nombre de lignes affectées
echo 'Nombre de lignes affectées :' . $connection->rowCount();
} catch (PDOException $e) {
echo "Erreur lors de la suppression des données : ", $e->getMessage();
}
}
?>
<?php
// Fonction pour mettre à jour une donnée dans la table Regions
function mettreAJourDonnee(PDO $connection, $id, $nouveauNom, $nouveauNbHab)
{
try {
// Échapper l'ID
$idEchappe = $connection->quote($id);
// Échapper et encoder les valeurs des chaînes de caractères
$NomEncode = htmlspecialchars($nouveauNom);
$NbHabEncode = htmlspecialchars($nouveauNbHab);
// Échapper les valeurs de chaînes de caractères
$nouveauNomEchappe = $connection->quote($NomEncode);
$nouveauNbHabEchappe = $connection->quote($NbHabEncode);
// Construction de la requête de mise à jour
$requete = "UPDATE Regions SET nomreg = $nouveauNomEchappe,
nbhab = $nouveauNbHabEchappe WHERE id = $idEchappe";
// Exécution de la requête de mise à jour
$connection->exec($requete);
// Afficher le nombre de lignes affectées
echo 'Nombre de lignes affectées :' . $connection->rowCount();
} catch (PDOException $e) {
echo "Erreur lors de la mise à jour des données : ", $e->getMessage();
}
}
?>
<?php
/* --- À NE PAS FAIRE --- */
$login = $_GET['login'];
$pdo->query("SELECT name FROM users WHERE login='$login'"); // très dangereux !
?>
'; DROP TABLE users; --
(le grand classique)PDO permet la préparation de requêtes par le moteur SQL
sécurité vis-à-vis des injections SQL
$rq = "UPDATE users SET name=:nom, birthday=:naissance WHERE id=:id"
PDOStatement
) :
$stmt = $pdo->prepare($rq);
$data = array(
':id' => 23456,
':nom' => 'Rachel',
':naissance' => '2023-12-04',
);
$stmt->execute($data);
Avec un SELECT
, on utilise aussi execute
<?php
/*** préparation ***/
$rq = "SELECT * FROM animals WHERE species = :espece AND name = :nom";
$stmt = $pdo->prepare($rq);
/*** remplissage des paramètres ***/
$data = array(":espece" => "humain", ":nom" => "Jean-Michel");
/*** exécution du statement ***/
$stmt->execute($data);
/*** récupération du résultat ***/
$result = $stmt->fetchAll();
?>
Attention à bien appeler execute
avant de faire
fetch
/fetchAll
!
LIMIT
en SQL attend des entiers sans guillemets :
<?php
$rq = "SELECT name FROM animals LIMIT :nb";
$stmt = $pdo->prepare($rq);
$data = array(":nb" => 10);
/* ne fonctionne pas : la requête exécutée est *
* SELECT name FROM animals LIMIT '10' */
$stmt->execute($data);
$result = $stmt->fetchAll();
bindValue
PDO::PARAM_STR
(par défaut)PDO::PARAM_INT
PDO::PARAM_BOOL
<?php
$rq = "SELECT name FROM animals LIMIT :nb";
$stmt = $pdo->prepare($rq);
$stmt->bindValue(":nb", 10, PDO::PARAM_INT);
$stmt->execute();
$result = $stmt->fetchAll();
PDO::ATTR_EMULATE_PREPARES
à false
ne pas le faire peut créer une vulnérabilité dont même les requêtes préparées ne protègent pas si elles ne sont qu'émulées.
Au passage, il n'y a qu'un seul encodage à utiliser dans MySQL : utf8mb4
(et pas utf8
, qui n'est pas le vrai UTF-8 !)
Une dernière chose : la méthode lastInsertId()
de PDO
renvoie la valeur de la clef primaire autoincrémentée de la dernière entrée ajoutée
(si le SGBD supporte cette fonction).
peut être utile pour MySQL (pour PostgreSQL, utiliser INSERT… RETURNING
semble plus simple)