Chapitre 4 PDO 2022

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 28

Chapitre 4

PHP Data Objects (PDO)

L'extension PHP Data Objects (PDO) définit une excellente interface pour
accéder à une base de données depuis PHP. Chaque pilote de base de données
implémenté dans l'interface PDO peut utiliser des fonctionnalités spécifiques de
chacune des bases de données en utilisant des extensions de fonctions. Notez que
vous ne pouvez exécuter aucune fonction de base de données en utilisant
l'extension PDO par elle-même ; vous devez utiliser un driver PDO spécifique à la
base de données pour accéder au serveur de base de données.

PDO fournit une interface d'abstraction à l'accès de données, ce qui signifie que

vous utilisez les mêmes fonctions pour exécuter des requêtes ou récupérer les

données quel que soit la base de données utilisée. PDO ne fournit pas une

abstraction de base de données : il ne réécrit pas le SQL, n'émule pas des

fonctionnalités manquantes. Vous devriez utiliser une interface d'abstraction

complète si vous avez besoin de cela.

PDO est fourni avec PHP 5.1 et est disponible en tant qu'extension PECL pour

PHP 5.0 ; PDO requiert les nouvelles fonctionnalités OO fournies par PHP 5 et

donc, ne fonctionne pas avec les versions antérieures de PHP.

Page 1
Se connecter à la base de données en PHP

Pour pouvoir travailler avec la base de données en PHP, il faut d'abord s'y
connecter.

Nous allons apprendre dans ce chapitre à lire des données dans une BDD (base
de données). Or, je vous rappelle que PHP doit faire l'intermédiaire entre vous et
MySQL.

Problème : PHP ne peut pas dire à MySQL dès le début « Récupère-moi ces
valeurs ». En effet, MySQL demande d'abord un nom d'utilisateur et un mot de
passe.

Il va donc falloir que PHP s'authentifie : on dit qu'il établit une connexion avec
MySQL. Une fois que la connexion sera établie, vous pourrez faire toutes les
opérations que vous voudrez sur votre base de données !

1. Connexion à la base de données en PHP

PHP propose plusieurs moyens de se connecter à une base de données MySQL.

 L'extension mysql_ : ce sont des fonctions qui permettent d'accéder à une


base de données MySQL et donc de communiquer avec MySQL.
 Leur nom commence toujours par mysql_. Toutefois, ces fonctions sont
vieilles et on recommande de ne plus les utiliser aujourd'hui.
 L'extension mysqli_ : ce sont des fonctions améliorées d'accès à MySQL.
Elles proposent plus de fonctionnalités et sont plus à jour.
 L'extension PDO : c'est un outil complet qui permet d'accéder à n'importe
quel type de base de données. On peut donc l'utiliser pour se connecter
aussi bien à MySQL que PostgreSQL ou Oracle.

Il reste à choisir entre mysqli_ et PDO.

Page 2
Nous allons ici utiliser PDO car c'est cette méthode d'accès aux bases de données
qui va devenir la plus utilisée dans les prochaines versions de PHP.

D'autre part, le gros avantage de PDO est que vous pouvez l'utiliser de la même
manière pour vous connecter à n'importe quel autre type de base de données
(PostgreSQL, Oracle…) (figure suivante).

PDO permet de se connecter à n'importe quel type de base de données

Vous pourrez donc réutiliser ce que vous allez apprendre si vous choisissez
d'utiliser une autre base de données que MySQL.

2. Se connecter à MySQL avec PDO

Pour se connecter à MySQL. Nous allons avoir besoin de quatre renseignements :

 le nom de l'hôte : c'est l'adresse de l'ordinateur où MySQL est installé


(comme une adresse IP). Le plus souvent, MySQL est installé sur le même
ordinateur que PHP : dans ce cas, mettez la valeur localhost (cela signifie
« sur le même ordinateur »).
 la base : c'est le nom de la base de données à laquelle vous voulez vous
connecter. Dans notre cas, la base s'appelle test . Nous l'avons créée avec
phpMyAdmin.
 le login : il permet de vous identifier dans notre cas root.
 le mot de passe :
 dans note cas chaine vide

Page 3
Pour l'instant, nous faisons des tests sur notre ordinateur à la maison. On
dit qu'on travaille « en local ». Par conséquent, le nom de l'hôte sera
localhost.

Quant au login et au mot de passe, par défaut le login est root et il n'y a
pas de mot de passe.

<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
?>

Voici donc comment on doit faire pour se connecter à MySQL via PDO sur la base
test :

<?php
// Sous WAMP (Windows)
$bdd = new PDO(' mysql : host = localhost ; dbname = test ; charset=utf8', 'root', ' ');
?>

La ligne de code qu'on vient de voir crée ce qu'on appelle un objet $bdd.

c'est un objet qui représente la connexion à la base de données. On crée la


connexion en indiquant dans l'ordre dans les paramètres :

 le nom d'hôte (localhost) ;


 la base de données (test) ;
 le login (root) ;
 le mot de passe (sous WAMP il n'y a pas de mot de passe, j'ai donc mis une
chaîne vide,).

Le premier paramètre (qui commence par mysql) s'appelle le DSN : Data Source
Name. C'est généralement le seul qui change en fonction du type de base de
données auquel on se connecte.

Page 4
3. Tester la présence d'erreurs

Si vous avez renseigné les bonnes informations (nom de l'hôte, de la base, le login
et le mot de passe), rien ne devrait s'afficher à l'écran.

Toutefois, s'il y a une erreur (vous vous êtes trompés de mot de passe ou de nom
de base de données, par exemple), PHP risque d'afficher toute la ligne qui pose
l'erreur, ce qui inclut le mot de passe !

Vous ne voudrez pas que vos visiteurs puissent voir le mot de passe si une erreur
survient lorsque votre site est en ligne. Il est préférable de traiter l'erreur.

En cas d'erreur, PDO renvoie ce qu'on appelle une exception qui permet de
« capturer » l'erreur. Voici comment je vous propose de faire :

<?php
try
{$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
}
catch (Exception $e)
{
die('Erreur : ' . $e->getMessage());
}
?>

<?php
try {
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
foreach($dbh->query('SELECT * from FOO') as $row) {
print_r($row);
}
$dbh = null;
} catch (PDOException $e) {
print "Erreur !: " . $e->getMessage() . "<br/>";
die();
}
?>

Page 5
il faut savoir que PHP essaie d'exécuter les instructions à l'intérieur du bloc try.
S'il y a une erreur, il rentre dans le bloc catch et fait ce qu'on lui demande (ici, on
arrête l'exécution de la page en affichant un message décrivant l'erreur).

Si au contraire tout se passe bien, PHP poursuit l'exécution du code et ne lit pas
ce qu'il y a dans le bloc catch.

4. Fermeture d'une connexion

Lorsque la connexion à la base de données a réussi, une instance de la classe


PDO est retournée à votre script. La connexion est active tant que l'objet PDO
l'est. Pour clore la connexion, vous devez détruire l'objet en vous assurant que
toutes ses références sont effacées. Vous pouvez faire cela en assignant NULL
à la variable gérant l'objet. Si vous ne le faites pas explicitement, PHP
fermera automatiquement la connexion lorsque le script arrivera à la fin.

<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
// utiliser la connexion ici
$sth = $dbh->query('SELECT * FROM foo');

// et maintenant, fermez-la !
$sth = null;
$dbh = null;
?>

5. Récupérer les données

Dans un premier temps, nous allons apprendre à lire des informations dans la
base de données, puis nous verrons comment ajouter et modifier des données.

Dans la base de données test créer la table jeux_video : jeux_video.sql

La table jeux_video apparaît maintenant dans phpMyAdmin

Page 6
Voici les cinq premières entrées qu'elle contient (il y en a une cinquantaine en tout !) :

ID nom possesseur console prix nbre_joueurs_max commentaires


Super
1 Mario Florent NES 4 1 Un jeu d'anthologie !
Bros
Pour moi, le
2 Sonic Patrick Megadrive 2 1 meilleur jeu au
monde !
Un jeu grand, beau
Zelda :
Nintendo et complet comme
3 ocarina Florent 15 1
64 on en voit rarement
of time
de nos jours
Mario Nintendo Un excellent jeu de
4 Florent 25 4
Kart 64 64 kart !
Super
Smash Un jeu de baston
5 Michel GameCube 55 4
Bros délirant !
Melee

Notre objectif est de créer une page PHP qui va afficher ce que contient la
table jeux_video.

6. Faire une requête

On va demander à MySQL de nous dire tout ce que contient la table jeux_video.

Pour récupérer des informations de la base de données, nous avons besoin de


notre objet représentant la connexion à la base. il s'agit de $bdd. Nous allons
effectuer la requête comme ceci :

$reponse = $bdd->query('Tapez votre requête SQL ici');

On récupère ce que la base de données nous a renvoyé dans un autre objet que
l'on a appelé ici $reponse.

Votre première requête SQL


Page 7
Voici la première requête SQL que nous allons utiliser :

SELECT * FROM jeux_video

Effectuons la requête avec la méthode que l'on vient de découvrir :

<?php
$reponse = $bdd->query('SELECT * FROM jeux_video');
?>

$reponse contient maintenant la réponse de MySQL.

7. Afficher le résultat d'une requête

Le problème, c'est que $reponse contient quelque chose d'inexploitable. MySQL

nous renvoie beaucoup d'informations qu'il faut organiser.

Vous imaginez toutes les informations qui s'y trouvent ? Si c'est une table à 10

champs, avec 200 entrées, cela représente plus de 2 000 informations !

Pour ne pas tout traiter d'un coup, on extrait cette réponse ligne par ligne, c'est-

à-dire entrée par entrée.

Pour récupérer une entrée, on prend la réponse de MySQL et on y exécute fetch(),

ce qui nous renvoie la première ligne.

<?php
$donnees = $reponse->fetch();
?>

fetch en anglais signifie « va chercher ».

$donnees est un array qui contient champ par champ les valeurs de la première

entrée.

Page 8
Par exemple, si vous vous intéressez au champ console, vous utiliserez

l'array $donnees['console'].

Il faut faire une boucle pour parcourir les entrées une à une. Chaque fois que

vous appelez $reponse->fetch(), vous passez à l'entrée suivante. La boucle est

donc répétée autant de fois qu'il y a d'entrées dans votre table.

En cas d’erreur

<?php

try

{// On se connecte à MySQL

$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');

catch(Exception $e)

// En cas d'erreur, on affiche un message et on arrête tout

die('Erreur : ' . $e->getMessage());

// Si tout va bien, On récupère tout le contenu de la table jeux_video

$reponse = $bdd->query('SELECT * FROM jeux_video');

// On affiche chaque entrée une à une

while ($donnees = $reponse->fetch())

{ ?> <p>

<strong>Jeu</strong> : <?php echo $donnees['nom']; ?> <br />

Page 9
Le possesseur de ce jeu est : <?php echo $donnees['possesseur']; ?>, et il le vend

à <?php echo $donnees['prix']; ?> euros !<br />

Ce jeu fonctionne sur <?php echo $donnees['console']; ?> et on peut y jouer à

<?php echo $donnees['nbre_joueurs_max']; ?> au maximum<br />

<?php echo $donnees['possesseur']; ?> a laissé ces commentaires sur <?php

echo $donnees['nom']; ?> : <em><?php echo $donnees['commentaires']; ?>

Exemple.php (base de donnée jeu table jeux_video)

<?php

try

{// On se connecte à MySQL

$bdd = new PDO('mysql:host=localhost;dbname=jeu;charset=utf8', 'root', '');

catch(Exception $e)

// En cas d'erreur, on affiche un message et on arrête tout

die('Erreur : ' . $e->getMessage());

// Si tout va bien, On récupère tout le contenu de la table jeux_video

$reponse = $bdd->query('SELECT * FROM jeux_video');

// On affiche chaque entrée une à une

while ($donnees = $reponse->fetch())

{ ?> <p>

<strong>Jeu</strong> : <?php echo $donnees['nom']; ?> <br />

Page 11
Le possesseur de ce jeu est : <?php echo $donnees['possesseur']; ?>, et il le vend

à <?php echo $donnees['prix']; ?> euros !<br />

Ce jeu fonctionne sur <?php echo $donnees['console']; ?> et on peut y jouer à

<?php echo $donnees['nbre_joueurs_max']; ?> au maximum<br />

<?php echo $donnees['possesseur']; ?> a laissé ces commentaires sur <?php

echo $donnees['nom']; ?> : <em><?php echo $donnees['commentaires']; ?></em>

</p>

<?php }

?>

Exécution

Jeu : Super Mario Bros


Le possesseur de ce jeu est : Florent, et il le vend à 4 euros !
Ce jeu fonctionne sur NES et on peut y jouer à 1 au maximum
Florent a laissé ces commentaires sur Super Mario Bros : Un jeu d'anthologie !

Jeu : Sonic
Le possesseur de ce jeu est : Patrick, et il le vend à 2 euros !
Ce jeu fonctionne sur Megadrive et on peut y jouer à 1 au maximum
Patrick a laissé ces commentaires sur Sonic : Pour moi, le meilleur jeu du
monde !

Jeu : Zelda : ocarina of time


Le possesseur de ce jeu est : Florent, et il le vend à 15 euros !
Ce jeu fonctionne sur Nintendo 64 et on peut y jouer à 1 au maximum
Florent a laissé ces commentaires sur Zelda : ocarina of time : Un jeu grand,
beau et complet comme on en voit rarement de nos jours

Jeu : Mario Kart 64


Le possesseur de ce jeu est : Florent, et il le vend à 25 euros !
Ce jeu fonctionne sur Nintendo 64 et on peut y jouer à 4 au maximum
Florent a laissé ces commentaires sur Mario Kart 64 : Un excellent jeu de kart !

Jeu : Super Smash Bros Melee


Le possesseur de ce jeu est : Michel, et il le vend à 55 euros !
Ce jeu fonctionne sur GameCube et on peut y jouer à 4 au maximum

Page 11
Michel a laissé ces commentaires sur Super Smash Bros Melee : Un jeu de
baston délirant !

Jeu : Dead or Alive


Le possesseur de ce jeu est : Patrick, et il le vend à 60 euros !
Ce jeu fonctionne sur Xbox et on peut y jouer à 4 au maximum
Patrick a laissé ces commentaires sur Dead or Alive : Le plus beau jeu de
baston jamais créé

Jeu : Dead or Alive Xtreme Beach Volley Ball


Le possesseur de ce jeu est : Patrick, et il le vend à 60 euros !
Ce jeu fonctionne sur Xbox et on peut y jouer à 4 au maximum
Patrick a laissé ces commentaires sur Dead or Alive Xtreme Beach Volley Ball :
Un jeu de beach volley de toute beauté o_O

Jeu : Enter the Matrix


Le possesseur de ce jeu est : Michel, et il le vend à 45 euros !
Ce jeu fonctionne sur PC et on peut y jouer à 1 au maximum
Michel a laissé ces commentaires sur Enter the Matrix : Plutôt bof comme jeu,
mais ça complète bien le film

Etc….

Explication de l’Affichage du résultat de la requête

On fait une boucle pour chaque entrée de la table.

On commence par l'entrée n°1, puis l'entrée n°2, etc. Chaque fois qu'on fait une

nouvelle boucle, on passe en revue une autre entrée.

Quelle est la différence entre $reponse et $donnees ?

$reponse contenait toute la réponse de MySQL en vrac, sous forme d'objet.

$donnees est un array renvoyé par le fetch(). Chaque fois qu'on fait une boucle,

fetch va chercher dans $reponse l'entrée suivante et organise les champs dans

l'array $donnees.

En effet, la ligne while ($donnees = $reponse->fetch())

Page 12
cette ligne fait deux choses à la fois :

 elle récupère une nouvelle entrée et place son contenu dans $donnees ;

 elle vérifie si $donnees vaut vrai ou faux.

Le fetch renvoie faux (false) dans $donnees lorsqu'il est arrivé à la fin des

données, c'est-à-dire que toutes les entrées ont été passées en revue. Dans ce cas,

la condition du whilevaut faux et la boucle s'arrête.

8. Afficher seulement le contenu de quelques champs

<?php
try
{
$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
}
catch(Exception $e)
{
die('Erreur : '. $e->getMessage());
}

$reponse = $bdd->query('SELECT nom FROM jeux_video');

while ($donnees = $reponse->fetch())


{
echo $donnees['nom'] . '<br />';
}

$reponse->closeCursor();

?>

Page 13
9. Les critères de sélection

Imaginons que je souhaite obtenir uniquement la liste des jeux disponibles de la


console « Nintendo 64 » et les trier par prix croissants.

Vous allez voir qu'en modifiant nos requêtes SQL, il est possible de filtrer et trier
très facilement vos données. Nous allons nous intéresser ici aux mots-clés
suivants du langage SQL :

 WHERE ;
 ORDER BY ;
 LIMIT.

WHERE

Grâce au mot-clé WHERE, vous allez pouvoir trier vos données.

Supposons par exemple que je veuille lister uniquement les jeux appartenant à
Patrick.

La requête au début sera la même qu'avant, mais je rajouterai à la fin


WHERE possesseur='Patrick'.
Cela nous donne la requête :

SELECT * FROM jeux_video WHERE possesseur='Patrick'

Traduction : « Sélectionner tous les champs de la table jeux_video lorsque le


champ possesseur est égal à Patrick ».

<?php
try
{
$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
}
catch(Exception $e)
{
die('Erreur : '.$e->getMessage());
}

Page 14
$reponse = $bdd->query('SELECT nom, possesseur FROM jeux_video WHERE
possesseur=\'Patrick\'');

while ($donnees = $reponse->fetch())


{
echo $donnees['nom'] . ' appartient à ' . $donnees['possesseur'] . '<br />';
}

?>

Si vous vous amusez à changer le nom du possesseur


(WHERE possesseur='Michel'par exemple ), ça n'affichera que les jeux
appartenant à Michel. Essayez, vous verrez !

Il est par ailleurs possible de combiner plusieurs conditions. Par exemple, si je


veux lister les jeux de Patrick qu'il vend à moins de 20 euros, je combinerai les
critères de sélection à l'aide du mot-cléAN AND (qui signifie « et ») :

SELECT * FROM jeux_video WHERE possesseur='Patrick' AND prix < 20

Traduction : « Sélectionner tous les champs de jeux_video lorsque le possesseur


est Patrick ET lorsque le prix est inférieur à 20 ».

Il existe aussi le mot-clé OR qui signifie « ou ».

ORDER BY

ORDER BY nous permet d'ordonner nos résultats. Nous pourrions ainsi classer
les résultats en fonction de leur prix ! La requête SQL serait :

SELECT * FROM jeux_video ORDER BY prix

Traduction : « Sélectionner tous les champs de jeux_video et ordonner les


résultats par prix croissants ».

Page 15
Application :

<?php
try
{
$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
}
catch(Exception $e)
{
die('Erreur : '.$e->getMessage());
}

$reponse = $bdd->query('SELECT nom, prix FROM jeux_video ORDER BY prix');

while ($donnees = $reponse->fetch())


{
echo $donnees['nom'] . ' coûte ' . $donnees['prix'] . ' EUR<br />';
}

$reponse->closeCursor();

?>

10. classer par ordre décroissant

Facile. Il suffit de rajouter le mot-clé DESC à la fin :

SELECT * FROM jeux_video ORDER BY prix DESC

Traduction : « Sélectionner tous les champs de jeux_video, et ordonner les


résultats par prix décroissants ».

À noter : si on avait utilisé ORDER BY sur un champ contenant du texte, le


classement aurait été fait par ordre alphabétique.

LIMIT

LIMIT nous permet de ne sélectionner qu'une partie des résultats (par exemple
les 20 premiers). C'est très utile lorsqu'il y a beaucoup de résultats et que vous
souhaitez les paginer (c'est-à-dire par exemple afficher les 30 premiers résultats
sur la page 1, les 30 suivants sur la page 2, etc).

Page 16
À la fin de la requête, il faut ajouter le mot-cléLIMITsuivi de deux nombres
séparés par une virgule. Par exemple :

SELECT * FROM jeux_video LIMIT 0, 20

 LIMIT 0, 20 : affiche les vingt premières entrées ;


 LIMIT 5, 10 : affiche de la sixième à la quinzième entrée ;
 LIMIT 10, 2 : affiche la onzième et la douzième entrée.

Un petit schéma se trouve à la figure suivante pour résumer le fonctionnement


deLIMIT.

Afficher les 10 premiers jeux de la table, on utilisera le code suivant :

<?php
try
{
$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
}
catch(Exception $e)
{
die('Erreur : '.$e->getMessage());
}

$reponse = $bdd->query('SELECT nom FROM jeux_video LIMIT 0, 10');

echo '<p>Voici les 10 premières entrées de la table jeux_video :</p>';


while ($donnees = $reponse->fetch())
{
echo $donnees['nom'] . '<br />';
}

$reponse->closeCursor();

?>

Construire des requêtes en fonction de variables

Au lieu de toujours afficher les jeux de Patrick, on aimerait que cette requête soit

capable de s'adapter au nom de la personne défini dans une variable, par

exemple$_GET['possesseur']. Ainsi la requête pourrait s'adapter en fonction de la

demande de l'utilisateur !

Page 17
Nous pourrions être tentés de concaténer la variable dans la requête, comme

ceci :

<?php
$reponse = $bdd->query('SELECT nom FROM jeux_video WHERE possesseur=\''
. $_GET['possesseur'] . '\'');
?>

Il est nécessaire d'entourer la chaîne de caractères d'où la présence des antislashs


pour insérer les apostrophes :\'

Bien que ce code fonctionne, c'est l'illustration parfaite de ce qu'il ne faut pas
faire et que pourtant beaucoup de sites font encore. En effet, si la
variable$_GET['possesseur'] a été modifiée par un visiteur (et nous savons à quel
point il ne faut pas faire confiance à l'utilisateur !), il y a un gros risque de faille
de sécurité qu'on appelle injection SQL. Un visiteur pourrait s'amuser à insérer
une requête SQL au milieu de la vôtre et potentiellement lire tout le contenu de
votre base de données, comme par exemple la liste des mots de passe de vos
utilisateurs.

Nous allons utiliser un autre moyen plus sûr d'adapter nos requêtes en

fonction de variables : les requêtes préparées.

11. La solution : les requêtes préparées

Classe PDO

PDO {
public __construct ( string $dsn [, string $username [, string $passwd [, array
$options ]]] )
public beginTransaction ( void ) : bool
public commit ( void ) : bool

Page 18
public errorCode ( void ) : string
public errorInfo ( void ) : array
public exec ( string $statement ) : int
public getAttribute ( int $attribute ) : mixed
public static getAvailableDrivers ( void ) : array
public inTransaction ( void ) : bool
public lastInsertId ([ string $name = NULL ] ) : string
public prepare ( string $statement [, array $driver_options = array() ] ) :
PDOStatement
public query ( string $statement ) : PDOStatement
public quote ( string $string [, int $parameter_type = PDO::PARAM_STR ] ) :
string
public rollBack ( void ) : bool
public setAttribute ( int $attribute , mixed $value ) : bool
}

PDO::prepare — Prépare une requête à l'exécution et retourne un objet

PDO::query — Exécute une requête SQL, retourne un jeu de résultats en tant


qu'objet PDOStatement

PDO::setAttribute — Configure un attribut PDO

PDO::prepare

public PDOStatement PDO::prepare ( string $statement [, array $driver_options


= array() ] )

Prépare une requête SQL à être exécutée par la méthode


PDOStatement::execute(). La requête SQL peut contenir zéro ou plusieurs noms
(:nom) ou marqueurs (?) pour lesquels les valeurs réelles seront substituées
lorsque la requête sera exécutée. Vous ne pouvez pas utiliser les marqueurs
nommés et les marqueurs interrogatifs dans une même requête SQL ; choisissez

Page 19
l'un ou l'autre. Utilisez ces paramètres pour lier les entrées utilisateurs, ne les
incluez pas directement dans la requête.

Vous devez inclure un marqueur avec un nom unique pour chaque valeur que
vous souhaitez passer dans la requête lorsque vous appelez
PDOStatement::execute(). Vous ne pouvez pas utiliser un marqueur avec deux
noms identiques dans une requête préparée, à moins que le mode émulation ne
soit actif.

Le système de requêtes préparées a l'avantage d'être beaucoup plus sûr mais


aussi plus rapide pour la base de données si la requête est exécutée plusieurs fois.

Avec des marqueurs « ? »

Dans un premier temps, on va « préparer » la requête sans sa partie variable, que


l'on représentera avec un marqueur sous forme de point d'interrogation :

<?php
$req = $bdd->prepare('SELECT nom FROM jeux_video WHERE possesseur = ?');
?>

Au lieu d'exécuter la requête avec query() comme la dernière fois, on appelle ici
prepare().

La requête est alors prête, sans sa partie variable. Maintenant, nous allons
exécuter la requête en appelant execute et en lui transmettant la liste des
paramètres :

<?php
$req = $bdd->prepare('SELECT nom FROM jeux_video WHERE possesseur = ?');
$req->execute(array($_GET['possesseur']));
?>

La requête est alors exécutée à l'aide des paramètres que l'on a indiqués sous
forme d'array.

S'il y a plusieurs marqueurs, il faut indiquer les paramètres dans le bon ordre :

<?php
$req = $bdd->prepare('SELECT nom FROM jeux_video WHERE possesseur = ?
AND prix <= ?');

Page 21
$req->execute(array($_GET['possesseur'], $_GET['prix_max']));
?>

Le premier point d'interrogation de la requête sera remplacé par le contenu de la


variable$_GET['possesseur'], et le second par le contenu de$_GET['prix_max']. Le
contenu de ces variables aura été automatiquement sécurisé pour prévenir les
risques d'injection SQL.

Construire une page capable de lister les jeux appartenant à une personne et
dont le prix ne dépasse pas une certaine somme :

<?php
try
{
$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
}
catch(Exception $e)
{
die('Erreur : '.$e->getMessage());
}

$req = $bdd->prepare('SELECT nom, prix FROM jeux_video WHERE possesseur


= ? AND prix <= ? ORDER BY prix');
$req->execute(array($_GET['possesseur'], $_GET['prix_max']));

echo '<ul>';
while ($donnees = $req->fetch())
{
echo '<li>' . $donnees['nom'] . ' (' . $donnees['prix'] . ' EUR)</li>';
}
echo '</ul>';

$req->closeCursor();

?>

Bien que la requête soit « sécurisée » (ce qui élimine les risques d'injection SQL),
il faudrait quand même vérifier que $_GET['prix_max'] contient bien un nombre
et qu'il est compris dans un intervalle correct. Vous n'êtes donc pas dispensés
d'effectuer des vérifications supplémentaires si vous estimez que cela est
nécessaire.

Page 21
Essayez d'appeler cette page (que l'on nommera par exemple selection_jeux.php)
en modifiant les valeurs des paramètres. Vous allez voir que la liste des jeux qui
ressort change en fonction des paramètres envoyés !

12. Avec des marqueurs nominatifs

Si la requête contient beaucoup de parties variables, il peut être plus pratique de


nommer les marqueurs plutôt que d'utiliser des points d'interrogation.

Voici comment on s'y prendrait :

<?php

$req = $bdd->prepare('SELECT nom, prix FROM jeux_video WHERE possesseur

= :possesseur AND prix <=: prixmax');

$req->execute(array('possesseur'=> $_GET['possesseur'], 'prixmax' =>

$_GET['prix_max']));

?>

Les points d'interrogation ont été remplacés par les marqueurs nominatifs
:possesseur et : prixmax.

Cette fois-ci, ces marqueurs sont remplacés par les variables à l'aide d'un array
associatif. Quand il y a beaucoup de paramètres, cela permet parfois d'avoir plus
de clarté. De plus, contrairement aux points d'interrogation, nous ne sommes
cette fois plus obligés d'envoyer les variables dans le même ordre que la requête.

13. Traquer les erreurs

Lorsqu'une requête SQL « plante », bien souvent PHP vous dira qu'il y a eu une
erreur à la ligne du fetch :

Fatal error: Call to a member function fetch() on a non-


object in C:\wamp\www\tests\index.php on line 13

Ce n'est pas très précis comme réponse.

Page 22
Ce n'est pas la ligne du fetch qui est en cause : c'est souvent vous qui avez mal
écrit votre requête SQL quelques lignes plus haut.

Pour afficher des détails sur l'erreur, il faut activer les erreurs lors de la
connexion à la base de données via PDO.

soit la ligne ?

<?php
$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
?>

Ajoutez-y un paramètre à la fin pour activer les erreurs :

<?php
$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '',
array(PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION));
?>

Désormais, toutes vos requêtes SQL qui comportent des erreurs les afficheront
avec un message beaucoup plus clair.

Supposons par exemple que j'écrive mal le nom du champ :

<?php
$reponse = $bdd->query('SELECT champinconnu FROM jeux_video');
?>

L'erreur suivante s'affichera alors :

Unknown column 'champinconnu' in 'field list'

14. Écrire des données

Nous avons vu que l'on pouvait facilement récupérer des informations de notre
base de données. Nous avons également pu constater que le langage SQL est très
puissant car il propose de nombreux critères de sélection et de tri (WHERE,
ORDER BY, etc.).

on peut ajouter et modifier des données dans la base :INSERT, UPDATE et


DELETE.

Page 23
a. INSERT : ajouter des données

La requête INSERT INTO permet d'ajouter une entrée

Pour rajouter une entrée, vous aurez besoin de connaître la requête SQL. En voici
une par exemple qui ajoute un jeu :

INSERT INTO jeux_video(ID, nom, possesseur, console, prix, nbre_joueurs_max,


commentaires) VALUES('', 'Battlefield 1942', 'Patrick', 'PC', 45, 50, '2nde guerre
mondiale')

Les nombres (tels que 45 et 50 ici) n'ont pas besoin d'être entourés d'apostrophes.
Seules les chaînes de caractères les nécessitent.

Vous remarquerez que pour le premier champ (ID), j'ai laissé des apostrophes
vides. C'est voulu : le champ a la propriété auto_increment, MySQL mettra donc
le numéro d'ID lui-même. On pourrait même se passer du champ ID dans la
requête :

INSERT INTO jeux_video(nom, possesseur, console, prix, nbre_joueurs_max,


commentaires) VALUES('Battlefield 1942', 'Patrick', 'PC', 45, 50, '2nde guerre
mondiale')

Enfin, si vous le désirez, sachez que vous n'êtes pas obligés de lister les noms des
champs en premier ; cette requête marche tout aussi bien (mais elle est moins
claire) :

INSERT INTO jeux_video VALUES('', 'Battlefield 1942', 'Patrick', 'PC', 45, 50,
'2nde guerre mondiale')

Il faut lister les valeurs pour tous les champs sans exception (ID compris) dans le
bon ordre.

Application en PHP

Utilisons cette requête SQL au sein d'un script PHP.

Page 24
<?php
try
{
$bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
}
catch(Exception $e)
{
die('Erreur : '.$e->getMessage());
}

// On ajoute une entrée dans la table jeux_video


$bdd->exec('INSERT INTO jeux_video(nom, possesseur, console, prix,
nbre_joueurs_max, commentaires) VALUES(\'Battlefield 1942\', \'Patrick\',
\'PC\', 45, 50, \'2nde guerre mondiale\')');

echo 'Le jeu a bien été ajouté !';


?>

ce code ajoute une entrée dans la BDD pour le jeu « Battlefield 1942 »,
appartenant à « Patrick », qui fonctionne sur « PC », qui coûte 45 euros, etc.

La présence de multiples apostrophes rend la requête un peu difficile à lire et à


écrire à cause des antislashs\ que l'on doit rajouter devant. De plus, cette requête
insère toujours les mêmes données si on veut rendre une partie de la requête
variable, le plus rapide et le plus sûr est de faire appel aux requêtes préparées.

b. Insertion de données variables grâce à une requête préparée

Si on choisit d'utiliser une requête préparée (ce que je vous recommande si vous
souhaitez insérer des variables), le fonctionnement est en fait exactement le
même que dans le chapitre précédent

il faut toujours se connecter au préalable si on veut que la requête fonctionne

<?php

Page 25
$req = $bdd->prepare('INSERT INTO jeux_video(nom, possesseur, console, prix,
nbre_joueurs_max, commentaires) VALUES(:nom, :possesseur, :console, :prix,
:nbre_joueurs_max, :commentaires)');
$req->execute(array(
'nom' => $nom,
'possesseur' => $possesseur,
'console' => $console,
'prix' => $prix,
'nbre_joueurs_max' => $nbre_joueurs_max,
'commentaires' => $commentaires
));

echo 'Le jeu a bien été ajouté !';


?>

On a utilisé ici des marqueurs nominatifs. On a créé l'array sur plusieurs lignes :
c'est autorisé, et c'est surtout bien plus lisible.

Les variables telles que $nom et $possesseur doivent avoir été définies
précédemment. Généralement, on récupèrera des variables de$_POST(issues
d'un formulaire) pour insérer une entrée dans la base de données. Nous
découvrirons un cas pratique dans le TP suivant.

c. UPDATE : modifier des données

Application en PHP

De la même manière, en PHP on fait appel à exec() pour effectuer des


modifications :

<?php
$bdd->exec('UPDATE jeux_video SET prix = 10, nbre_joueurs_max = 32 WHERE
nom = \'Battlefield 1942\'');
?>

Notez que cet appel renvoie le nombre de lignes modifiées. Essayez de récupérer
cette valeur dans une variable et de l'afficher, par exemple comme ceci :

Page 26
<?php
$nb_modifs = $bdd->exec('UPDATE jeux_video SET possesseur = \'Florent\'
WHERE possesseur = \'Michel\'');
echo $nb_modifs . ' entrées ont été modifiées !';
?>

Cela affichera quelque chose comme :13 entrées ont été modifiées !

Avec une requête préparée

<?php

$req = $bdd->prepare('UPDATE jeux_video SET prix = :nvprix,

nbre_joueurs_max = :nv_nb_joueurs WHERE nom = :nom_jeu');

$req->execute(array(

'nvprix' => $nvprix,

'nv_nb_joueurs' => $nv_nb_joueurs,

'nom_jeu' => $nom_jeu

));?>

d. DELETE : supprimer des données

Voici comment on supprime par exemple l'entrée de « Battlefield » :

DELETE FROM jeux_video WHERE nom='Battlefield 1942'

Il n'y a rien de plus facile :

 DELETE FROM : pour dire « supprimer dans » ;


 jeux_video : le nom de la table ;
 WHERE : indispensable pour indiquer quelle(s) entrée(s) doi(ven)t être
supprimée(s).

Si vous oubliez le WHERE, toutes les entrées seront supprimées. Cela équivaut à
vider la table.

Je vous laisse essayer cette requête en PHP. Vous pouvez là encore passer par
exec()si vous voulez exécuter une requête bien précise, ou bien utiliser une
requête préparée si votre requête dépend de variables.

Page 27
15. Traiter les erreurs SQL

Nous allons donc voir :

1. comment repérer une erreur SQL en PHP ;


2. comment faire parler PHP pour qu'il nous donne l'erreur SQL

Repérer l'erreur SQL en PHP

Lorsqu'il s'est produit une erreur SQL, la page affiche le plus souvent l'erreur
suivante :

Fatal error: Call to a member function fetch() on a non-object

Cette erreur survient lorsque vous voulez afficher les résultats de votre requête,
généralement dans la boucle while ($donnees = $reponse->fetch()).

Repérez la requête qui selon vous plante (certainement celle juste avant la boucle
while), et demandez d'afficher l'erreur s'il y en a une, comme ceci :

<?php
$reponse = $bdd->query('SELECT nom FROM jeux_video') or die(print_r($bdd-
>errorInfo()));

Si la requête fonctionne, aucune erreur ne sera affichée. Si en revanche la


requête plante, PHP arrêtera de générer la page et vous affichera l'erreur donnée
par MySQL.

Page 28

Vous aimerez peut-être aussi