OUJOOD.COM
La fonction store_result() / mysqli_store_result() en PHP : Introduction
PHP Version: 5+
La fonction store_result() (ou mysqli_stmt_store_result()) est une méthode essentielle en PHP MySQLi pour récupérer et stocker en mémoire le résultat complet d'une requête préparée (prepared statement). Contrairement aux requêtes simples, cette fonction charge immédiatement l'intégralité des lignes du jeu de résultats en mémoire, permettant ainsi des opérations avancées comme le comptage précis des lignes via num_rows, le parcours multiple des données, et un meilleur contrôle du flux d'exécution.
⚠️ Important à savoir :
La fonction store_result() s'utilise EXCLUSIVEMENT avec les requêtes préparées (prepared statements), après l'exécution d'un execute(). Elle ne doit JAMAIS être utilisée avec mysqli_query() qui retourne directement un objet mysqli_result prêt à l'emploi. Cette distinction est cruciale pour éviter des erreurs fatales dans votre code.
Définition et utilisation
La fonction store_result() est spécifiquement conçue pour les requêtes préparées MySQLi. Elle récupère le jeu de résultats complet après l'exécution d'une requête préparée avec execute(), et le stocke en mémoire côté client pour un accès rapide et flexible.
Syntaxe procédurale :
mysqli_stmt_store_result($stmt);
Syntaxe orientée objet :
$stmt->store_result();
Où $stmt est un objet mysqli_stmt (statement/requête préparée) obtenu via prepare().
La fonction renvoie true en cas de succès (résultat correctement stocké) ou false en cas d'erreur (problème de connexion, requête invalide, mémoire insuffisante).
Différence entre store_result() et mysqli_query()
📌 Distinction essentielle à comprendre :
- mysqli_query() : Exécute une requête SQL simple (non préparée) et retourne directement un objet mysqli_result exploitable. Aucun appel à store_result() n'est nécessaire ni possible
- store_result() : S'utilise UNIQUEMENT avec les requêtes préparées (prepared statements) après un execute(). Elle transforme le flux de résultats en données stockées accessibles via bind_result()
Exemple avec mysqli_query() (PAS besoin de store_result) :
// Requête simple sans paramètres - Retourne directement mysqli_result $result = $mysqli->query('SELECT * FROM users'); // Parcours immédiat des résultats sous forme de tableau associatif while ($row = $result->fetch_assoc()) { echo $row['name']; }
Exemple avec requête préparée (BESOIN de store_result) :
// Requête préparée avec paramètre sécurisé - Protection contre injections SQL $stmt = $mysqli->prepare('SELECT name FROM users WHERE id = ?'); // Liaison du paramètre : 'i' = integer (entier) $stmt->bind_param('i', $id); // Exécution de la requête sur le serveur MySQL $stmt->execute(); // ÉTAPE OBLIGATOIRE : Stockage du résultat en mémoire côté client $stmt->store_result(); // Liaison des colonnes du résultat aux variables PHP $stmt->bind_result($name); // Récupération ligne par ligne des résultats stockés while ($stmt->fetch()) { echo $name; }
Syntaxes orientée objet et procédurale
La fonction store_result() est disponible en deux syntaxes équivalentes selon votre style de programmation :
- Syntaxe orientée objet (recommandée pour les projets modernes) :
$stmt->store_result();
- Syntaxe procédurale (compatible avec l'ancien code legacy) :
mysqli_stmt_store_result($stmt);
Valeurs possibles et paramètres
La fonction store_result() ne prend aucun paramètre en entrée.
Elle retourne :
- true : Si le résultat a été stocké avec succès en mémoire (opération réussie)
- false : En cas d'erreur (connexion perdue, mémoire insuffisante, requête invalide)
Exemples pratiques avec requêtes préparées
Exemple orienté objet avec requête préparée
Exemple : 📋 Copier le code
<?php // Établissement de la connexion à la base de données MySQL $mysqli = new mysqli('localhost', 'root', '', 'my_database'); // Vérification que la connexion a réussi if ($mysqli->connect_error) { die('Erreur de connexion : ' . $mysqli->connect_error); } // Préparation d'une requête SQL avec marqueur de position (?) // Cette approche protège contre les injections SQL $stmt = $mysqli->prepare('SELECT name, email FROM users WHERE age > ?'); if (!$stmt) { die('Erreur de préparation : ' . $mysqli->error); } // Liaison du paramètre : 'i' signifie que $age est un integer // Types possibles : 'i' (integer), 's' (string), 'd' (double), 'b' (blob) $age = 18; $stmt->bind_param('i', $age); // Exécution de la requête préparée sur le serveur MySQL $stmt->execute(); // CRUCIAL : Stockage du résultat complet en mémoire côté client // Sans cette ligne, num_rows ne serait pas disponible $stmt->store_result(); // Liaison des colonnes du résultat aux variables PHP // Chaque fetch() remplira automatiquement $name et $email $stmt->bind_result($name, $email); // Affichage du nombre total de résultats (disponible grâce à store_result) echo "Nombre de résultats : " . $stmt->num_rows . PHP_EOL; // Parcours ligne par ligne du jeu de résultats stocké while ($stmt->fetch()) { echo "Nom : $name, Email : $email" . PHP_EOL; } // Libération des ressources et fermeture des connexions $stmt->close(); $mysqli->close(); ?>
Exemple procédural avec requête préparée
Exemple : 📋 Copier le code
<?php // Connexion procédurale à la base de données $mysqli = mysqli_connect('localhost', 'root', '', 'my_database'); // Test de la connexion if (!$mysqli) { die('Erreur de connexion : ' . mysqli_connect_error()); } // Préparation de la requête avec paramètre protégé contre les injections SQL $stmt = mysqli_prepare($mysqli, 'SELECT name, email FROM users WHERE age > ?'); if (!$stmt) { die('Erreur de préparation : ' . mysqli_error($mysqli)); } // Liaison du paramètre : 'i' = type integer pour la variable $age $age = 18; mysqli_stmt_bind_param($stmt, 'i', $age); // Exécution de la requête préparée sur le serveur mysqli_stmt_execute($stmt); // Stockage de l'intégralité du résultat en mémoire (obligatoire pour num_rows) mysqli_stmt_store_result($stmt); // Association des colonnes retournées aux variables PHP $name et $email mysqli_stmt_bind_result($stmt, $name, $email); // Comptage du nombre de lignes retournées (possible uniquement après store_result) echo "Nombre de résultats : " . mysqli_stmt_num_rows($stmt) . PHP_EOL; // Itération sur chaque ligne du résultat while (mysqli_stmt_fetch($stmt)) { echo "Nom : $name, Email : $email" . PHP_EOL; } // Nettoyage : fermeture du statement et de la connexion mysqli_stmt_close($stmt); mysqli_close($mysqli); ?>
Cas d'utilisation de la fonction store_result() en PHP
Cas d'utilisation 1 : Afficher la liste de tous les utilisateurs de la table perso dont l'âge est supérieur à une valeur
Approche orientée objet
Exemple : 📋 Copier le code
<?php // Initialisation de la connexion à la base de données 'teste' $mysqli = new mysqli('localhost', 'root', '', 'teste'); if ($mysqli->connect_error) { die('Erreur : ' . $mysqli->connect_error); } // Préparation d'une requête SELECT filtrée par âge minimum // Le ? sera remplacé par la valeur de $age_min de manière sécurisée $stmt = $mysqli->prepare('SELECT nom, prenom, age FROM perso WHERE age > ?'); // Définition de l'âge minimum et liaison au paramètre de la requête // 'i' indique que $age_min est un entier (integer) $age_min = 25; $stmt->bind_param('i', $age_min); // Exécution de la requête avec le paramètre lié $stmt->execute(); // Stockage du résultat complet en mémoire pour accès à num_rows $stmt->store_result(); // Association des trois colonnes retournées aux variables PHP $stmt->bind_result($nom, $prenom, $age); // Affichage du nombre total de personnes correspondant au critère echo "Nombre de personnes trouvées : " . $stmt->num_rows . PHP_EOL; // Boucle de parcours : fetch() remplit les variables à chaque itération while ($stmt->fetch()) { echo $nom . ' ' . $prenom . ' (' . $age . ' ans)' . PHP_EOL; } // Libération des ressources mémoire $stmt->close(); $mysqli->close(); ?>
Approche procédurale
Exemple : 📋 Copier le code
<?php // Connexion procédurale à MySQL $mysqli = mysqli_connect('localhost', 'root', '', 'teste'); if (!$mysqli) { die('Erreur : ' . mysqli_connect_error()); } // Création d'une requête préparée avec filtre sur l'âge $stmt = mysqli_prepare($mysqli, 'SELECT nom, prenom, age FROM perso WHERE age > ?'); // Liaison du paramètre d'âge minimum (type integer) $age_min = 25; mysqli_stmt_bind_param($stmt, 'i', $age_min); // Lancement de l'exécution de la requête mysqli_stmt_execute($stmt); // Chargement de tous les résultats en mémoire client mysqli_stmt_store_result($stmt); // Mappage des colonnes SQL vers les variables PHP mysqli_stmt_bind_result($stmt, $nom, $prenom, $age); // Comptage précis du nombre de résultats obtenus echo "Nombre de personnes trouvées : " . mysqli_stmt_num_rows($stmt) . PHP_EOL; // Récupération et affichage ligne par ligne while (mysqli_stmt_fetch($stmt)) { echo $nom . ' ' . $prenom . ' (' . $age . ' ans)' . PHP_EOL; } // Fermeture propre du statement et de la connexion mysqli_stmt_close($stmt); mysqli_close($mysqli); ?>
Cas d'utilisation 2 : Afficher le nom et le prix du produit le plus cher de la table produits dans une catégorie
Approche orientée objet
Exemple : 📋 Copier le code
<?php // Connexion à la base de données $mysqli = new mysqli('localhost', 'root', '', 'teste'); if ($mysqli->connect_error) { die('Erreur : ' . $mysqli->connect_error); } // Requête préparée pour trouver le produit le plus cher d'une catégorie // ORDER BY prix DESC : tri décroissant par prix // LIMIT 1 : ne récupère que le premier résultat (le plus cher) $stmt = $mysqli->prepare('SELECT nom_produit, prix FROM produits WHERE categorie = ? ORDER BY prix DESC LIMIT 1'); // Liaison du paramètre catégorie : 's' pour string (chaîne de caractères) $categorie = 'Électronique'; $stmt->bind_param('s', $categorie); // Exécution de la recherche $stmt->execute(); // Stockage du résultat (1 ligne maximum à cause de LIMIT 1) $stmt->store_result(); // Liaison des colonnes retournées aux variables PHP $stmt->bind_result($nom_produit, $prix); // Vérification qu'au moins un produit a été trouvé if ($stmt->num_rows > 0) { // Récupération de l'unique ligne de résultat $stmt->fetch(); echo "Produit le plus cher : " . $nom_produit . " - Prix : " . $prix . " €" . PHP_EOL; } else { echo "Aucun produit trouvé dans cette catégorie" . PHP_EOL; } // Nettoyage des ressources $stmt->close(); $mysqli->close(); ?>
Approche procédurale
Exemple : 📋 Copier le code
<?php // Établissement de la connexion procédurale $mysqli = mysqli_connect('localhost', 'root', '', 'teste'); if (!$mysqli) { die('Erreur : ' . mysqli_connect_error()); } // Préparation d'une requête de recherche du produit avec le prix maximum // La clause WHERE filtre par catégorie, ORDER BY trie par prix décroissant $stmt = mysqli_prepare($mysqli, 'SELECT nom_produit, prix FROM produits WHERE categorie = ? ORDER BY prix DESC LIMIT 1'); // Association de la catégorie recherchée au placeholder (?) $categorie = 'Électronique'; mysqli_stmt_bind_param($stmt, 's', $categorie); // Lancement de l'exécution SQL mysqli_stmt_execute($stmt); // Récupération et stockage du résultat en mémoire mysqli_stmt_store_result($stmt); // Mapping des colonnes vers les variables PHP mysqli_stmt_bind_result($stmt, $nom_produit, $prix); // Test : est-ce qu'on a trouvé au moins un produit ? if (mysqli_stmt_num_rows($stmt) > 0) { // Chargement de la ligne de résultat dans les variables mysqli_stmt_fetch($stmt); echo "Produit le plus cher : " . $nom_produit . " - Prix : " . $prix . " €" . PHP_EOL; } else { echo "Aucun produit trouvé dans cette catégorie" . PHP_EOL; } // Fermeture du statement et de la connexion mysqli_stmt_close($stmt); mysqli_close($mysqli); ?>
Cas d'utilisation 3 : Vérifier si un utilisateur existe avant d'effectuer une action
Approche orientée objet
Exemple : 📋 Copier le code
<?php // Connexion à la base de données $mysqli = new mysqli('localhost', 'root', '', 'teste'); // Préparation d'une requête de recherche d'utilisateur par email // Cas d'usage typique : vérification d'existence avant inscription ou connexion $stmt = $mysqli->prepare('SELECT id, nom FROM users WHERE email = ?'); // Liaison de l'email à vérifier : 's' pour string $email = 'user@example.com'; $stmt->bind_param('s', $email); // Exécution de la recherche dans la table users $stmt->execute(); // Stockage obligatoire pour pouvoir utiliser num_rows $stmt->store_result(); // Vérification de l'existence : num_rows > 0 signifie que l'email existe if ($stmt->num_rows > 0) { // Liaison des colonnes retournées $stmt->bind_result($id, $nom); // Récupération des données de l'utilisateur trouvé $stmt->fetch(); echo "Utilisateur trouvé : ID = $id, Nom = $nom" . PHP_EOL; } else { // Aucun utilisateur avec cet email : on peut procéder à l'inscription echo "Aucun utilisateur avec cet email" . PHP_EOL; } // Libération des ressources $stmt->close(); $mysqli->close(); ?>
store_result() vs use_result() : Quel choix faire ?
| Critère | store_result() | use_result() |
|---|---|---|
| Chargement mémoire | Charge TOUT en mémoire immédiatement (côté client) | Charge ligne par ligne à la demande (streaming) |
| Performance | Plus rapide pour petits/moyens résultats (< 1000 lignes) | Meilleur pour gros volumes (> 10000 lignes) |
| num_rows disponible | ✅ Oui, immédiatement après store_result() | ❌ Non disponible (pas de comptage préalable) |
| Parcours multiple | ✅ Possible avec data_seek() pour repositionner le curseur | ❌ Une seule passe (pas de retour en arrière) |
| Usage typique | Requêtes standards, pagination, comptage, dashboards | Exports CSV massifs, rapports volumineux, streaming de données |
| Blocage serveur MySQL | ✅ Libère le serveur MySQL immédiatement après stockage | ⚠️ Garde la connexion ouverte pendant tout le parcours |
| Consommation RAM | ⚠️ Proportionnelle au nombre de lignes (peut être élevée) | ✅ Faible et constante (quelques lignes en mémoire) |
Astuces pour choisir l'approche orientée objet ou procédurale
En général, l'approche orientée objet est fortement recommandée pour les raisons suivantes :
- Code plus concis et élégant : Moins de répétition du nom de la ressource ($stmt-> au lieu de mysqli_stmt_*($stmt))
- Meilleure lisibilité et maintenabilité : Structure plus claire, intuitive et proche du langage naturel
- Standard moderne PHP : Suit les conventions actuelles et les meilleures pratiques du développement PHP contemporain
- Moins d'erreurs de frappe : L'autocomplétion des IDE (PHPStorm, VSCode) fonctionne mieux avec les objets
- Facilité de refactoring : Plus simple à modifier et à étendre dans des architectures orientées objet
L'approche procédurale reste pertinente dans ces cas spécifiques :
- Maintenance de code legacy existant écrit en procédural
- Migration progressive depuis les anciennes fonctions mysql_ vers mysqli_
- Scripts simples et autonomes sans architecture orientée objet
- Contraintes de compatibilité avec des frameworks ou CMS anciens
Astuces et bonnes pratiques
- Utilisez store_result() uniquement avec les requêtes préparées - Pour les requêtes simples, mysqli_query() retourne directement le résultat sans besoin de store_result(). Mélanger les deux approches provoque des erreurs fatales
- Avantage principal de store_result() : Permet d'obtenir le nombre exact de lignes avec num_rows avant de parcourir les résultats, ce qui est impossible avec use_result(). Indispensable pour la pagination et les statistiques
- Gestion intelligente de la mémoire : store_result() charge TOUTES les lignes en mémoire RAM. Pour les très gros volumes (> 10 000 lignes), préférez use_result() pour économiser la RAM et éviter les dépassements de memory_limit
- Libération obligatoire des ressources : Toujours fermer le statement avec $stmt->close() ou mysqli_stmt_close($stmt) pour libérer la mémoire et éviter les fuites mémoire (memory leaks)
- Gestion d'erreurs rigoureuse : Vérifiez toujours le succès de prepare() et execute() avant d'appeler store_result(). Une erreur de préparation rend store_result() inutilisable
- Sécurité renforcée : Les requêtes préparées avec bind_param() protègent automatiquement contre les injections SQL, contrairement aux requêtes simples avec concaténation de chaînes. Utilisez TOUJOURS des prepared statements pour les données utilisateur
- Optimisation des performances : Si vous n'avez pas besoin de num_rows ni de parcours multiple, vous pouvez directement utiliser fetch() après execute() sans appeler store_result(), économisant ainsi de la mémoire
- Réutilisation et parcours multiple : Avec store_result(), vous pouvez utiliser data_seek() pour repositionner le curseur et relire les résultats plusieurs fois, utile pour des traitements complexes
- Types de paramètres bind_param() : Mémorisez les types 'i' (integer), 's' (string), 'd' (double/float), 'b' (blob). Un type incorrect peut causer des erreurs subtiles de conversion de données
- Ordre des opérations : Respectez toujours la séquence prepare() → bind_param() → execute() → store_result() → bind_result() → fetch(). Inverser l'ordre provoque des erreurs
Quand utiliser store_result() ?
Utilisez store_result() dans ces situations :
- ✅ Vous avez besoin de connaître le nombre de résultats (num_rows) avant le traitement pour afficher "X résultats trouvés" ou gérer la pagination
- ✅ Vous devez parcourir les résultats plusieurs fois (comparaisons, calculs multiples, affichages différents)
- ✅ Le nombre de lignes est raisonnable (< 10 000 lignes) et votre serveur dispose de RAM suffisante
- ✅ Vous voulez libérer rapidement le serveur MySQL pour d'autres requêtes concurrentes (haute disponibilité)
- ✅ Vous implémentez une pagination qui nécessite le compte total de résultats pour calculer le nombre de pages
- ✅ Vous devez trier ou filtrer les résultats en PHP après récupération (bien que le tri SQL soit préférable)
- ✅ Vous utilisez data_seek() pour repositionner le curseur et relire des données spécifiques
N'utilisez PAS store_result() dans ces cas :
- ❌ Traitement de très gros volumes de données (exports CSV, rapports massifs > 50 000 lignes)
- ❌ Serveur avec peu de mémoire RAM disponible ou memory_limit PHP restrictif
- ❌ Simple parcours unique sans besoin de compter les lignes ni de revenir en arrière
- ❌ Vous utilisez mysqli_query() au lieu de prepared statements (incompatible !)
- ❌ Génération de fichiers volumineux en streaming (logs, dumps, exports)
- ❌ Applications temps réel nécessitant une latence minimale (préférez use_result pour commencer le traitement immédiatement)
Erreurs courantes à éviter
❌ Erreur 1 : Utiliser store_result() avec mysqli_query()
// FAUX - Ne fonctionne pas : mysqli_query retourne déjà un mysqli_result $result = $mysqli->query('SELECT * FROM users'); $result->store_result(); // Fatal Error : Call to undefined method! // CORRECT - Pas besoin de store_result avec query() $result = $mysqli->query('SELECT * FROM users'); while ($row = $result->fetch_assoc()) { echo $row['name']; }
❌ Erreur 2 : Oublier bind_result() après store_result()
// FAUX - Manque bind_result : les variables ne sont pas définies $stmt->execute(); $stmt->store_result(); while ($stmt->fetch()) { echo $name; // Notice: Undefined variable $name! } // CORRECT - bind_result() obligatoire pour associer les colonnes aux variables $stmt->execute(); $stmt->store_result(); $stmt->bind_result($name, $email); // Association explicite while ($stmt->fetch()) { echo $name; // Fonctionne parfaitement }
❌ Erreur 3 : Ne pas vérifier les erreurs de préparation
// FAUX - Pas de vérification : crash si la table n'existe pas $stmt = $mysqli->prepare('SELECT * FROM table_inexistante'); $stmt->execute(); // Fatal Error : Call to a member function on null! // CORRECT - Toujours vérifier le retour de prepare() $stmt = $mysqli->prepare('SELECT * FROM users'); if (!$stmt) { die('Erreur de préparation : ' . $mysqli->error); } $stmt->execute(); // Sécurisé
❌ Erreur 4 : Inverser l'ordre store_result() et bind_result()
// FAUX - bind_result() avant store_result() ne fonctionne pas correctement $stmt->execute(); $stmt->bind_result($name, $email); // Trop tôt ! $stmt->store_result(); // CORRECT - store_result() PUIS bind_result() $stmt->execute(); $stmt->store_result(); // D'abord stocker $stmt->bind_result($name, $email); // Puis lier
❌ Erreur 5 : Oublier de fermer le statement (fuite mémoire)
// FAUX - Pas de fermeture : fuite mémoire sur boucles ou scripts longs $stmt->execute(); $stmt->store_result(); $stmt->bind_result($name); while ($stmt->fetch()) { echo $name; } // Mémoire non libérée ! // CORRECT - Toujours fermer pour libérer les ressources $stmt->execute(); $stmt->store_result(); $stmt->bind_result($name); while ($stmt->fetch()) { echo $name; } $stmt->close(); // Libération mémoire $mysqli->close(); // Fermeture connexion
Conclusion
La fonction store_result() est un outil essentiel et puissant pour travailler avec les requêtes préparées MySQLi en PHP. Elle permet de stocker l'intégralité du résultat en mémoire côté client, offrant ainsi des avantages décisifs comme l'accès immédiat au nombre de lignes (num_rows), la possibilité de parcourir les résultats plusieurs fois avec data_seek(), et la libération rapide du serveur MySQL pour optimiser les performances en environnement concurrent.
Points clés à retenir absolument :
- S'utilise EXCLUSIVEMENT avec les requêtes préparées (prepared statements), jamais avec mysqli_query()
- Charge toutes les données en mémoire immédiatement après execute(), avant tout fetch()
- Permet d'obtenir num_rows avant le parcours des résultats, indispensable pour pagination et statistiques
- Idéale pour des résultats de taille petite à moyenne (< 10 000 lignes) sur serveurs avec RAM suffisante
- Préférez use_result() pour les très gros volumes de données (> 50 000 lignes) afin d'économiser la RAM
- Toujours combiner avec bind_result() pour mapper les colonnes SQL aux variables PHP
- Respecter l'ordre strict : prepare() → bind_param() → execute() → store_result() → bind_result() → fetch()
- Toujours fermer avec close() pour libérer la mémoire et éviter les fuites (memory leaks)
En maîtrisant parfaitement store_result() et les requêtes préparées, vous améliorerez considérablement la sécurité (protection contre les injections SQL), les performances (optimisation mémoire et requêtes), et la robustesse de vos applications PHP avec MySQL. C'est une compétence fondamentale pour tout développeur PHP professionnel travaillant avec des bases de données.
Par carabde | Mis à jour le 16 novembre 2025