Pour rappel, MySQL est un SGBD (Système de Gestion de Bases de Données) qui est très souvent associé à PHP. Il faut connaître la syntaxe objet de php pour comprendre PDO.
Extension PDO
L'extension PDO (PHP Data Objects) est un outil qui permet d'accéder à n'importe quel SGBD. Il est très populaire et c'est avec lui que nous allons apprendre à établir la communication entre PHP et MySQL.
Normalement, PDO est activé par défaut. Pour le vérifier, on peut afficher une page php qui renvoie phpinfo() :
<?php phpinfo();
Vous devriez alors trouver les informations suivantes :
PDO support | enabled |
---|---|
PDO drivers | mysql |
Si vous avez besoin de l'activer, ouvrez le fichier de configuration de PHP php.ini et décommentez la ligne suivante :
extension=php_pdo_mysql.dll
Connexion
Pour se connecter à une base de données MySQL via du PHP et son extension PDO, il faut créer une nouvelle instance de PDO. Il vous faudra pour cela fournir 3 arguments :
- le premier argument est un peu complexe puisqu'il comprend 3 informations :
- l'adresse de la machine "host" qui héberge Mysql
- le nom de la base de données
- le type d'encodage
- le deuxième argument permet de spécifier le nom de l'utilisateur de la base de données
- le troisième argument est le mot de passe de l'utilisateur de la base de données
Par exemple :
$pdo = new PDO('mysql:host=localhost;dbname=mycms;charset=utf8', 'bob', 'xxx');
En fait pour que votre code soit plus propre et donne une indication plus lisible à l'utilisateur, on va "catcher" les éventuelles erreurs :
<?php try { $pdo = new PDO('mysql:host=localhost;dbname=mycms;charset=utf8', 'bob', 'xxx'); $pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING); } catch(PDOException $e) { echo "Pb de connexion à la base de données ", $e->getMessage(); }
Requête
L'exécution et la restitution d'une requête peut s'opèrer en 3 étapes :
- la création de chaine de caractères de requête
- l'exécution de la requête
- le parcours et l'affichage des résultats de la requête
Ex de code :
$sql = 'select * from node;'; $req = $pdo->query($sql); // fetch renvoie les enregistrements un par un // tant qu'il (while) y a un enregistrement, on reste dans la boucle while($record = $req->fetch(PDO::FETCH_ASSOC)) { // $record est un tableau associatif (FETCH_ASSOC) - Ex $record["nid"]; // il permet d'accéder à la valeur des champs (colonne) echo $record["nid"]; echo $record["title"]; }
L'argument "PDO::FETCH_ASSOC" de la méthode fetch qui permet de récupérer les résultats sous formes de tableau associatif. L'alternative la plus fréquement utilisée est "PDO::FETCH_OBJ"" qui vous permettra de récupérer les données sous la forme d'objets.
Gestion des erreurs
Par défaut, PDO est en mode "silence", ce qui veut dire qu'il ne vous préviendra pas forcément dès qu'il aura identifié une erreur.
C'est pour cette raison qu'il est conseillé de passer en mode "WARNING" comme nous l'avons fait précédemment dans notre "catch"
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
Cela va ensuite nous permettre de mieux gérer les erreurs :
$sql = 'select * from node;'; try { $req = $pdo->query($sql); while($record = $req->fetch(PDO::FETCH_ASSOC)) { print_r($record); } } catch(PDOException $e) { echo "Pb de requête", $e->getMessage(); }
Règles de formatage d'une requête SQL
Pour éviter les "injections SQL", il faut connaitre les règles de formatage que nous allons énumérer ci-dessous. Mais pour que ces règles soient systématiquement respectées, il faudra utiliser les requêtes préparées (cf paragraphe suivant).
Règles de formatage d'une requête SQL :
Concernant les chaînes de caractères :
- elles doivent être comprises dans des apostrophes
- les caractères spéciaux doivent être "échappés" (en utilisant la méthode quote de PDO par exemple : $pdo->quote($firstname); - ou la fonction htmlspecialchars )
- un type d'encodage (côté client) doit être spécifié ou la chaîne peut être encodée en hexadécimal
Concernant les nombres :
- doivent être filtrés pour s'assurer que seuls les caractères numériques, un séparateur décimal et un signe sont présents
- doivent être compris dans des "backticks"
- les caractères spéciaux doivent être "échappés"
Operateurs et mot clés :
il n'y a pas de règles de formatage spéciales pour les mots-clés et les opérateurs à part le fait qu'ils doivent être des opérateurs et des mots-clés SQL légitimes.
Requêtes préparées
La notions de requête préparée est essentielle dans la gestion d'une base de données car :
- La requête n'est préparée qu'une seule fois, mais peut être exécutée plusieurs fois avec des paramètres modifiables. Lorsque la requête est préparée, la base de données va analyser, compiler et optimiser son plan pour exécuter la requête. Pour les requêtes complexes, ce processus peut prendre assez de temps, ce qui peut ralentir vos applications si vous devez répéter la même requête plusieurs fois avec différents paramètres. En utilisant les requêtes préparées, vous évitez ainsi de répéter le cycle analyse/compilation/optimisation. Pour résumer, les requêtes préparées utilisent moins de ressources et s'exécutent plus rapidement.
- Les requêtes préparées empêchent les injection SQL qui sont l'une des failles de sécurité les plus courantes. Le fait de créer une requête SQL à partir d'une partie constante et d'espaces réservés, qui seront remplacés par des données réelles, va permettre le formatage automatique des requêtes SQL .
- Les paramètres pour préparer les requêtes n'ont pas besoin d'être entre guillemets, c'est donc automatiquement géré.
Selection avec une requête préparée
try { $data = [ 'nid' => $node_id ]; $req = self::$pdo->prepare('SELECT * FROM node WHERE nid = :nid'); $req->execute($data); return $req->fetch(PDO::FETCH_OBJ); } catch (PDOException $e) { echo "Pb de requête", $e->getMessage(); }
Update avec une requête préparée
try { $data = [ 'nid' => $_POST['nid'], 'title' => $_POST['title'], 'seo_title' => $_POST['seo_title'], 'body' => $_POST['body'], 'path' => $_POST['path'], ]; $req = self::$pdo->prepare('UPDATE node SET title = :title, seo_title = :seo_title, body = :body, path = :path WHERE nid = :nid'); $req->execute($data); } catch (PDOException $e) { echo "Pb de requête", $e->getMessage(); }
Insertion avec une requête préparée
try { $data = [ 'type' => 'article', 'title' => 'Deuxième article', 'body' => '<p>Ceci est le corps de mon deuxième article</p>', 'path' => '/deuxieme-article' ]; $req = $pdo->prepare('INSERT INTO node (type,title,body,path) VALUES (:type,:title,:body,:path)'); $req->execute($data); } catch(PDOException $e) { echo "Pb de requête", $e->getMessage(); }
Suppression avec une requête préparée
try { $data = [ 'nid' => $nid ]; // Requête préparée $req = $pdo->prepare('DELETE FROM node WHERE nid = :nid'); //Execution de la requête $req->execute($data); catch(PDOException $e) { echo "Pb de requête", $e->getMessage(); }
Exercice :
Créez via phpmyadmin votre base de données "mycms" et utilisez ce code sql pour créer la table "node" avec quelques enregistrements.
- Créer une première page qui liste les nodes enregistrés en base de données en affichant uniquement : l'id, le titre et un lien "modifier". Exemple :
- au click sur le bouton modifier, un formulaire prérempli avec toutes les informations (sauf le nid) du "node" en question va permettre à l'internaute de modifier les différents champs.
- A la validation de ce formulaire, la listes des "nodes" sera à nouveau affichée (seulement id, titre et lien "modifier").