Remplacer PHP mysql(i) par PDO

Beaucoup d’entre vous utilisent encore mysql ou mysqli pour se connecter à leur base de données et la manipuler.
Bien que c’est fonctions existent encore sur PHP5, elle sont voués a disparaître, attention donc, il faut au plus vite perdre les mauvaises habitudes.

Passer de mysql(i) à PDO n’est pas bien méchant !

Avant vous aviez quelque chose du genre pour ouvrir la connexion :

$mysql = mysql_connect('localhost', 'utilisateur', 'passwd');
if (!$mysql) die ('Impossible de se connecter');
mysql_select_db('ma_base');
//...

Avec PDO, il y a bien sur quelques changements :

try {
    $dbh = new PDO('mysql:host=localhost;dbname=mydb', 'user', 'passwd');
    $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
    $dbh->setAttribute(PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION);
    $dbh->exec("SET NAMES utf8");
} catch (PDOException $e) {
    die ('Une erreur MySQL est arrivée: ' . $e->getMessage());
}
//...

Première chose, vous noterez que PDO permet une gestion plus fine des options de connexion à la base de données (ici par exemple je force toute les transactions en lowercase, c’est à dire que tout les noms de champs seront représenté en minuscule, même si ils comporte une majuscule sur le serveur, ce qui je le rappel n’est pas du tout conventionnel).

Maintenant que l’on est connecté, il faut exécuter nos requêtes.
Pour la sécurité, avantage encore a PDO (merci la préparation), pour la vitesse d’exécution, avantage a mysqli … mais bon sur plus de 10000 enregistrements la différence et de moins d’un quart de seconde (serveur mysql5.0 avec 500Mo de RAM)

Donc une requête de sélection à l’ancienne mode ressemblait à :

$rs = mysql_query('select sql_small_result * from users where login="' . $_POST['user'] . '" and pwd="' . SHA1($_POST['pwd']) . '" limit 0,1');
if (mysql_num_rows($rs) > 0) {
    $rw = mysql_fetch_array($rs);
    //...
}

Ok, c’est bien, mais pour se protéger des vilains, on est mal…
Première (et vielle) méthode utiliser: htmlspecialchar() pour casser les tentatives d’injection, seulement, htmlspecialchar n’est pas fait pour cela, elle transformera bien les caractères spéciaux comme le = ou >, en symbole HTML, ce qui empêchera certaine malversations, mais même si ça marche, ça reste bancale…
Un peu mieux ensuite, nous avons mysql_real_escape_string, qui protégera bcp mieux vos chaines, mais là aussi autre problème, ceci est obsolète depuis PHP 5.5, ce qui veux dire que vos scripts l’utilisant ne seront plus valables d’ici quelques temps (années)…

Le solution est donc a nouveau dans PDO, cette dernière nous proposant des méthodes de sécurisation de nos requêtes.

$sth = $dbh->prepare('select sql_small_result * from users where login = :login and pwd = :pwd limit 0,1');
$sth->bindParam(':login', $_POST['user'], PDO::PARAM_STR);
$sth->bindParam(':pwd', sha1($_POST['pwd']), PDO::PARAM_STR);
$rs = $sth->execute();
if ($rs->rowCount() > 0) {
    $rw = $rs->fetch(PDO::FETCH_OBJ);
    //...
}

Je vois d’ici les râleurs dire « Rohlalala mais ça fait beaucoup plus de choses à écrire !! »
OUI en effet, mais là votre requête est vraiment protégée contrairement à précédemment, en effet, ici seule des chaînes de caractères (et non une sous-requête) seront acceptés, vous pouvez bien sur préparer d’autres types de variables (booléens, entiers, etc… ceci fera l’objet d’un autre billet)

Donc pour conclure :

  • Utiliser PDO de préférence pour vos nouveaux projets, cela vous évitera de devoir tout refaire d’ici peu
  • Oui PDO est un poil plus gourmant en ligne que mysql(i), mais avec une vrai sécurité des données
  • Changer ses habitudes est toujours désagréables, mais cela vous apportera tant !

4 réflexions au sujet de « Remplacer PHP mysql(i) par PDO »

  1. Aurelien

    Bonjour Benjamin,
    Te serait -il possible de préciser cette ligne, il y a quelque chose qui m’échappe.

    $sth->bindParam(‘:login’, $_POST[‘user’], PDO::PARAM_STR);

    Merci à toi et bonne journée

    Aurélien

    Ps : Sympa la vidéo !!

    Répondre
    1. Benjamin Auteur de l’article

      Hello Aurélien
      Oui bien sur, bindParam permet de passer tes paramètres à la requête préparer, le premier argument et le nom du param dans la dite requête (ici « :login »), $_POST[‘user’] est l’identifiant récupérer dans le POST, et enfin le dernier paramètre permet de préciser le type de données que l’on passe (ici une chaîne de caractères, pdo ajoutera tout seul les cotes par ex)

      Répondre
      1. Aurelien

        Pour confirmation et comme tu parles de sécurisation, cela n’a rien à voir avec la faille XSS, mais seulement sécurisé l’injection de code dans les requêtes ?

        Répondre
        1. Benjamin Auteur de l’article

          Oui Aurélien, c’est bien ca, ce n’est pas du XSS, mais contre l’injection SQL (bien que l’injection SQL découle de la faille XSS)

          Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *