Guide de démarrage rapide Ce guide de démarrage rapide va vous aider à vous familiariser avec l'API PHP MySQL. Il fournit une vue d'ensemble de l'extension mysqli. Des exemples de code sont fournis pour tous les aspects importants de l'API. Les concepts de base de données sont expliqués avec une finesse permettant de comprendre les spécificités des concepts de MySQL. Requis : Vous devez être familier avec le langage de programmation PHP, le langage SQL ainsi qu'avoir quelques bases avec le serveur MySQL.
Interface procédurale et orientée objet L'extension mysqli fournit 2 interfaces. Elle supporte la programmation procédurale mais aussi, la programmation orientée objet. Les utilisateurs migrants depuis l'ancienne extension mysql préfèreront l'interface procédurale. Cette interface est similaire à celle utilisée par l'ancienne extension mysql. Dans la plupart des cas, les noms de fonctions ne diffèrent que par leurs préfixes. Quelques fonctions mysqli prennent un gestionnaire de connexion comme premier argument, alors que la fonction correspondante de l'ancienne interface mysql le prenait comme argument optionnel en dernière position. Migration facile depuis l'ancienne extension mysql &example.outputs; L'interface orientée objet En plus de l'interface procédurale, les utilisateurs peuvent choisir d'utiliser l'interface orientée objet. La documentation est organisée en utilisant cette interface. Elle montre les fonctions groupées par leurs buts, rendant simple le démarrage de la programmation. La section référence fournit des exemples sur les deux syntaxes. Il n'y a pas de différence significative d'un point de vue performance entre les deux interfaces. Les utilisateurs peuvent faire leur choix que d'un point de vue personnel. Interface procédurale et orientée objet query("SELECT 'choices to please everybody.' AS _msg FROM DUAL"); $row = $result->fetch_assoc(); echo $row['_msg']; ]]> &example.outputs; L'interface orientée objet est utilisée dans le démarrage rapide de la documentation en raison du fait que la section référence est organisée de cette façon. Mixage des styles Il est possible de passer d'un style à un autre à tout moment bien que ce ne soit pas recommandé pour des raisons de clarté et de style de codage. Mauvais style de codage fetch_assoc()) { echo $row['_msg']; } ]]> &example.outputs; Voir aussi mysqli::__construct mysqli::query mysqli_result::fetch_assoc $mysqli::connect_errno $mysqli::connect_error $mysqli::errno $mysqli::error Le résumé des fonctions de l'extension MySQLi
Connexions Le serveur MySQL supporte l'utilisation de différentes couches de transport pour les connexions. Les connexions peuvent utiliser TCP/IP, les sockets de domaine Unix ou les pipes nommés Windows. Le nom d'hôte localhost a une signification particulière. Il est lié à l'utilisation des sockets de domaine Unix. Pour ouvrir une connexion TCP/IP sur l'hôte local, 127.0.0.1 doit être utilisé au lieu de localhost. Signification spéciale de localhost host_info . "\n"; $mysqli = new mysqli("127.0.0.1", "user", "password", "database", 3306); echo $mysqli->host_info . "\n"; ]]> &example.outputs; Paramètres par défaut d'une connexion Suivant la fonction de connexion utilisée, des paramètres peuvent être omis. Si un paramètre n'est pas fourni, alors l'extension tentera d'utiliser les valeurs par défaut définies dans le fichier de configuration de PHP. Paramètres par défaut Ces valeurs de paramètres sont alors passées à la bibliothèque cliente utilisée par l'extension. Si la bibliothèque cliente détecte un paramètre vide ou non défini, alors elle utilisera les valeurs par défaut internes à la bibliothèque. Valeurs par défaut internes à la bibliothèque pour la connexion Si la valeur de l'hôte n'est pas définie ou est vide, alors la bibliothèque cliente utilisera par défaut une connexion de type socket Unix sur localhost. Si le socket n'est pas défini ou vide, et qu'une connexion de type socket Unix est demandée, alors une connexion au socket par défaut /tmp/mysql.sock sera tentée. Sous les systèmes Windows, le nom d'hôte . est interprété par la bibliothèque cliente comme une tentative d'ouvrir un tube nommé Windows pour la connexion. Dans ce cas, le paramètre socket est interprété comme un tube nommé. S'il n'est pas fourni ou vide, alors le socket (tube nommé) vaudra par défaut \\.\pipe\MySQL. Si ni un socket de domaine Unix, ni un tube nommé Windows n'est fourni, une connexion de base sera établie et si la valeur du port n'est pas défini, la bibliothèque utilisera le port 3306. La bibliothèque mysqlnd et la bibliothèque cliente MySQL (libmysqlclient) implémentent la même logique pour déterminer les valeurs par défaut. Options de connexion Des options de connexion sont disponibles pour, par exemple, définir des commandes d'initialisation à exécuter lors de la connexion, ou pour demander l'utilisation d'un jeu de caractères particulier. Les options de connexion doivent être définies avant la connexion au réseau. Pour définir une option de connexion, l'opération de connexion doit être effectuée en 3 étapes : création d'un gestionnaire de connexion avec mysqli_init ou mysqli::__construct, définition des options demandées en utilisant mysqli::options, et connexion au réseau avec mysqli::real_connect. File d'attente de connexion L'extension mysqli supporte les connexions persistantes au base de données, qui sont des connexions spéciales. Par défaut, chaque connexion à une base de données ouverte par un script est soit explicitement close par l'utilisateur durant l'exécution, ou soit libérée automatiquement à la fin du script. Ce n'est pas le cas d'une connexion persistante. En effet, elle sera placée dans une file d'attente pour une ré-utilisation future, si une connexion au même serveur, utilisant le même nom d'utilisateur, le même mot de passe, le même socket, le même port, ainsi que la même base de données est ouverte. Cette ré-utilisation permet d'alléger la charge indue par les connexions. Chaque processus PHP utilise sa propre file d'attente de connexions mysqli. Suivant le modèle de déploiement du serveur web, un processus PHP peut servir une ou plusieurs requêtes. Toutefois, une connexion mise en file d'attente peut être utilisée par un ou plusieurs scripts par la suite. Les connexions persistantes Si une connexion persistante pour une combinaison d'hôte, de nom d'utilisateur, de mot de passe, de socket, de port et de base de données inutilisée ne peut être trouvée dans la file d'attente de connexion, alors mysqli ouvrira une nouvelle connexion. L'utilisation des connexions persistantes peut être activée ou désactivée en utilisant la directive PHP mysqli.allow_persistent. Le nombre total de connexions ouvertes par un script peut être limité avec la directive mysqli.max_links. Le nombre maximal de connexions persistantes par processus PHP peut être restreint avec la directive mysqli.max_persistent. Veuillez noter que le serveur web peut engendrer plusieurs processus PHP. Une plainte courante contre les connexions persistantes est que leurs statuts n'est pas ré-initialisés avant la ré-utilisation. Par exemple, les transactions ouvertes et non terminées ne sont pas automatiquement annulées. Mais aussi, les modifications autorisées survenant entre le moment où la connexion est mise en file d'attente et sa ré-utilisation ne seront pas prises en compte. Ce comportement peut être vu comme un effet de bord non désiré. Au contraire, le nom persistent peut être compris comme une promesse sur le fait que le statut persiste réellement. L'extension mysqli supporte deux interprétations d'une connexion persistante : statut persistant, et un statut réinitialisé avant ré-utilisation. Par défaut, il sera réinitialisé. Avant qu'une connexion persistante ne soit réutilisée, l'extension mysqli appelle implicitement la fonction mysqli::change_user pour réinitialiser le statut. La connexion persistante apparaît à l'utilisateur comme si elle venait juste d'être ouverte. Aucune trace d'une utilisation précédente ne sera visible. La fonction mysqli::change_user est une opération couteuse. Pour de meilleures performances, les utilisateurs peuvent vouloir re-compiler l'extension avec le drapeau de compilation MYSQLI_NO_CHANGE_USER_ON_PCONNECT. Ainsi, il sera laissé à l'utilisateur le choix entre un comportement sécurisé et une performance optimisée. Les deux ont comme but l'optimisation. Pour une utilisation plus simple, le comportement sécurisé a été placé par défaut au détriment d'une performance maximale. Voir aussi mysqli_init mysqli::__construct mysqli::options mysqli::real_connect mysqli::change_user $mysqli::host_info Les options de configuration MySQLi Les connexions persistantes aux bases de données
Exécution des requêtes Les requêtes peuvent être exécutées avec les fonctions mysqli::query, mysqli::real_query et mysqli::multi_query.. La fonction mysqli::query est la plus commune, et combine l'exécution de la requête avec une récupération de son jeu de résultats en mémoire tampon, s'il y en a un, en un seul appel. Appeler la fonction mysqli::query est identique à appeler la fonction mysqli::real_querysuivie d'un appel à la fonction mysqli::store_result. Exécution des requêtes query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT)"); ]]> Jeux de résultats en mémoire tampon Après exécution de la requête, les résultats peuvent être intégralement récupérés en une fois ou bien être lus ligne par ligne. La mise en mémoire tampon du jeu de résultats côté client autorise le serveur à libérer les ressources associées avec le résultat de la requête aussi vite que possible. De manière générale, les clients sont lents à parcourir les jeux de résultats. Toutefois, il est recommandéd'utiliser la mise en mémoire tampon des jeux de résultats. La fonction mysqli::query combine à la fois l'exécution de la requête et la mise en mémoire tampon du jeu de résultats. Les applications PHP peuvent naviguer librement dans les résultats mis en mémoire tampon. La navigation est rapide car les jeux de résultats sont stockés dans la mémoire client. Veuillez garder à l'esprit qu'il est souvent plus simple de réaliser cette opération par le client que par le serveur. Navigation dans des résultats mis en mémoire tampon query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT)"); $mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)"); $result = $mysqli->query("SELECT id FROM test ORDER BY id ASC"); echo "Ordre inversé...\n"; for ($row_no = $result->num_rows - 1; $row_no >= 0; $row_no--) { $result->data_seek($row_no); $row = $result->fetch_assoc(); echo " id = " . $row['id'] . "\n"; } echo "Ordre du jeu de résultats...\n"; foreach ($result as $row) { echo " id = " . $row['id'] . "\n"; } ]]> &example.outputs; Jeux de résultats non mis en mémoire tampon Si la mémoire client est une ressource limitée, et que la libération des ressources serveur aussi vite que possible pour conserver une charge serveur basse n'est pas nécessaire, les résultats non mis en mémoire tampon peuvent être utilisés. La navigation au travers de résultats non mis en mémoire tampon n'est pas possible tant que toutes les lignes n'ont pas été lues. Navigation dans des résultats non mis en mémoire tampon real_query("SELECT id FROM test ORDER BY id ASC"); $result = $mysqli->use_result(); echo "Ordre du jeu de résultats...\n"; foreach ($result as $row) { echo " id = " . $row['id'] . "\n"; } ]]> Types de données des valeurs du jeu de résultats Les fonctions mysqli::query, mysqli::real_query et mysqli::multi_query sont utilisées pour exécuter des requêtes non-préparées. Au niveau du protocole client-serveur MySQL, la commande COM_QUERY ainsi que le protocole texte sont utilisés pour l'exécution de la requête. Avec le protocole texte, le serveur MySQL convertit toutes les données d'un jeu de résultats en chaînes de caractères avant de les envoyer. La bibliothèque cliente mysql reçoit toutes les valeurs des colonnes sous forme de chaîne de caractères. Aucun autre transtypage côté client n'est effectué pour retrouver le type natif des colonnes. A la place de cela, toutes les valeurs sont fournis sous la forme de chaîne de caractères PHP. Le protocole texte retourne des chaînes de caractères par défaut query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT, label CHAR(1))"); $mysqli->query("INSERT INTO test(id, label) VALUES (1, 'a')"); $result = $mysqli->query("SELECT id, label FROM test WHERE id = 1"); $row = $result->fetch_assoc(); printf("id = %s (%s)\n", $row['id'], gettype($row['id'])); printf("label = %s (%s)\n", $row['label'], gettype($row['label'])); ]]> &example.outputs; Il est possible de convertir des colonnes de type entières et nombres à virgule flottante en nombre PHP en définissant l'option de connexion MYSQLI_OPT_INT_AND_FLOAT_NATIVE, si vous utilisez la bibliothèque mysqlnd. Si défini, la bibliothèque mysqlnd va vérifier les méta-données des types des colonnes dans le jeu de résultats et va convertir les colonnes SQL numériques en nombres PHP, si la valeur entre dans l'intervalle autorisé de PHP. De cette façon, par exemple, les colonnes SQL INT sont retournées sous la forme d'entier. Types natifs des données avec mysqlnd et l'option de connexion options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1); $mysqli->real_connect("example.com", "user", "password", "database"); $mysqli->query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT, label CHAR(1))"); $mysqli->query("INSERT INTO test(id, label) VALUES (1, 'a')"); $result = $mysqli->query("SELECT id, label FROM test WHERE id = 1"); $row = $result->fetch_assoc(); printf("id = %s (%s)\n", $row['id'], gettype($row['id'])); printf("label = %s (%s)\n", $row['label'], gettype($row['label'])); ]]> &example.outputs; Voir aussi mysqli::__construct mysqli::options mysqli::real_connect mysqli::query mysqli::multi_query mysqli::use_result mysqli::store_result
Les requêtes préparées La base de données MySQL supporte les requêtes préparées. Une requête préparée ou requête paramétrable est utilisée pour exécuter la même requête plusieurs fois, avec une grande efficacité et protège des injections SQL. Flux de travail de base L'exécution d'une requête préparée se déroule en deux étapes : la préparation et l'exécution. Lors de la préparation, un template de requête est envoyé au serveur de base de données. Le serveur effectue une vérification de la syntaxe, et initialise les ressources internes du serveur pour une utilisation ultérieure. Le serveur MySQL supporte le mode anonyme, avec des marqueurs de position utilisant le caractère ?. La préparation est suivie de l'exécution. Pendant l'exécution, le client lie les valeurs des paramètres et les envoie au serveur. Le serveur exécute l'instruction avec les valeurs liées en utilisant les ressources internes précédemment créées. Première étape : la préparation query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT, label TEXT)"); // Requête préparée, étape 1 : préparation $stmt = $mysqli->prepare("INSERT INTO test(id, label) VALUES (?, ?)"); // Requête préparée, étape 2 : lie les valeurs et exécute la requête $id = 1; $label = 'PHP'; $stmt->bind_param("is", $id, $label); // "is" means that $id is bound as an integer and $label as a string $stmt->execute(); ]]> Exécution répétée Une requête préparée peut être exécutée à plusieurs reprises. A chaque exécution, la valeur courante de la variable liée est évaluée, et envoyée au serveur. La requête n'est pas analysée de nouveau. Le template de requête n'est pas une nouvelle fois envoyée au serveur non plus. Requête de type INSERT préparée une seule fois, et exécutée plusieurs fois query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT, label TEXT)"); // Requête préparée, étape 1 : la préparation if (!($stmt = $mysqli->prepare("INSERT INTO test(id) VALUES (?)"))) { echo "Échec lors de la préparation : (" . $mysqli->errno . ") " . $mysqli->error; } // Requête préparée, étape 2 : lie les valeurs et exécute la requête $id = 1; $stmt->bind_param("is", $id, $label); // "is" means that $id is bound as an integer and $label as a string $data = [ 1 => 'PHP', 2 => 'Java', 3 => 'C++' ]; foreach ($data as $id => $label) { $stmt->execute(); } $result = $mysqli->query('SELECT id, label FROM test'); var_dump($result->fetch_all(MYSQLI_ASSOC)); ]]> &example.outputs; string(1) "1" ["label"]=> string(3) "PHP" } [1]=> array(2) { ["id"]=> string(1) "2" ["label"]=> string(4) "Java" } [2]=> array(2) { ["id"]=> string(1) "3" ["label"]=> string(3) "C++" } } ]]> Chaque requête préparée occupe des ressources sur le serveur. Elles doivent être fermées explicitement immédiatement après utilisation. Si vous ne le faîtes pas, la requête sera fermée lorsque le gestionnaire de requête sera libéré par PHP. L'utilisation de requête préparée n'est pas toujours la façon la plus efficace d'exécuter une requête. Une requête préparée exécutée une seule fois provoque plus d'aller-retour client-serveur qu'une requête non préparée. C'est pour cela que la requête de type SELECT n'est pas exécutée comme requête préparée dans l'exemple ci-dessus. De plus, vous devez prendre en considération l'utilisation des syntaxes multi-INSERT MySQL pour les INSERTs. Par exemple, les multi-INSERTs requièrent moins d'aller-retour client-serveur que la requête préparée vue dans l'exemple ci-dessus. Moins d'aller-retour en utilisant les multi-INSERTs SQL query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT)"); $values = [1, 2, 3, 4]; $stmt = $mysqli->prepare("INSERT INTO test(id) VALUES (?), (?), (?), (?)"); $stmt->bind_param('iiii', ...$values); $stmt->execute(); ]]> Types de données des valeurs du jeu de résultats Le protocole serveur client MySQL définit un protocole de transfert des données différent pour les requêtes préparées et pour les requêtes non préparées. Les requêtes préparées utilisent un protocole appelé binaire. Le serveur MySQL envoie les données du jeu de résultats "tel que", au format binaire. Les résultats ne sont pas sérialisés en chaînes de caractères avant envoi. La bibliothèque cliente reçoit des données binaires et tente de convertir les valeurs en un type de données PHP approprié. Par exemple, les résultats depuis une colonne INT SQL seront fournis comme variables de type entier PHP. Types de données natifs query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT, label TEXT)"); $mysqli->query("INSERT INTO test(id, label) VALUES (1, 'PHP')"); $stmt = $mysqli->prepare("SELECT id, label FROM test WHERE id = 1"); $stmt->execute(); $result = $stmt->get_result(); $row = $result->fetch_assoc(); printf("id = %s (%s)\n", $row['id'], gettype($row['id'])); printf("label = %s (%s)\n", $row['label'], gettype($row['label'])); ]]> &example.outputs; Ce comportement diffère pour les requêtes non préparées. Par défaut, les requêtes non préparées retournent tous les résultats sous forme de chaînes de caractères. Ce comportement par défaut peut être modifié en utilisant une option lors de la connexion. Si cette option est utilisée, alors il n'y aura plus de différence entre une requête préparée et une requête non préparée. Récupération des résultats en utilisant des variables liées Les résultats depuis les requêtes préparées peuvent être récupérées en liant les variables de sortie, ou en interrogeant l'objet mysqli_result. Les variables de sortie doivent être liées après l'exécution de la requête. Une variable doit être liée pour chaque colonne du jeu de résultats de la requête. Liage des variables de sortie query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT, label TEXT)"); $mysqli->query("INSERT INTO test(id, label) VALUES (1, 'PHP')"); $stmt = $mysqli->prepare("SELECT id, label FROM test WHERE id = 1"); $stmt->execute(); $stmt->bind_result($out_id, $out_label); $out_id = NULL; $out_label = NULL; if (!$stmt->bind_result($out_id, $out_label)) { echo "Échec lors du liage des paramètres de sortie : (" . $stmt->errno . ") " . $stmt->error; } while ($stmt->fetch()) { printf("id = %s (%s), label = %s (%s)\n", $out_id, gettype($out_id), $out_label, gettype($out_label)); } ]]> &example.outputs; Les requêtes préparées retournent des jeux de résultats non mis en mémoire tampon par défaut. Les résultats de la requête ne sont pas implicitement récupérés et transférés depuis le serveur vers le client pour une mise en mémoire tampon côté client. Le jeu de résultats prend des ressources serveur tant que tous les résultats n'ont pas été récupérés par le client. Aussi, il est recommandé de les récupérer rapidement. Si un client échoue dans la récupération de tous les résultats, ou si le client ferme la requête avant d'avoir récupéré toutes les données, les données doivent être récupérées implicitement par mysqli. Il est également possible de mettre en mémoire tampon les résultats d'une requête préparée en utilisant la fonction mysqli_stmt::store_result. Récupération des résultats en utilisant l'interface mysqli_result Au lieu d'utiliser des résultats liés, les résultats peuvent aussi être récupérées via l'interface mysqli_result. La fonction mysqli_stmt::get_result retourne un jeu de résultats mis en mémoire tampon. Utilisation de mysqli_result pour récupérer les résultats query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT, label TEXT)"); $mysqli->query("INSERT INTO test(id, label) VALUES (1, 'PHP')"); $stmt = $mysqli->prepare("SELECT id, label FROM test WHERE id = 1"); $stmt->execute(); $result = $stmt->get_result(); var_dump($result->fetch_all(MYSQLI_ASSOC)); ]]> &example.outputs; array(2) { ["id"]=> int(1) ["label"]=> string(3) "PHP" } } ]]> L'utilisation de l'interface mysqli_result offre d'autres avantages d'un point de vue flexibilité dans la navigation dans le jeu de résultats côté client. Jeu de résultats mis en mémoire tampon pour plus de flexibilité dans la lecture query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT, label TEXT)"); $mysqli->query("INSERT INTO test(id, label) VALUES (1, 'PHP'), (2, 'Java'), (3, 'C++')"); $stmt = $mysqli->prepare("SELECT id, label FROM test"); $stmt->execute(); $result = $stmt->get_result(); for ($row_no = $result->num_rows - 1; $row_no >= 0; $row_no--) { $result->data_seek($row_no); var_dump($result->fetch_assoc()); } ]]> &example.outputs; int(3) ["label"]=> string(1) "C++" } array(2) { ["id"]=> int(2) ["label"]=> string(1) "Java" } array(2) { ["id"]=> int(1) ["label"]=> string(1) "PHP" } ]]> Échappement et injection SQL Les variables liées sont envoyées au serveur séparément de la requête, ne pouvant ainsi pas interférer avec celle-ci. Le serveur utilise ces valeurs directement au moment de l'exécution, après que le template ne soit analysé. Les paramètres liés n'ont pas besoin d'être échappés sachant qu'ils ne sont jamais placés dans la chaîne de requête directement. Une astuce doit être fournie au serveur pour spécifier le type de variable liée, afin d'effectuer la conversion appropriée. Voir la fonction mysqli_stmt::bind_param pour plus d'informations. Une telle séparation est souvent considérée comme la seule fonctionnalité pour se protéger des injections SQL, mais le même degré de sécurité peut être atteint avec les requêtes non-préparées, si toutes les valeurs sont correctement formatées. Notez qu'un formattage correct n'est pas la même chose qu'un échappement et nécessite plus de logique qu'un simple échappement. Aussi, les requêtes préparées sont simplement une méthode plus simple et moins prompte aux erreurs concernant cette approche sécuritaire. Émulation côté client de la préparation d'une requête L'API n'inclut pas d'émulation côté client de la préparation d'une requête. Voir aussi mysqli::__construct mysqli::query mysqli::prepare mysqli_stmt::prepare mysqli_stmt::execute mysqli_stmt::bind_param mysqli_stmt::bind_result
Les procédures stockées La base de données MySQL supporte les procédures stockées. Une procédure stockée est une sous routine stockée dans le catalogue de la base de données. Les applications peuvent appeler et exécuter une procédure stockée. La requête SQL CALL est utilisée pour exécuter une procédure stockée. Paramètre Les procédures stockées peuvent avoir des paramètres IN, INOUT and OUT, suivant la version de MySQL. L'interface mysqli n'a pas de notion spécifique des différents types de paramètres. Paramètre IN Les paramètres d'entrée sont fournis avec la requête CALL. Assurez-vous d'échapper correctement les valeurs. Appel d'une procédure stockée query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT)"); $mysqli->query("DROP PROCEDURE IF EXISTS p"); $mysqli->query("CREATE PROCEDURE p(IN id_val INT) BEGIN INSERT INTO test(id) VALUES(id_val); END;"); $mysqli->query("CALL p(1)"); $result = $mysqli->query("SELECT id FROM test"); var_dump($result->fetch_assoc()); ]]> &example.outputs; string(1) "1" } ]]> Paramètre INOUT/OUT Les valeurs des paramètres INOUT/OUT sont accédées en utilisant les variables de session. Utilisation des variables de session query("DROP PROCEDURE IF EXISTS p"); $mysqli->query('CREATE PROCEDURE p(OUT msg VARCHAR(50)) BEGIN SELECT "Hi!" INTO msg; END;'); $mysqli->query("SET @msg = ''"); $mysqli->query("CALL p(@msg)"); $result = $mysqli->query("SELECT @msg as _p_out"); $row = $result->fetch_assoc(); echo $row['_p_out']; ]]> &example.outputs; Les développeurs d'application et de framework peuvent fournir une API plus conviviale utilisant un mix des variables de session et une inspection du catalogue de la base de données. Cependant, veuillez garder à l'esprit l'impact sur les performances dû à une solution personnalisée basée sur l'inspection du catalogue. Gestion des jeux de résultats Les procédures stockées peuvent retourner des jeux de résultats. Les jeux de résultats retournés depuis une procédure stockée ne peuvent être récupérés correctement en utilisant la fonction mysqli::query. La fonction mysqli::query combine l'exécution de la requête et la récupération du premier jeu de résultats dans un jeu de résultats mis en mémoire tampon, s'il y en a. Cependant, il existe d'autres jeux de résultats issus de la procédure stockée qui sont cachés de l'utilisateur et qui font que la fonction mysqli::query échoue lors de la récupération des jeux de résultats attendus de l'utilisateur. Les jeux de résultats retournés depuis une procédure stockée sont récupérés en utilisant la fonction mysqli::real_query ou mysqli::multi_query. Ces deux fonctions autorisent la récupération de n'importe quel nombre de jeux de résultats retournés par une requête, comme la requête CALL. L'échec dans la récupération de tous les jeux de résultats retournés par une procédure stockée cause une erreur. Récupération des résultats issus d'une procédure stockée query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT)"); $mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)"); $mysqli->query("DROP PROCEDURE IF EXISTS p"); $mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;'); $mysqli->multi_query("CALL p()"); do { if ($res = $mysqli->store_result()) { var_dump($result->fetch_all()); $result->free(); } } while ($mysqli->next_result()); ]]> &example.outputs; array(1) { [0]=> string(1) "1" } [1]=> array(1) { [0]=> string(1) "2" } [2]=> array(1) { [0]=> string(1) "3" } } --- array(3) { [0]=> array(1) { [0]=> string(1) "2" } [1]=> array(1) { [0]=> string(1) "3" } [2]=> array(1) { [0]=> string(1) "4" } } ]]> Utilisation des requêtes préparées Aucune gestion spéciale n'est requise lors de l'utilisation de l'interface de préparation des requêtes pour récupérer les résultats depuis la même procédure stockée que celle ci-dessous. Les interfaces de requête préparée et non préparée sont similaires. Veuillez noter que toutes les versions du serveur MySQL ne supporte pas la préparation des requêtes SQL CALL. Procédures stockées et requête préparée connect_errno) { echo "Échec lors de la connexion à MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } $mysqli->query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT)"); $mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)"); $mysqli->query("DROP PROCEDURE IF EXISTS p"); $mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;'); $stmt = $mysqli->prepare("CALL p()"); if (!$stmt->execute()) { echo "Échec lors de l'exécution : (" . $stmt->errno . ") " . $stmt->error; } do { if ($result = $stmt->get_result()) { var_dump($result->fetch_all()); $result->free(); } } while ($stmt->next_results()); ]]> &example.outputs; array(1) { [0]=> int(1) } [1]=> array(1) { [0]=> int(2) } [2]=> array(1) { [0]=> int(3) } } --- array(3) { [0]=> array(1) { [0]=> int(2) } [1]=> array(1) { [0]=> int(3) } [2]=> array(1) { [0]=> int(4) } } ]]> Bien sûr, l'utilisation de l'API de liage pour la récupération est également supportée. Procédures stockées et requête préparée en utilisant l'API de liage query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT)"); $mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)"); $mysqli->query("DROP PROCEDURE IF EXISTS p"); $mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;'); $stmt = $mysqli->prepare("CALL p()"); $stmt->execute(); do { if ($stmt->store_result()) { $stmt->bind_result($id_out); while ($stmt->fetch()) { echo "id = $id_out\n"; } } } while ($stmt->next_result()); ]]> &example.outputs; Voir aussi mysqli::query mysqli::multi_query mysqli::next_result mysqli::more_results
Requêtes multiples MySQL autorise optionnellement le fait d'avoir plusieurs requêtes dans une seule chaîne de requête mais nécessite une gestion spéciale. Les requêtes multiples ou multirequêtes doivent être exécutées avec la fonction mysqli::multi_query. Les requêtes individuelles dans la chaîne de requête sont séparées par un point virgule. Ensuite, tous les jeux de résultats retournés par l'exécution des requêtes doivent être récupérés. Le serveur MySQL autorise d'avoir des requêtes qui retournent des jeux de résultats ainsi que des requêtes qui ne retournent aucun jeu de résultats dans la même requête multiple. Requêtes multiples query("DROP TABLE IF EXISTS test"); $mysqli->query("CREATE TABLE test(id INT)"); $sql = "SELECT COUNT(*) AS _num FROM test; INSERT INTO test(id) VALUES (1); SELECT COUNT(*) AS _num FROM test; "; $mysqli->multi_query($sql); do { if ($result = $mysqli->store_result()) { var_dump($result->fetch_all(MYSQLI_ASSOC)); $result->free(); } } while ($mysqli->next_result()); ]]> &example.outputs; array(1) { ["_num"]=> string(1) "0" } } array(1) { [0]=> array(1) { ["_num"]=> string(1) "1" } } ]]> D'un point de vue de la sécurité Les fonctions mysqli::query et mysqli::real_query de l'API ne définissent pas de drapeau de connexion nécessaire pour l'activation des multirequêtes sur le serveur. Un appel supplémentaire à l'API est utilisé pour les multirequêtes pour réduire la probabilité d'injection SQL accidentelle. Un attaquant peut tenter d'ajouter des requêtes comme ; DROP DATABASE mysql ou ; SELECT SLEEP(999). Si l'attaquant arrive à ajouter ce genre de SQL dans la chaîne de requête mais que mysqli::multi_query n'est pas utilisé, le serveur n'excutera que la première requête, mais pas la seconde représentant la requête SQL malicieuse. Injection SQL query("SELECT 1; DROP TABLE mysql.user"); ]]> &example.outputs; Prepared statements L'utilisation des requêtes multiples avec des requêtes préparées n'est pas supportée. Voir aussi mysqli::query mysqli::multi_query mysqli_result::next-result mysqli_result::more-results
Support API pour les transactions Le serveur MySQL supporte les transactions suivant le moteur de stockage utilisé. Depuis MySQL 5.5, le moteur de stockage par défaut est InnoDB. InnoDB a un support complet des transactions ACID. Les transactions peuvent soit être contrôlées en utilisant SQL, soit par des appels API. Il est recommandé d'utiliser les appels API pour activer ou désactiver le mode autocommit et pour valider et annuler les transactions. Définir le mode <literal>autocommit</literal> via SQL ou via l'API autocommit(false); // Ne sera pas monitoré et reconnu par le plugin de réplication et de balance de charge $mysqli->query('SET AUTOCOMMIT = 0'); ]]> Les paquets de fonctionnalités additionnelles, comme les plugins de réplication et de balance de charge peuvent monitorer les appels API. Le plugin de réplication offre une sécurité sur les transactions lors de la balance de charge, si les transactions sont contrôlées avec des appels API. La sécurité des transactions lors de la balance de charge n'est pas disponible si les requêtes SQL sont utilisées pour définir le mode autocommit, pour valider ou annuler une transaction. Validation et annulation d'une transaction autocommit(false); $mysqli->query("INSERT INTO test(id) VALUES (1)"); $mysqli->rollback(); $mysqli->query("INSERT INTO test(id) VALUES (2)"); $mysqli->commit(); ]]> Veuillez noter que le serveur MySQL ne peut pas annuler toutes les requêtes. Quelques requêtes requièrent une validation implicite. Voir aussi mysqli::autocommit mysqli::begin_transaction mysqli::commit mysqli::rollback
Les méta-données Un jeu de résultats MySQL contient des méta-données. Elles décrivent les colonnes trouvées dans le jeu de résultats. Toutes les méta-données envoyées par MySQL sont accessible via l'interface mysqli. L'extension n'effectue que très peu (voire, pas du tout) de modification sur les informations qu'elle reçoit. Les différences entre les versions MySQL ne sont pas identiques. Les méta-données peuvent être consultées via l'interface mysqli_result. Accès aux méta-données du jeu de résultats connect_errno) { echo "Échec lors de la connexion à MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } $res = $mysqli->query("SELECT 1 AS _one, 'Hello' AS _two FROM DUAL"); var_dump($res->fetch_fields()); ]]> &example.outputs; object(stdClass)#3 (13) { ["name"]=> string(4) "_one" ["orgname"]=> string(0) "" ["table"]=> string(0) "" ["orgtable"]=> string(0) "" ["def"]=> string(0) "" ["db"]=> string(0) "" ["catalog"]=> string(3) "def" ["max_length"]=> int(1) ["length"]=> int(1) ["charsetnr"]=> int(63) ["flags"]=> int(32897) ["type"]=> int(8) ["decimals"]=> int(0) } [1]=> object(stdClass)#4 (13) { ["name"]=> string(4) "_two" ["orgname"]=> string(0) "" ["table"]=> string(0) "" ["orgtable"]=> string(0) "" ["def"]=> string(0) "" ["db"]=> string(0) "" ["catalog"]=> string(3) "def" ["max_length"]=> int(5) ["length"]=> int(5) ["charsetnr"]=> int(8) ["flags"]=> int(1) ["type"]=> int(253) ["decimals"]=> int(31) } } ]]> Requêtes préparées Les méta-données des jeux de résultats créés en utilisant des requêtes préparées sont accessibles de la même façon. Un gestionnaire mysqli_result utilisable est retourné par la fonction mysqli_stmt::result_metadata. Méta-données via des requêtes préparées prepare("SELECT 1 AS _one, 'Hello' AS _two FROM DUAL"); $stmt->execute(); $result = $stmt->result_metadata(); var_dump($result->fetch_fields()); ]]> Voir aussi mysqli::query mysqli_result::fetch_fields