PHP et l'extension MySQLi (Prepared Statements)

Introduction

Une nouvelle extension MySQL (ext / MySQLi ou MySQL Improved Extension) pour PHP a été implémentée pour supporter les nouvelles fonctionnalités de MySQL 4.1 (commandes préparées etc...).

Cet article présente les objectifs de cette nouvelle extension, son utilisation basique avec PHP et la mise en œuvre des commandes préparées (Prepared Statements, nouveauté MySQL 4.1).

L'extension MySQLi (PHP 4 / PHP 5)

Fonctionnalités principales

Parmi les fonctionnalités principales de l'extension MySQLi :

  • Performances accrues avec la mise en œuvre du nouveau protocole binaire d'échange de données (binary protocol) : les requêtes ne sont plus converties en chaînes de caractères lourdes entre le client et le serveur, ces dernières sont transmises avec un nouveau protocole optimisé au format binaire. Certaines opérations sont 40 fois plus rapides avec l'extension MySQLi par rapport à l'ancienne extension.
  • Possibilité d'utiliser un mode procédural ou un mode orienté objet dans l'utilisation de l'extension MySQLi, le mode orienté objet étant la grande nouveauté de l'extension MySQLi.
  • Compatibilité avec les nouveautés MySQL 4.1 (prepared statements, character sets etc...)
  • Support additionnel pour l'activation des traces, le debuggage, le load balancing et les fonctionnalités liées à la réplication.

Utilisation basique

Toutes les fonctions PHP liées à l'extension MySQLi commencent par le préfixe mysqli_%.

Exemple mode procédural

Le mode procédural ne diffère pas de l'utilisation classique de la précédente extension ext /mysql.

<?php
 $link = mysqli_connect("localhost", "my_user", "my_password", "world");
 
 /* Vérification de la connexion */
 if (mysqli_connect_errno()) {
     printf("Connect failed: %s\n", mysqli_connect_error());
     exit();
 }
 
 $query = "SELECT Name, CountryCode FROM City ORDER by ID DESC LIMIT 50,5";
 
 if ($result = mysqli_query($link, $query)) {

    /* Récupération du tableau associé */
    while ($row = mysqli_fetch_row($result)) {
       printf ("%s (%s)\n", $row[0], $row[1]);
    }
 
    /* Libération du jeu de résultats */
    mysqli_free_result($result);
 }
 
 printf("System status: %s\n", mysqli_stat($link));
 mysqli_close($link);
?>

Exemple mode Orienté objet

<?php
 $mysqli = new mysqli("localhost", "my_user", "my_password", "world");

 /* Vérification de la connexion */
 if (mysqli_connect_errno()) {
     printf("Connect failed: %s\n", mysqli_connect_error());
     exit();
 }

 $query = "SELECT Name, CountryCode FROM City ORDER by ID DESC LIMIT 50,5";

 if($result = $mysqli->query($query)) {
 /* Récupération du tableau associé */
   while ($row = $result->fetch_row()) {
       printf ("%s (%s)\n", $row[0], $row[1]);
   }
   /* Libération du jeu de résultats */
   $result->close();
 }

 printf ("System status: %s\n", $mysqli->stat());
 $mysqli->close();
?>

À la différence du mode procédural où un objet ressource $link est utilisé (ex : mysql_query($link,query) ), dans le mode orienté objet, un objet mysqli disposant de méthodes est créé avec la syntaxe :

$mysqli = new mysqli("localhost", "my_user", "my_password", "world");

et les méthodes de l'objet mysqli sont appliquées sur l'objet en n'ayant plus à se soucier d'un objet ressource :

$mysqli->query($query);

Il en est de même pour les résultats en mode orienté objet : un objet de type mysqli_result disposant de méthodes et de propriétés est créé lors du retour de résultats :

$result->fetch_row();

Un autre type d'objet existe avec l'extension MySQLi et concerne uniquement les commandes préparées (prepared statements) : il s'agit de l'objet mysqli_stmt. Cet objet sera abordé dans le paragraphe 3.

Mode orienté objet : objets, méthodes et propriétés de l'extension MySQLi

Dans le tableau ci-dessous sont récapitulés les types d'objet, méthodes et propriétés disponibles avec l'extension MySQLi en mode orienté objet.

mysqli mysqli_result mysqli_stmt
Méthodes Propriétés Méthodes Propriétés Méthodes Propriétés
autocommit change_user character_set_name close connect commit debug dump_debug_info get_client_info get_host_info get_server_info get_server_version init info kill multi_query more_results next_result options ping prepare query real_connect real_escape_string rollback select_db ssl_set stat stmt_init store_result use_result thread_safe affected_rows client_info client_version errno error field_count host_info insert_id protocol_version sqlstate thread_id warning_count close data_seek fetch_field fetch_fields fetch_field_direct fetch_array fetch_assoc fetch_object fetch_row free field_seek current_field field_count lengths num_rows bind_param bind_result close data_seek execute fetch free_result prepare reset result_metadata send_long_data store_result affected_rows errno error param_count sqlstate

Prepared Statements (Commandes Préparées)

Les commandes préparées (prepared statements) fournissent la possibilité de créer des requêtes plus sécurisées, plus performantes et plus simples à écrire.

(pour plus d'informations sur les commandes préparées, voir l'article MySQL 4.1 et les commandes préparées).

Paramètres substituables liés à une commande préparée (bound parameters)

L'objectif est d'utiliser un modèle de requête une seule fois, modèle dans lequel les valeurs des paramètres substituables ... (bound parameters) sont données dynamiquement pour l'exécution.

Exemples :

INSERT INTO City (ID, Name) VALUES (?, ?);
SELECT id FROM t_si_users WHERE login like ?;

Avantages

Réduction des coûts de parsing

Ce modèle de requête (Prepared Statement) est validé et créé au niveau du serveur MySQL et pourra être exécuté plusieurs fois de suite avec des valeurs différentes pour les paramètres substituables.

Dans cette cinématique, la requête est parsée une bonne fois pour toutes au niveau du serveur MySQL, ce qui accroît les performances, et retourne un handle qui n'est autre qu'un objet mysqli_stmt permettant de faire référence à cette commande préparée stockée dans un buffer spécial du serveur MySQL.

Réduction de la taille des échanges réseaux

L'extension MySQLi utilise le nouveau protocole binaire (binary protocol), ce qui réduit considérablement la taille des échanges entre le serveur et le client, en effet dans l'ancienne extension les requêtes étaient échangées entre le client et le serveur avec des chaînes de caractères plus volumineuses.

Sécurité sur les valeurs des paramètres substituables

La sécurité est renforcée pour les valeurs données aux paramètres substituables et il n'est plus nécessaire par exemple d'utiliser la fonction mysql_real_escape_string( ) pour la gestion des quotes et autres caractères spéciaux.

Le client et le serveur avec l'extension MySQLi travaillent communément pour gérer la sécurité sur l'échange des données (caractères spéciaux etc ...), notamment pour les paramètres substituables (bound parameters).

Exemple

L'exemple qui suit repose sur le modèle orienté objet de l'objet MySQLi.

<?php
   $mysqli = new mysqli('localhost', 'user', 'password', 'world');
   
   $stmt = $mysqli
   
   /* Préparation de la commande */ 
   prepare("INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
   
   /* Association de valeurs aux paramètres */ 
   $stmt->bind_param('sssd', $code, $language, $official, $percent);
   
   $code = 'DEU'; $language = 'Bavarian';  $official = "F"; $percent = 11.2;
   
   /* Exécution de la commande préparée */ 
   $stmt->execute();
   
   printf("%d Row inserted.\n", $stmt->affected_rows);
   
   $stmt->close(); /* Fermeture de la commande */  
   $mysqli->close(); /* Fermeture de la connexion */

La fonction bind_param() de l'objet mysqli_stmt a comme premier paramètre une chaîne de caractères qui est utilisé pour spécifier comment les données variables doivent être traitées (chaînes de caractères, numérique, blob etc...).

Dans l'exemple plus haut, 'sssd' indique que les trois premiers paramètres $code, $language et $official doivent être envoyés comme chaînes de caractères, alors que le quatrième paramètre $percent contiendra une valeur de type double ou float.

Le type de chaque paramètre doit être notifié dans le premier argument de la méthode bind_param !

Le tableau ci-dessous regroupe les types possibles à utiliser avec la méthode bind_param :

Type Type de colonne
i Tous les types INT
d DOUBLE et FLOAT
b BLOBs
s Tous les autres types

Ces types permettent à l'extension MySQLi d'encoder les données avec une meilleure efficacité.

Résultats liés à une commande préparée

Autre grande nouveauté : l'extension MySQLi permet de lier automatiquement des variables PHP aux valeurs des champs dans le résultat d'une commande préparée grâce à la méthode bind_result de l'objet mysqli_stmt :

Exemple pratique :

<?php
    $mysqli = new mysqli("localhost", "user", "password", "world");
    
    /* Commande préparée */
    
    if ($stmt = $mysqli->prepare("SELECT Code, Name FROM Country ORDER BY Name LIMIT 5"))
    {
      $stmt->execute();
      /* Association des résultats à des variables PHP */ 
      $stmt->bind_result($col1, $col2);
      
      /* Récupération des valeurs */
      while ($stmt->fetch()) {
          printf("%s %s\n", $col1, $col2);
      }
      
      $stmt->close();  /* Fermeture de la commande */ 
    } 
    
    $mysqli->close();  /* Fermeture de la connexion */

?>