Chapitre 6 : Accès aux bases de données (sans framework)
PDO : PHP Data Objects
- Caractéristiques
- Depuis PHP 5
- Orienté objet
- Interface commune à de multiples bases de données (+ de 10), via un driver spécifié dans le DSN
-- Rappels SQL
- Référence aux types de champs
- Protection des noms des objets : SELECT * FROM `nomTable`
-- Connexion
-- Exemple de connexion
<?php
$dbo = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
?>
-- DSN
Data Source Name : chaîne définissant les paramètres de connexion à une base
| BDD | DSN |
|---|---|
| Mysql | mysql:host=localhost;port=3307;dbname=testdb |
| PostgreSQL | pgsql:host=localhost;port=5432;dbname=testdb;user=bruce;password=mypass |
| Oracle | oci:dbname=localhost:1521/mydb |
-- Gestion des erreurs
<?php
try {
$dbo = new PDO('mysql:host=localhost;dbname=test',$user, $pass);
$dbo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
?>
-- Paramètres divers
-- Port par défaut du serveur
<?php
$dbo = new PDO('mysql:host=localhost;dbname=test;port=3306',$user, $pass);
?>
-- Définition de l'encodage
<?php
$dbo = new PDO('mysql:host=localhost;dbname=test;port=3306',$user, $pass);
$dbo->exec("SET CHARACTER SET utf8");
?>
ou
<?php
$dbo=new PDO("mysql:host=localhost;dbname=test;charset=UTF8");
?>
-- Interrogation de données
PDO::query retourne un PDOStatement (jeu d'enregistrements)
qu'il est ensuite possible de parcourir avec un foreach (retourne false en cas d’échec):
<?php
$sql = 'SELECT name, color, calories FROM fruit ORDER BY name';
$statement=$dbo->query($sql);
foreach ($statement as $row) {
print $row['name'] . "\t";
print $row['color'] . "\t";
print $row['calories'] . "\n";
}
?>
Produit le résultat :
apple red 150 banana yellow 250 kiwi brown 75 lemon yellow 25 orange orange 300 pear green 150 watermelon pink 90
<?php
$sql = 'SELECT name, calories FROM fruit WHERE calories >100 ORDER BY name';
$statement=$dbo->query($sql);
foreach ($statement as $row) {
print $row['name'] . "\t";
print $row['calories'] . "\n";
}
?>
Produit le résultat :
apple red 150 banana yellow 250 orange orange 300 pear green 150
-- Mise à jour de données
A la différence des instructions de sélection, la mise à jour ne retourne pas de jeu d'enregistrements.
PDO::exec() exécute une requête SQL et retourne le nombre de lignes affectées par la requête.
<?php
$dbo = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
/* Effacement de toutes les lignes de la table FRUIT dont la couleur est rouge*/
$count = $dbo->exec("DELETE FROM fruit WHERE couleur = 'rouge'");
/* Retourne le nombre de lignes effacées */
print("Retourne le nombre de lignes effacées :\n");
print("Effacement de $count lignes.\n");
?>
L'exemple ci-dessus va afficher :
Retourne le nombre de lignes effacées : Effacement de 2 lignes.
-- Injections SQL
Consiste à modifier une instruction SQL existante, de façon malveillante (pour suppression de données, acquisition de droits, visualisation de données…)
Exemple :
URL afficheClient.php?id=1
<?php $id = $_GET["id"]; // Attention, aucun contrôle ! $query = "SELECT * FROM clients where id=".$id.";"; $result = $dbo->query($sql); ?>
exemple d'injection possible :
1;DELETE FROM clients WHERE 1=1
via l'URL afficheClient.php?id=1;DELETE FROM clients WHERE 1=1
Conséquence : Suppression de toutes les données de la table client
-- Solutions pour éviter les injections SQL
- Se connecter à la BDD avec un utilisateur ayant des droits limités.
- Vérifiez que les données ont bien le type attendu. Utiliser is_numeric(), ctype_digit()… ou modifiez les avec settype(), ou sprintf()
- Utiliser des requêtes paramétrées
Version corrigée avec sprintf de la requête précédente :
<?php
$id = $_GET["id"];
$query = sprintf("SELECT * FROM clients where id=%d;",$id);
$result = $dbo->query($sql);
?>
-- Requêtes paramétrées
Avantages
- Anti-injection
- Réutilisation
-- Préparation
PDOStatement PDO::prepare ( string $statement [, array $driver_options = array() ] )
Prépare une requête SQL à être exécutée, et retourne un PDOStatement
<?php $sql = 'SELECT nom, couleur, calories FROM fruit WHERE calories < :calories AND couleur = :couleur'; $sta = $dbo->prepare($sql); ?>
-- Affectation des valeurs
Avec paramètres nommés
<?php
$sta->bindValue(':calories', $calories, PDO::PARAM_INT);
$sta->bindValue(':couleur', $couleur, PDO::PARAM_STR);
?>
-- Exécution
<?php $sta->execute(); ?>
Version avec paramètres fictifs
<?php
$calories = 150;
$couleur = 'rouge';
$sta = $dbh->prepare('SELECT nom, couleur, calories FROM fruit WHERE calories < ? AND couleur = ?');
$sta->bindValue(1, $calories, PDO::PARAM_INT);
$sta->bindValue(2, $couleur, PDO::PARAM_STR);
$sta->execute();
?>
-- Récupération des enregistrements
PDOStatement::fetchAll — Retourne un tableau contenant toutes les lignes du jeu d'enregistrements
<?php
$calories=100;
$sta = $dbo->prepare("SELECT nom, couleur FROM fruit WHERE calories> ?");
$sta->bindValue(1, $calories, PDO::PARAM_INT);
$sta->execute();
/* Récupération de toutes les lignes d'un jeu de résultats */
print("Récupération de toutes les lignes d'un jeu de résultats :\n");
$result = $sta->fetchAll();
print_r($result);
?>
Classes/Enregistrements
Utilisation de PDO::FETCH_CLASS
<?php
class Fruit {
public $name;
public $colour;
}
$sta = $dbo->prepare("SELECT name, colour FROM fruit");
$sta->execute();
$result = $sta->fetchAll(PDO::FETCH_CLASS, "Fruit");
var_dump($result);
?>
Résultat :
array(3) {
[0]=>
object(fruit)#1 (2) {
["name"]=>
string(5) "apple"
["colour"]=>
string(5) "green"
}
[1]=>
object(fruit)#2 (2) {
["name"]=>
string(4) "pear"
["colour"]=>
string(6) "yellow"
}
[2]=>
object(fruit)#3 (2) {
["name"]=>
string(10) "watermelon"
["colour"]=>
string(4) "pink"
}
}