-
Notifications
You must be signed in to change notification settings - Fork 157
/
Copy pathquickstart.xml
1692 lines (1576 loc) · 55 KB
/
quickstart.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 60a292997b3c6341cf52099d901aa0b5f8673d87 Maintainer: yannick Status: ready -->
<!-- Reviewed: no -->
<chapter xml:id="mysqli.quickstart" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Guide de démarrage rapide</title>
<para>
Ce guide de démarrage rapide va vous aider à vous familiariser
avec l'API PHP MySQL.
</para>
<para>
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.
</para>
<para>
Requis : Vous devez être familier avec le langage de programmation PHP,
le langage SQL ainsi qu'avoir quelques bases avec le serveur MySQL.
</para>
<section xml:id="mysqli.quickstart.dual-interface">
<title>Interface procédurale et orientée objet</title>
<para>
L'extension mysqli fournit 2 interfaces. Elle supporte la programmation
procédurale mais aussi, la programmation orientée objet.
</para>
<para>
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.
</para>
<para>
<example>
<title>Migration facile depuis l'ancienne extension mysql</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = mysqli_connect("example.com", "user", "password", "database");
$result = mysqli_query($mysqli, "SELECT 'Please do not use the deprecated mysql extension for new development. ' AS _msg FROM DUAL");
$row = mysqli_fetch_assoc($result);
echo $row['_msg'];
$mysql = mysql_connect("example.com", "user", "password");
mysql_select_db("test");
$result = mysql_query("SELECT 'Use the mysqli extension instead.' AS _msg FROM DUAL", $mysql);
$row = mysql_fetch_assoc($result);
echo $row['_msg'];
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Please do not use the deprecated mysql extension for new development. Use the mysqli extension instead.
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">L'interface orientée objet</emphasis>
</para>
<para>
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.
</para>
<para>
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.
</para>
<para>
<example>
<title>Interface procédurale et orientée objet</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = mysqli_connect("example.com", "user", "password", "database");
$result = mysqli_query($mysqli, "SELECT 'A world full of ' AS _msg FROM DUAL");
$row = mysqli_fetch_assoc($result);
echo $row['_msg'];
$mysqli = new mysqli("example.com", "user", "password", "database");
$result = $mysqli->query("SELECT 'choices to please everybody.' AS _msg FROM DUAL");
$row = $result->fetch_assoc();
echo $row['_msg'];
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
A world full of choices to please everybody.
]]>
</screen>
</example>
</para>
<para>
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.
</para>
<para>
<emphasis role="bold">Mixage des styles</emphasis>
</para>
<para>
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.
</para>
<para>
<example>
<title>Mauvais style de codage</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = new mysqli("example.com", "user", "password", "database");
$result = mysqli_query($mysqli, "SELECT 'Possible but bad style.' AS _msg FROM DUAL");
if ($row = $result->fetch_assoc()) {
echo $row['_msg'];
}
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Possible but bad style.
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">Voir aussi</emphasis>
</para>
<para>
<simplelist>
<member><methodname>mysqli::__construct</methodname></member>
<member><methodname>mysqli::query</methodname></member>
<member><methodname>mysqli_result::fetch_assoc</methodname></member>
<member><link linkend="mysqli.connect-errno">$mysqli::connect_errno</link></member>
<member><link linkend="mysqli.connect-error">$mysqli::connect_error</link></member>
<member><link linkend="mysqli.errno">$mysqli::errno</link></member>
<member><link linkend="mysqli.error">$mysqli::error</link></member>
<member><link linkend="mysqli.summary">Le résumé des fonctions de l'extension MySQLi</link></member>
</simplelist>
</para>
</section>
<section xml:id="mysqli.quickstart.connections">
<title>Connexions</title>
<para>
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.
</para>
<para>
Le nom d'hôte <literal>localhost</literal> 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, <literal>127.0.0.1</literal> doit être utilisé
au lieu de <literal>localhost</literal>.
</para>
<para>
<example>
<title>Signification spéciale de localhost</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = new mysqli("localhost", "user", "password", "database");
echo $mysqli->host_info . "\n";
$mysqli = new mysqli("127.0.0.1", "user", "password", "database", 3306);
echo $mysqli->host_info . "\n";
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Localhost via UNIX socket
127.0.0.1 via TCP/IP
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">Paramètres par défaut d'une connexion</emphasis>
</para>
<para>
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.
</para>
<para>
<example>
<title>Paramètres par défaut</title>
<programlisting role="ini">
<![CDATA[
mysqli.default_host=192.168.2.27
mysqli.default_user=root
mysqli.default_pw=""
mysqli.default_port=3306
mysqli.default_socket=/tmp/mysql.sock
]]>
</programlisting>
</example>
</para>
<para>
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.
</para>
<para>
<emphasis role="bold">Valeurs par défaut internes à la bibliothèque pour la connexion</emphasis>
</para>
<para>
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 <literal>localhost</literal>.
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 <literal>/tmp/mysql.sock</literal>
sera tentée.
</para>
<para>
Sous les systèmes Windows, le nom d'hôte <literal>.</literal> 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 <literal>\\.\pipe\MySQL</literal>.
</para>
<para>
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 <literal>3306</literal>.
</para>
<para>
La bibliothèque <link linkend="mysqlnd.overview">mysqlnd</link> et la bibliothèque
cliente MySQL (libmysqlclient) implémentent la même logique pour déterminer les valeurs
par défaut.
</para>
<para>
<emphasis role="bold">Options de connexion</emphasis>
</para>
<para>
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.
</para>
<para>
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
<function>mysqli_init</function> ou <methodname>mysqli::__construct</methodname>,
définition des options demandées en utilisant
<methodname>mysqli::options</methodname>, et connexion au réseau avec
<methodname>mysqli::real_connect</methodname>.
</para>
<para>
<emphasis role="bold">File d'attente de connexion</emphasis>
</para>
<para>
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.
</para>
<para>
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.
</para>
<para>
<emphasis role="bold">Les connexions persistantes</emphasis>
</para>
<para>
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
<link linkend="ini.mysqli.allow-persistent">mysqli.allow_persistent</link>.
Le nombre total de connexions ouvertes par un script peut être limité avec
la directive <link linkend="ini.mysqli.max-links">mysqli.max_links</link>.
Le nombre maximal de connexions persistantes par processus PHP peut être
restreint avec la directive <link linkend="ini.mysqli.max-persistent">mysqli.max_persistent</link>.
Veuillez noter que le serveur web peut engendrer plusieurs processus PHP.
</para>
<para>
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 <literal>persistent</literal>
peut être compris comme une promesse sur le fait que le statut persiste
réellement.
</para>
<para>
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
<methodname>mysqli::change_user</methodname> 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.
</para>
<para>
La fonction <methodname>mysqli::change_user</methodname> est une opération couteuse.
Pour de meilleures performances, les utilisateurs peuvent vouloir re-compiler
l'extension avec le drapeau de compilation <constant>MYSQLI_NO_CHANGE_USER_ON_PCONNECT</constant>.
</para>
<para>
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.
</para>
<para>
<emphasis role="bold">Voir aussi</emphasis>
</para>
<para>
<simplelist>
<member><function>mysqli_init</function></member>
<member><methodname>mysqli::__construct</methodname></member>
<member><methodname>mysqli::options</methodname></member>
<member><methodname>mysqli::real_connect</methodname></member>
<member><methodname>mysqli::change_user</methodname></member>
<member><link linkend="mysqli.get-host-info">$mysqli::host_info</link></member>
<member><link linkend="mysqli.configuration">Les options de configuration MySQLi</link></member>
<member><link linkend="features.persistent-connections">Les connexions persistantes aux bases de données</link></member>
</simplelist>
</para>
</section>
<section xml:id="mysqli.quickstart.statements">
<title>Exécution des requêtes</title>
<para>
Les requêtes peuvent être exécutées avec les fonctions
<methodname>mysqli::query</methodname>, <methodname>mysqli::real_query</methodname>
et <methodname>mysqli::multi_query</methodname>.. La fonction
<methodname>mysqli::query</methodname> 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 <methodname>mysqli::query</methodname>
est identique à appeler la fonction
<methodname>mysqli::real_query</methodname>suivie d'un appel à la fonction
<methodname>mysqli::store_result</methodname>.
</para>
<para>
<example>
<title>Exécution des requêtes</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
]]>
</programlisting>
</example>
</para>
<para>
<emphasis role="bold">Jeux de résultats en mémoire tampon</emphasis>
</para>
<para>
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 <methodname>mysqli::query</methodname>
combine à la fois l'exécution de la requête et la mise en mémoire tampon du jeu de résultats.
</para>
<para>
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.
</para>
<para>
<example>
<title>Navigation dans des résultats mis en mémoire tampon</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
$mysqli->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";
}
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Ordre inversé...
id = 3
id = 2
id = 1
Ordre du jeu de résultats...
id = 1
id = 2
id = 3
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">Jeux de résultats non mis en mémoire tampon</emphasis>
</para>
<para>
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.
</para>
<para>
<example>
<title>Navigation dans des résultats non mis en mémoire tampon</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli->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";
}
]]>
</programlisting>
</example>
</para>
<para>
<emphasis role="bold">Types de données des valeurs du jeu de résultats</emphasis>
</para>
<para>
Les fonctions <methodname>mysqli::query</methodname>, <methodname>mysqli::real_query</methodname>
et <methodname>mysqli::multi_query</methodname> sont utilisées pour exécuter des
requêtes non-préparées. Au niveau du protocole client-serveur MySQL, la commande
<literal>COM_QUERY</literal> 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.
</para>
<para>
<example>
<title>Le protocole texte retourne des chaînes de caractères par défaut</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("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']));
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
id = 1 (string)
label = a (string)
]]>
</screen>
</example>
</para>
<para>
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
<constant>MYSQLI_OPT_INT_AND_FLOAT_NATIVE</constant>, 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.
</para>
<para>
<example>
<title>Types natifs des données avec mysqlnd et l'option de connexion</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli();
$mysqli->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']));
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
id = 1 (integer)
label = a (string)
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">Voir aussi</emphasis>
</para>
<para>
<simplelist>
<member><methodname>mysqli::__construct</methodname></member>
<member><methodname>mysqli::options</methodname></member>
<member><methodname>mysqli::real_connect</methodname></member>
<member><methodname>mysqli::query</methodname></member>
<member><methodname>mysqli::multi_query</methodname></member>
<member><methodname>mysqli::use_result</methodname></member>
<member><methodname>mysqli::store_result</methodname></member>
</simplelist>
</para>
</section>
<section xml:id="mysqli.quickstart.prepared-statements">
<title>Les requêtes préparées</title>
<para>
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.
</para>
<para>
<emphasis role="bold">Flux de travail de base</emphasis>
</para>
<para>
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.
</para>
<para>
Le serveur MySQL supporte le mode anonyme, avec des marqueurs de position
utilisant le caractère <literal>?</literal>.
</para>
<para>
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.
</para>
<para>
<example>
<title>Première étape : la préparation</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
// Requête non préparée
$mysqli->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();
]]>
</programlisting>
</example>
</para>
<para>
<emphasis role="bold">Exécution répétée</emphasis>
</para>
<para>
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.
</para>
<para>
<example>
<title>Requête de type INSERT préparée une seule fois, et exécutée plusieurs fois</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
// Requête non préparée
$mysqli->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));
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array(3) {
array(2) {
["id"]=>
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++"
}
}
]]>
</screen>
</example>
</para>
<para>
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.
</para>
<para>
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 <literal>SELECT</literal>
n'est pas exécutée comme requête préparée dans l'exemple ci-dessus.
</para>
<para>
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.
</para>
<para>
<example>
<title>Moins d'aller-retour en utilisant les multi-INSERTs SQL</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
$mysqli->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();
]]>
</programlisting>
</example>
</para>
<para>
<emphasis role="bold">Types de données des valeurs du jeu de résultats</emphasis>
</para>
<para>
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 <literal>INT</literal>
SQL seront fournis comme variables de type entier PHP.
</para>
<para>
<example>
<title>Types de données natifs</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
// Requête non préparée
$mysqli->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']));
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
id = 1 (integer)
label = PHP (string)
]]>
</screen>
</example>
</para>
<para>
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.
</para>
<para>
<emphasis role="bold">Récupération des résultats en utilisant des variables liées</emphasis>
</para>
<para>
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
<classname>mysqli_result</classname>.
</para>
<para>
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.
</para>
<para>
<example>
<title>Liage des variables de sortie</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
// Requête non préparée
$mysqli->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));
}
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
id = 1 (integer), label = a (string)
]]>
</screen>
</example>
</para>
<para>
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
<literal>mysqli</literal>.
</para>
<para>
Il est également possible de mettre en mémoire tampon les résultats d'une
requête préparée en utilisant la fonction
<methodname>mysqli_stmt::store_result</methodname>.
</para>
<para>
<emphasis role="bold">Récupération des résultats en utilisant l'interface mysqli_result</emphasis>
</para>
<para>
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 <methodname>mysqli_stmt::get_result</methodname>
retourne un jeu de résultats mis en mémoire tampon.
</para>
<para>
<example>
<title>Utilisation de mysqli_result pour récupérer les résultats</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
// Requête non préparée
$mysqli->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));
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array(1) {
[0]=>
array(2) {
["id"]=>
int(1)
["label"]=>
string(3) "PHP"
}
}
]]>
</screen>
</example>
</para>
<para>
L'utilisation de l'interface <classname>mysqli_result</classname> offre d'autres avantages
d'un point de vue flexibilité dans la navigation dans le jeu de résultats côté client.
</para>
<para>
<example>
<title>Jeu de résultats mis en mémoire tampon pour plus de flexibilité dans la lecture</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
// Requête non préparée
$mysqli->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());
}
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array(2) {
["id"]=>
int(3)
["label"]=>
string(1) "C++"
}
array(2) {
["id"]=>
int(2)
["label"]=>
string(1) "Java"
}
array(2) {
["id"]=>
int(1)
["label"]=>
string(1) "PHP"
}
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">Échappement et injection SQL</emphasis>
</para>
<para>
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 <methodname>mysqli_stmt::bind_param</methodname> pour plus d'informations.
</para>
<para>
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.
</para>
<para>
<emphasis role="bold">Émulation côté client de la préparation d'une requête</emphasis>
</para>
<para>
L'API n'inclut pas d'émulation côté client de la préparation d'une requête.
</para>
<para>
<emphasis role="bold">Voir aussi</emphasis>
</para>