Comprendre et utiliser MySQLi
Dans cet article, nous allons explorer MySQLi (MySQL Improved), un des principaux connecteurs PHP utilisés pour interagir avec les bases de données MySQL et ses dérivés, comme MariaDB. MySQLi a été conçu pour offrir une alternative moderne et sécurisée à l’ancienne extension MySQL, désormais dépréciée. Que vous soyez un développeur débutant ou expérimenté, ce guide vous permettra de comprendre comment et quand utiliser MySQLi dans vos projets.
Si vous souhaitez une vue d’ensemble sur les différents connecteurs disponibles pour MySQL, tels que PDO et MySQLi, consultez notre article Introduction aux connecteurs MySQL pour bien saisir leurs avantages respectifs.
Qu’est-ce que MySQLi et pourquoi a-t-il été introduit ?
MySQLi est une extension PHP qui permet aux développeurs de se connecter à des bases de données MySQL et d’interagir avec elles de manière plus sécurisée, performante, et flexible que l’ancien connecteur MySQL. Introduit avec PHP 5, MySQLi apporte de nombreuses améliorations, notamment le support des requêtes préparées, des transactions, et une approche orientée objet en plus de la traditionnelle interface procédurale.
Avec la dépréciation de l’extension MySQL à partir de PHP 5.5 et sa suppression complète dans PHP 7, MySQLi est devenu la solution par défaut pour tout projet PHP qui repose sur des bases de données MySQL.
Quand et pourquoi utiliser MySQLi ?
Si vous travaillez exclusivement avec une base de données MySQL ou ses dérivées comme MariaDB, MySQLi est souvent la meilleure option. Il offre des performances optimisées et un accès aux fonctionnalités avancées spécifiques à MySQL. En revanche, si vous avez besoin de vous connecter à d’autres types de bases de données (comme PostgreSQL ou SQLite), vous préférerez sans doute PDO, qui est multi-bases.
Cependant, si votre projet ne dépend que de MySQL, MySQLi vous permet d’accéder à des fonctionnalités avancées comme les transactions ACID, tout en offrant un haut niveau de sécurité grâce aux requêtes préparées.
MySQLi apporte une série d’améliorations et de fonctionnalités essentielles pour la gestion de bases de données MySQL, rendant vos interactions plus sécurisées et performantes. Que vous choisissiez d’utiliser MySQLi en mode procédural ou orienté objet, vous bénéficiez de fonctionnalités avancées telles que les requêtes préparées pour sécuriser vos requêtes SQL, le support des transactions pour garantir l’intégrité des données, et une gestion flexible de l’encodage des caractères, indispensable pour les projets multilingues. Voyons en détail ces différentes fonctionnalités.
Mode procédural et orienté objet
MySQLi vous offre la flexibilité de choisir entre deux approches :
- Le mode procédural, qui ressemble à l’ancien connecteur MySQL. Il est souvent plus familier pour les développeurs venant de projets utilisant l’ancienne extension.
- Le mode orienté objet, qui permet une structure de code plus propre et modulable, particulièrement utile dans les projets modernes.
Exemple de connexion en mode procédural :
$mysqli = mysqli_connect('localhost', 'user', 'password', 'database');
if (!$mysqli) {
die('Erreur de connexion : ' . mysqli_connect_error());
}
echo 'Connexion réussie!';
mysqli_close($mysqli);
Exemple de connexion en mode orienté objet :
$mysqli = new mysqli('localhost', 'user', 'password', 'database');
if ($mysqli->connect_error) {
die('Erreur de connexion (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
}
echo 'Connexion réussie!';
$mysqli->close();
Requêtes préparées : sécurité et efficacité
Une des plus grandes avancées de MySQLi par rapport à l’ancien connecteur est le support des requêtes préparées. Ce système permet de séparer la requête SQL des données utilisateurs, empêchant ainsi les attaques par injection SQL, une des vulnérabilités les plus courantes et dangereuses dans les applications web.
Les requêtes préparées peuvent être utilisées en mode procédural comme en mode orienté objet.
⚡ Encart : Requêtes préparées et gestion des erreurs
Les requêtes préparées sont une méthode clé pour sécuriser vos interactions avec une base de données. Elles permettent de créer une requête SQL statique, que l’on complète ensuite avec des données entrées par l’utilisateur, garantissant ainsi que les données ne seront jamais interprétées comme du code SQL. En plus de cela, MySQLi offre une gestion d’erreurs plus claire, avec des fonctions comme
mysqli_stmt_error()
ou$mysqli->error
en mode orienté objet, facilitant la détection et le débogage des problèmes.
Exemple de requête préparée en mode procédural :
$mysqli = mysqli_connect('localhost', 'user', 'password', 'database');
$stmt = mysqli_prepare($mysqli, "SELECT * FROM users WHERE id = ?");
mysqli_stmt_bind_param($stmt, "i", $userId);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);
echo $user['name'];
mysqli_stmt_close($stmt);
mysqli_close($mysqli);
Exemple de requête préparée en mode orienté objet :
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $userId);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
echo $user['name'];
$stmt->close();
Paramètres positionnels avec MySQLi
Avec MySQLi, les requêtes préparées utilisent des paramètres positionnels représentés par des marqueurs anonymes (?
). Chaque paramètre est lié via la méthode bind_param()
, qui permet de spécifier le type de chaque valeur et de l’associer à un marqueur.
Exemple d’utilisation de paramètres positionnels avec MySQLi :
$mysqli = new mysqli('localhost', 'user', 'password', 'database');
if ($mysqli->connect_error) {
die('Erreur de connexion : ' . $mysqli->connect_error);
}
// Préparation de la requête avec des marqueurs positionnels (?)
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ? AND status = ?");
$userId = 1;
$status = 'active';
// Liaison des paramètres positionnels (type "i" pour integer, "s" pour string)
$stmt->bind_param("is", $userId, $status);
// Exécution de la requête
$stmt->execute();
// Récupération des résultats
$result = $stmt->get_result();
$user = $result->fetch_assoc();
echo $user['name'];
// Fermeture des ressources
$stmt->close();
$mysqli->close();
Détails sur bind_param()
:
- La fonction
bind_param()
permet de lier des valeurs aux marqueurs positionnels (?
) présents dans la requête. - Elle prend en premier argument une chaîne de caractères indiquant le type des paramètres à lier :
"i"
: integer (entier)."s"
: string (chaîne de caractères)."d"
: double (nombre flottant)."b"
: blob (données binaires).
Dans l’exemple ci-dessus :
bind_param("is", $userId, $status)
est utilisé pour lier les deux paramètres de la requête :"s"
indique que le deuxième paramètre ($status
) est une chaîne de caractères."i"
indique que le premier paramètre ($userId
) est un entier.
Paramètres nommés non pris en charge
Contrairement à PDO, MySQLi ne permet pas l’utilisation de paramètres nommés (:nom
) dans les requêtes préparées. Toutes les requêtes doivent utiliser des marqueurs anonymes (?
), ce qui implique que les paramètres doivent être fournis dans un ordre spécifique et liés individuellement par leur position.
Cela signifie que si vous avez une préférence pour une syntaxe plus explicite ou si vous avez une requête complexe avec de nombreux paramètres, PDO pourrait être plus adapté. Avec PDO, chaque valeur est liée à un nom de paramètre, ce qui rend le code plus lisible et moins susceptible de causer des erreurs liées à l’ordre des paramètres.
Comparaison avec PDO
- MySQLi n’utilise que des marqueurs positionnels (
?
) pour les requêtes préparées. - PDO offre la possibilité d’utiliser des marqueurs nommés (
:nom
), offrant une meilleure lisibilité et flexibilité pour des requêtes complexes. - Dans MySQLi, l’ordre des valeurs est crucial, car les paramètres sont liés dans un ordre fixe via
bind_param()
.
Transactions : garantir l’intégrité des données
Les transactions permettent d’exécuter plusieurs opérations de manière atomique. Cela signifie que toutes les opérations sont exécutées ou aucune ne l’est, garantissant ainsi l’intégrité des données. Par exemple, dans une application financière, il est important qu’une transaction soit entièrement réalisée ou annulée en cas de problème.
MySQLi prend en charge les transactions, en vous offrant un contrôle total sur le commit et le rollback des opérations.
Imaginons que nous effectuons un transfert d’argent entre deux comptes utilisateurs dans une application bancaire. Si l’une des étapes du transfert échoue (débit ou crédit), la transaction doit être annulée pour garantir la cohérence des données.
Exemple en mode orienté objet
// Connexion à la base de données
$mysqli = new mysqli('localhost', 'user', 'password', 'database');
if ($mysqli->connect_error) {
die('Erreur de connexion (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
}
// Désactiver l'autocommit pour débuter la transaction
$mysqli->autocommit(FALSE);
try {
// Débiter le compte de l'utilisateur 1
$stmt = $mysqli->prepare("UPDATE accounts SET balance = balance - ? WHERE user_id = ?");
$amount = 100; // Montant à transférer
$userId1 = 1; // ID de l'utilisateur 1
$stmt->bind_param("di", $amount, $userId1);
$stmt->execute();
if ($stmt->affected_rows === 0) {
throw new Exception('Échec du débit');
}
// Créditer le compte de l'utilisateur 2
$stmt = $mysqli->prepare("UPDATE accounts SET balance = balance + ? WHERE user_id = ?");
$userId2 = 2; // ID de l'utilisateur 2
$stmt->bind_param("di", $amount, $userId2);
$stmt->execute();
if ($stmt->affected_rows === 0) {
throw new Exception('Échec du crédit');
}
// Si tout est réussi, valider la transaction
$mysqli->commit();
echo "Transaction réussie !";
} catch (Exception $e) {
// Si une erreur survient, annuler la transaction
$mysqli->rollback();
echo "Erreur lors de la transaction : " . $e->getMessage();
}
// Fermer la connexion et les statements
$stmt->close();
$mysqli->close();
Même exemple en mode procédural
// Connexion à la base de données
$mysqli = mysqli_connect('localhost', 'user', 'password', 'database');
if (!$mysqli) {
die('Erreur de connexion : ' . mysqli_connect_error());
}
// Désactiver l'autocommit pour débuter la transaction
mysqli_autocommit($mysqli, FALSE);
try {
// Débiter le compte de l'utilisateur 1
$stmt = mysqli_prepare($mysqli, "UPDATE accounts SET balance = balance - ? WHERE user_id = ?");
$amount = 100; // Montant à transférer
$userId1 = 1; // ID de l'utilisateur 1
mysqli_stmt_bind_param($stmt, "di", $amount, $userId1);
mysqli_stmt_execute($stmt);
if (mysqli_stmt_affected_rows($stmt) === 0) {
throw new Exception('Échec du débit');
}
// Créditer le compte de l'utilisateur 2
$stmt = mysqli_prepare($mysqli, "UPDATE accounts SET balance = balance + ? WHERE user_id = ?");
$userId2 = 2; // ID de l'utilisateur 2
mysqli_stmt_bind_param($stmt, "di", $amount, $userId2);
mysqli_stmt_execute($stmt);
if (mysqli_stmt_affected_rows($stmt) === 0) {
throw new Exception('Échec du crédit');
}
// Si tout est réussi, valider la transaction
mysqli_commit($mysqli);
echo "Transaction réussie !";
} catch (Exception $e) {
// Si une erreur survient, annuler la transaction
mysqli_rollback($mysqli);
echo "Erreur lors de la transaction : " . $e->getMessage();
}
// Fermer la connexion et le statement
mysqli_stmt_close($stmt);
mysqli_close($mysqli);
Explication de ce mécanisme :
- Nous avons désactivé l’autocommit en début de script avec
autocommit(FALSE)
pour indiquer que les changements ne seront appliqués à la base de données que lorsque la transaction sera validée explicitement avec un commit. - Si une des opérations échoue (par exemple, si le débit ou le crédit échoue), nous lançons une exception qui déclenche un rollback et annule toutes les modifications effectuées pendant la transaction.
- Si tout se passe bien, nous validons la transaction avec commit, ce qui applique définitivement les changements à la base de données.
Ce mécanisme garantit que les modifications sont appliquées uniquement si toutes les opérations réussissent, préservant ainsi l’intégrité des données.
Encodage des caractères : gérer UTF-8 avec MySQLi
Lorsque vous travaillez avec des bases de données contenant des textes multilingues ou des caractères spéciaux, il est essentiel de s’assurer que l’encodage des caractères est correctement géré pour éviter les problèmes de compatibilité. Avec MySQLi, il est facile de définir l’encodage utilisé pour les connexions à la base de données via la fonction mysqli_set_charset()
.
Mode orienté objet
En mode orienté objet, vous pouvez utiliser la méthode set_charset()
pour définir l’encodage de la connexion à la base de données :
$mysqli = new mysqli('localhost', 'user', 'password', 'database');
// Vérifier la connexion
if ($mysqli->connect_error) {
die('Erreur de connexion : ' . $mysqli->connect_error);
}
// Définir l'encodage en UTF-8
if (!$mysqli->set_charset("utf8")) {
die('Erreur lors du chargement du jeu de caractères utf8 : ' . $mysqli->error);
}
echo "L'encodage des caractères est défini sur UTF-8";
Mode procédural
En mode procédural, la fonction équivalente est mysqli_set_charset()
:
$mysqli = mysqli_connect('localhost', 'user', 'password', 'database');
// Vérifier la connexion
if (!$mysqli) {
die('Erreur de connexion : ' . mysqli_connect_error());
}
// Définir l'encodage en UTF-8
if (!mysqli_set_charset($mysqli, "utf8")) {
die('Erreur lors du chargement du jeu de caractères utf8 : ' . mysqli_error($mysqli));
}
echo "L'encodage des caractères est défini sur UTF-8";
L’utilisation de UTF-8 est fortement recommandée pour garantir la bonne gestion des caractères, en particulier si vous travaillez avec des utilisateurs internationaux ou des données multilingues.
Bases de données compatibles avec MySQLi
Il est important de noter que MySQLi est conçu exclusivement pour MySQL et ses dérivés, comme MariaDB. Contrairement à PDO, MySQLi ne prend pas en charge plusieurs systèmes de gestion de bases de données. Si votre projet repose uniquement sur MySQL, MySQLi est un excellent choix pour tirer parti de toutes les fonctionnalités spécifiques à ce système. Toutefois, si vous envisagez de travailler avec plusieurs types de bases de données, PDO serait plus adapté.
Comparaison avec PDO
Bien que MySQLi et PDO offrent tous deux des fonctionnalités similaires, comme les requêtes préparées et le support des transactions, il existe des différences clés :
- Spécificité MySQL : MySQLi est limité à MySQL et MariaDB, mais permet d’accéder à des fonctionnalités avancées spécifiques à MySQL (comme les procédures stockées).
- Portabilité : PDO est conçu pour être portable et permet de se connecter à plusieurs types de bases de données. Si vous avez besoin de flexibilité pour changer de base de données, PDO est le meilleur choix.
- Performances : Dans certains scénarios, MySQLi peut être légèrement plus performant pour des opérations spécifiques à MySQL, mais les différences de performance entre PDO et MySQLi sont généralement mineures.
Conclusion
MySQLi est un excellent choix pour tout projet PHP qui repose sur une base de données MySQL. Il offre des fonctionnalités avancées comme les requêtes préparées, les transactions, et une gestion améliorée des erreurs, tout en permettant une utilisation en mode procédural ou orienté objet. Si votre projet est exclusivement lié à MySQL ou MariaDB, MySQLi est l’outil idéal pour une connexion performante et sécurisée à vos bases de données.
Pour les projets nécessitant plus de portabilité ou une connexion à différents types de bases de données, nous vous invitons à consulter notre article sur Utiliser PDO pour gérer plusieurs types de bases de données en PHP, un connecteur multi-bases de données, qui apporte une flexibilité incomparable pour vos développements PHP.