-
Notifications
You must be signed in to change notification settings - Fork 46
/
Copy pathquickstart.xml
1700 lines (1588 loc) · 53.7 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: samesch Status: ready -->
<chapter xml:id="mysqli.quickstart" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Kurzanleitung für den schnellen Einstieg</title>
<para>
Diese Kurzanleitung hilft dabei, sich mit der PHP-MySQL-API vertraut zu
machen.
</para>
<para>
Diese Kurzanleitung gibt einen Überblick über die mysqli-Erweiterung. Es
werden Beispiele für alle wichtigen Aspekte der API beschrieben.
Datenbankkonzepte werden in dem Maße erklärt, wie es für die Darstellung
von MySQL-spezifischen Konzepten erforderlich ist.
</para>
<para>
Voraussetzungen: Vertrautheit mit der Programmiersprache PHP, der Sprache
SQL und Grundkenntnisse über den MySQL-Server.
</para>
<section xml:id="mysqli.quickstart.dual-interface">
<title>Prozedurale und objektorientierte Schnittstelle</title>
<para>
Die mysqli-Erweiterung verfügt über eine doppelte Schnittstelle. Sie
unterstützt das prozedurale und das objektorientierte Programmier-Paradigma.
</para>
<para>
Benutzer, die von der alten mysql-Erweiterung umsteigen, werden vielleicht
die prozedurale Schnittstelle bevorzugen. Die prozedurale Schnittstelle ist
ähnlich wie die der alten mysql Erweiterung. In vielen Fällen unterscheiden
sich die Funktionsnamen nur durch das Präfix. Einige mysqli-Funktionen
benötigen ein Verbindungs-Handle als ersten Parameter, während
entsprechende Funktionen der alten mysql-Schnittstelle es als optionalen
letzten Parameter nehmen.
</para>
<para>
<example>
<title>Einfacher Umstieg von der alten mysql-Erweiterung</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = mysqli_connect("example.com", "user", "password", "database");
$result = mysqli_query($mysqli, "SELECT 'Bitte verwenden Sie nicht die veraltete mysql-Erweiterung für neue Projekte. ' 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 'Verwenden Sie stattdessen die mysqli-Erweiterung.' AS _msg FROM DUAL", $mysql);
$row = mysql_fetch_assoc($result);
echo $row['_msg'];
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Bitte verwenden Sie nicht die veraltete mysql-Erweiterung für neue Projekte. Verwenden Sie stattdessen die mysqli-Erweiterung.
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">Die objektorientierte Schnittstelle</emphasis>
</para>
<para>
Neben der klassischen prozeduralen Schnittstelle kann der Benutzer auch die
objektorientierte Schnittstelle verwenden. Die Dokumentation ist anhand der
objektorientierten Schnittstelle gegliedert. Um den Einstieg zu
erleichtern, sind die Funktionen der objektorientierten Schnittstelle nach
ihrem Zweck gruppiert. Der Referenzteil enthält Beispiele für beide
Syntaxvarianten.
</para>
<para>
Es gibt keine signifikanten Leistungsunterschiede zwischen den beiden
Schnittstellen. Der Benutzer kann seine Wahl nach persönlichen Vorlieben
treffen.
</para>
<para>
<example>
<title>Objektorientierte und prozedurale Schnittstelle</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>
Für die Kurzanleitung wird die objektorientierte Schnittstelle verwendet,
weil der Referenzabschnitt entsprechend aufgebaut ist.
</para>
<para>
<emphasis role="bold">Stile miteinander kombinieren</emphasis>
</para>
<para>
Es ist jederzeit möglich, zwischen den Stilen zu wechseln. Das Kombinieren
beider Stile wird aus Gründen des Programmierstils und der Verständlichkeit
des Codes nicht empfohlen.
</para>
<para>
<example>
<title>Schlechter Programmierstil</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = new mysqli("example.com", "user", "password", "database");
$result = mysqli_query($mysqli, "SELECT 'Möglich, aber schlechter Stil.' AS _msg FROM DUAL");
if ($row = $result->fetch_assoc()) {
echo $row['_msg'];
}
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Möglich, aber schlechter Stil.
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">Siehe auch</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">Übersicht über die Funktionen der MySQLi-Erweiterung</link></member>
</simplelist>
</para>
</section>
<section xml:id="mysqli.quickstart.connections">
<title>Datenbankverbindungen</title>
<para>
Der MySQL-Server unterstützt die Verwendung verschiedener
Transportschichten für Verbindungen. Verbindungen verwenden TCP/IP,
Unix-Domain-Sockets oder Windows Named Pipes.
</para>
<para>
Der Hostname <literal>localhost</literal> hat eine besondere Bedeutung. Er
ist an die Verwendung von Unix-Domain-Sockets gebunden. Um eine
TCP/IP-Verbindung zum Localhost zu öffnen, muss <literal>127.0.0.1</literal>
anstelle des Hostnamens <literal>localhost</literal> verwendet werden.
</para>
<para>
<example>
<title>Die besondere Bedeutung von 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">Standardwerte für Verbindungsparameter</emphasis>
</para>
<para>
Je nach verwendeter Verbindungsfunktion können verschiedene Parameter
weggelassen werden. Wenn ein Parameter nicht angegeben wird, versucht die
Erweiterung, die Standardwerte zu verwenden, die in der
PHP-Konfigurationsdatei festgelegt sind.
</para>
<para>
<example>
<title>Standardwerte festlegen</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>
Die entsprechenden Parameterwerte werden dann an die Client-Bibliothek
übergeben, die von der Erweiterung verwendet wird. Wenn die
Client-Bibliothek leere oder nicht gesetzte Parameter findet, kann sie die
Standardwerte der Bibliothek verwenden.
</para>
<para>
<emphasis role="bold">Standardwerte der integrierten Verbindungsbibliothek</emphasis>
</para>
<para>
Wenn der Wert von host nicht definiert oder leer ist, verwendet die
Client-Bibliothek standardmäßig eine Unix-Socket-Verbindung zu
<literal>localhost</literal>. Wenn der Socket undefiniert oder leer ist und
eine Unix-Socket-Verbindung angefordert wird, dann wird versucht, eine
Verbindung zum Standardsocket <literal>/tmp/mysql.sock</literal>
aufzubauen.
</para>
<para>
Auf Windows-Systemen wird der Hostname <literal>.</literal> von der
Client-Bibliothek als Versuch interpretiert, eine auf einer Windows Namend
Pipe basierende Verbindung zu öffnen. In diesem Fall wird der
Socket-Parameter als Name der Pipe interpretiert. Wird er nicht angegeben
oder ist er leer, dann wird der Socket (Name der Pipe) standardmäßig auf
<literal>\\.\pipe\MySQL</literal> gesetzt.
</para>
<para>
Wenn eine Verbindung weder basierend auf einen Unix-Domänen-Socket noch auf
eine Windows Named Pipe aufgebaut werden soll und der Parameterwert für den
Port nicht gesetzt ist, verwendet die Bibliothek standardmäßig den Port
<literal>3306</literal>.
</para>
<para>
Die <link linkend="mysqlnd.overview">mysqlnd</link>-Bibliothek und die
MySQL-Client-Bibliothek (libmysqlclient) implementieren dieselbe Logik zur
Bestimmung der Standardwerte.
</para>
<para>
<emphasis role="bold">Optionen für die Verbindung</emphasis>
</para>
<para>
Die Verbindungsoptionen sind verfügbar, um &zb; Initialisierungsbefehle zu
setzen, die beim Aufbau der Verbindung ausgeführt werden, oder um die
Verwendung eines bestimmten Zeichensatzes anzugeben. Die
Verbindungsoptionen müssen gesetzt werden, bevor eine Netzwerkverbindung
aufgebaut wird.
</para>
<para>
Um eine Verbindungsoption zu setzen, muss der Verbindungsvorgang in drei
Schritten durchgeführt werden: erstellen eines Verbindungs-Handles mit
<function>mysqli_init</function> oder
<methodname>mysqli::__construct</methodname>, setzen der gewünschten
Optionen mit <methodname>mysqli::options</methodname> und Aufbau der
Netzwerkverbindung mit <methodname>mysqli::real_connect</methodname>.
</para>
<para>
<emphasis role="bold">Pooling von Verbindungen</emphasis>
</para>
<para>
Die mysqli-Erweiterung unterstützt persistente Datenbankverbindungen, die
eine spezielle Art von gepoolten Verbindungen sind. Standardmäßig wird jede
Datenbankverbindung, die von einem Skript geöffnet wird, entweder explizit
durch den Benutzer während der Laufzeit geschlossen oder am Ende des
Skripts automatisch freigegeben. Eine persistente Verbindung wird nicht
geschlossen. Stattdessen wird sie in einen Pool gelegt, um später
wiederverwendet zu werden, wenn eine Verbindung zum selben Server mit
denselben Benutzernamen, Passwort, Socket, Port und Standarddatenbank
geöffnet wird. Die Wiederverwendung spart Verbindungs-Overhead.
</para>
<para>
Jeder PHP-Prozess verwendet seinen eigenen mysqli-Verbindungspool. Abhängig
vom Einsatzmodell des Webservers kann ein PHP-Prozess eine oder mehrere
Anfragen bedienen. Daher kann eine in einem Pool gehaltene Verbindung von
einem oder mehreren Skripten nacheinander verwendet werden.
</para>
<para>
<emphasis role="bold">Persistente Verbindung</emphasis>
</para>
<para>
Wenn im Verbindungspool für eine bestimmte Kombination von Host,
Benutzername, Passwort, Socket, Port und Standarddatenbank keine unbenutzte
persistente Verbindung gefunden wird, öffnet mysqli eine neue Verbindung.
Die Verwendung persistenter Verbindungen kann mit der PHP-Direktive
<link linkend="ini.mysqli.allow-persistent">mysqli.allow_persistent</link>
aktiviert und deaktiviert werden. Die Gesamtzahl der von einem Skript
geöffneten Verbindungen kann mit
<link linkend="ini.mysqli.max-links">mysqli.max_links</link> begrenzt
werden. Die maximale Anzahl von persistenten Verbindungen pro PHP-Prozess
kann mit
<link linkend="ini.mysqli.max-persistent">mysqli.max_persistent</link>
begrenzt werden. Bitte beachten Sie, dass der Webserver viele PHP-Prozesse
erzeugen kann.
</para>
<para>
Eine häufige Kritik an persistenten Verbindungen ist, dass ihr Zustand vor
der Wiederverwendung nicht zurückgesetzt wird. Zum Beispiel werden offene
und nicht abgeschlossene Transaktionen nicht nicht automatisch
zurückgesetzt. Aber auch Berechtigungsänderungen, die in der Zeit zwischen
der Aufnahme der Verbindung in den Pool und ihrer Wiederverwendung
vorgenommen wurden, werden nicht berücksichtigt. Dies kann als
unerwünschter Nebeneffekt angesehen werden. Andererseits kann der Name
<literal>persistent</literal> als Zusage verstanden werden, dass der
Zustand erhalten bleibt.
</para>
<para>
Die mysqli-Erweiterung unterstützt beide Arten einer persistenten
Verbindung: den persistenten Zustand und den vor der Wiederverwendung
zurückgesetzten Zustand. Die Voreinstellung ist Zurücksetzen. Bevor eine
persistente Verbindung wiederverwendet wird, ruft die mysqli-Erweiterung
implizit <methodname>mysqli::change_user</methodname> auf, um den Zustand
zurückzusetzen. Die persistente Verbindung erscheint dem Benutzer so, als
ob sie gerade geöffnet worden wäre. Es sind keine Spuren von früheren
Verwendungen sichtbar.
</para>
<para>
Der Aufruf von <methodname>mysqli::change_user</methodname> ist eine
aufwändige Operation. Um die beste Leistung zu erzielen, sollten Benutzer
die Erweiterung mit dem Kompilierungsflag
<constant>MYSQLI_NO_CHANGE_USER_ON_PCONNECT</constant> neu kompilieren.
</para>
<para>
Es ist dem Benutzer überlassen, zwischen sicherem Verhalten und bester
Leistung zu wählen. Beides sind berechtigte Optimierungsziele. Um die
Benutzung zu erleichtern, wurde das sichere Verhalten auf Kosten der
maximalen Leistung als Standard festgelegt.
</para>
<para>
<emphasis role="bold">Siehe auch</emphasis>
</para>
<para>
<simplelist>
<member><methodname>mysqli::__construct</methodname></member>
<member><function>mysqli_init</function></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">MySQLi-Konfigurationsoptionen</link></member>
<member><link linkend="features.persistent-connections">Persistente Datenbankverbindungen</link></member>
</simplelist>
</para>
</section>
<section xml:id="mysqli.quickstart.statements">
<title>Ausführen von Anweisungen</title>
<para>
Anweisungen können mit <methodname>mysqli::query</methodname>,
<methodname>mysqli::real_query</methodname> und
<methodname>mysqli::multi_query</methodname> ausgeführt werden. Die
Funktion <methodname>mysqli::query</methodname> wird am häufigsten
verwendet und kombiniert in einem Aufruf die auszuführende Anweisung mit
einem gepufferten Abruf der Ergebnismenge, falls vorhanden. Der Aufruf von
<methodname>mysqli::query</methodname> ist gleichbedeutend mit dem Aufruf
von <methodname>mysqli::real_query</methodname> gefolgt von
<methodname>mysqli::store_result</methodname>.
</para>
<para>
<example>
<title>Ausführen von Anfragen</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">Gepufferte Ergebnisse</emphasis>
</para>
<para>
Nach der Ausführung der Anweisung können die Ergebnisse entweder alle auf
einmal vom Server abgerufen werden oder Zeile für Zeile. Die clientseitige
Pufferung der Ergebnismenge ermöglicht es dem Server, die mit den
Ergebnissen der Anweisung verbundenen Ressourcen so früh wie möglich
freizugeben. Im Allgemeinen verarbeiten Clients die Ergebnismengen langsam.
Es wird daher empfohlen, gepufferte Ergebnismengen zu verwenden.
<methodname>mysqli::query</methodname> kombiniert die Ausführung einer
Anweisung mit der Pufferung der Ergebnismenge.
</para>
<para>
PHP-Anwendungen können frei durch gepufferte Ergebnismengen navigieren. Da
die Ergebnismengen im Speicher des Clients gehalten werden, ist die
Navigation sehr schnell. Bitte bedenken Sie, dass es oft einfacher ist,
einen Client zu skalieren als den Server.
</para>
<para>
<example>
<title>Navigation durch gepufferte Ergebnisse</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 "Umgekehrte Reihenfolge...\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 "Reihenfolge der Ergebnisse...\n";
foreach ($result as $row) {
echo " id = " . $row['id'] . "\n";
}
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Umgekehrte Reihenfolge...
id = 3
id = 2
id = 1
Reihenfolge der Ergebnisse...
id = 1
id = 2
id = 3
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">Ungepufferte Ergebnismengen</emphasis>
</para>
<para>
Wenn der Client-Speicher knapp ist und es nicht notwendig ist,
Server-Ressourcen so früh wie möglich freizugeben, um die Serverlast gering
zu halten, können ungepufferte Ergebnisse verwendet werden. Das Blättern
durch ungepufferte Ergebnisse ist erst möglich, nachdem alle Zeilen gelesen
wurden.
</para>
<para>
<example>
<title>Navigation durch ungepufferte Ergebnisse</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli->real_query("SELECT id FROM test ORDER BY id ASC");
$result = $mysqli->use_result();
echo "Reihenfolge der Ergebnisse...\n";
foreach ($result as $row) {
echo " id = " . $row['id'] . "\n";
}
]]>
</programlisting>
</example>
</para>
<para>
<emphasis role="bold">Datentypen der Werte in der Ergebnismenge</emphasis>
</para>
<para>
Die Funktionen <methodname>mysqli::query</methodname>,
<methodname>mysqli::real_query</methodname> und
<methodname>mysqli::multi_query</methodname> werden verwendet, um nicht
vorbereitete Anweisungen auszuführen. Auf der Ebene des
Client-Server-Protokolls von MySQL werden der Befehl
<literal>COM_QUERY</literal> und das Textprotokoll für die Ausführung von
Anweisungen verwendet. Mit dem Textprotokoll wandelt der MySQL-Server alle
Daten einer Ergebnismenge vor dem Senden in Zeichenketten um. Diese
Umwandlung wird unabhängig vom Datentyp der Spalten der SQL-Ergebnismenge
durchgeführt. Die mysql-Client-Bibliotheken empfangen alle Spaltenwerte als
Zeichenketten. Es wird keine weitere clientseitige Umwandlung durchgeführt,
um Spalten zurück in ihre nativen Typen umzuwandeln. Stattdessen werden
alle Werte als PHP-Zeichenketten bereitgestellt.
</para>
<para>
<example>
<title>Das Textprotokoll gibt standardmäßig Zeichenketten zurück</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>
Wenn die mysqlnd-Bibliothek verwendet wird, können Integer- und
Float-Spalten durch Setzen der Verbindungsoption
<constant>MYSQLI_OPT_INT_AND_FLOAT_NATIVE</constant> in PHP-Zahlen
umgewandelt werden. Wenn sie gesetzt ist, überprüft die mysqlnd-Bibliothek
die Metadaten der Spaltentypen in der Ergebnismenge und wandelt
numerische SQL-Spalten in PHP-Zahlen um, wenn der Wertebereich des
PHP-Datentyps dies zulässt. Auf diese Weise werden &zb; SQL-INT-Spalten
als Integer zurückgegeben.
</para>
<para>
<example>
<title>Native Datentypen bei mysqlnd und Verbindungsoption</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">Siehe auch</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>Vorbereitete Anweisungen (Prepared Statements)</title>
<para>
Die MySQL-Datenbank unterstützt vorbereitete Anweisungen. Vorbereitete
Anweisungen oder parametrisierte Anweisungen ermöglichen die wiederholte
und effiziente Ausführung derselben Anweisung und schützen gleichzeitig vor
SQL-Injections.
</para>
<para>
<emphasis role="bold">Grundlegender Ablauf</emphasis>
</para>
<para>
Die Ausführung einer vorbereiteten Anweisung besteht aus zwei Phasen: der
Vorbereitung und der Ausführung. In der Vorbereitungsphase wird eine
Anweisungsvorlage an den Datenbankserver gesendet. Der Server führt eine
Syntaxprüfung durch und initialisiert Server-interne Ressourcen für die
spätere Verwendung.
</para>
<para>
Der MySQL-Server unterstützt die Verwendung des anonymen,
positionsbezogenen Platzhalters <literal>?</literal>.
</para>
<para>
Auf das Vorbereiten folgt das Ausführen. Während der Ausführung bindet der
Client die Parameterwerte und sendet sie an den Server. Der Server führt
die Anweisung mit den gebundenen Werten unter Verwendung der zuvor
erstellten internen Ressourcen aus.
</para>
<para>
<example>
<title>Vorbereitete Anweisung</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
/* Nicht-vorbereitete Anweisung */
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT, label TEXT)");
/* Vorbereitete Anweisung, Stufe 1: vorbereiten */
$stmt = $mysqli->prepare("INSERT INTO test(id, label) VALUES (?, ?)");
/* Vorbereitete Anweisung, Stufe 2: binden und ausführen */
$id = 1;
$label = 'PHP';
$stmt->bind_param("is", $id, $label); // "is" bedeutet, dass $id als Integer und
// $label als Zeichenkette gebunden ist
$stmt->execute();
]]>
</programlisting>
</example>
</para>
<para>
<emphasis role="bold">Mehrmalige Ausführung</emphasis>
</para>
<para>
Eine vorbereitete Anweisung kann mehrmals ausgeführt werden. Bei jeder
Ausführung wird der aktuelle Wert der gebundenen Variablen ausgewertet und
an den Server gesendet. Die Anweisung wird nicht erneut analysiert und die
Anweisungsvorlage wird nicht erneut an den Server übertragen.
</para>
<para>
<example>
<title>INSERT einmal vorbereitet, mehrfach ausgeführt</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
/* Nicht-vorbereitete Anweisung */
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT, label TEXT)");
/* Vorbereitete Anweisung, Stufe 1: vorbereiten */
$stmt = $mysqli->prepare("INSERT INTO test(id, label) VALUES (?, ?)");
/* Vorbereitete Anweisung, Stufe 2: binden und ausführen */
$stmt->bind_param("is", $id, $label); // "is" bedeutet, dass $id als Integer und
// $label als Zeichenkette gebunden ist
$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) {
[0]=>
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>
Jede vorbereitete Anweisung beansprucht Ressourcen auf dem Server, weshalb
sie sofort nach ihrer Verwendung explizit geschlossen werden sollte. Falls
dies nicht explizit geschieht, wird die Anweisung geschlossen, wenn das
Anweisungs-Handle von PHP freigegeben wird.
</para>
<para>
Die Verwendung einer vorbereiteten Anweisung ist nicht immer die
effizienteste Art, eine Anweisung auszuführen. Eine vorbereitete Anweisung,
die nur einmal ausgeführt wird, verursacht mehr Client-Server-Umläufe
(Roundtrips) als eine nicht-vorbereitete Anweisung. Aus diesem Grund wird
die <literal>SELECT</literal>-Anweisung nicht als vorbereitete Anweisung
ausgeführt.
</para>
<para>
Außerdem sollte für INSERTs die Verwendung der multi-INSERT-Syntax von
MySQL in Betracht gezogen werden. Für das Beispiel erfordert multi-INSERT
weniger Umläufe zwischen Server und Client als die oben gezeigte
vorbereitete Anweisung.
</para>
<para>
<example>
<title>Weniger Umläufe durch multi-INSERT-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">Datentypen der Werte in der Ergebnismenge</emphasis>
</para>
<para>
Das MySQL-Client-Server-Protokoll definiert unterschiedliche
Datenübertragungsprotokolle für vorbereitete Anweisungen und
nicht-vorbereitete Anweisungen. Vorbereitete Anweisungen verwenden das
sogenannte Binärprotokoll. Der MySQL-Server sendet die Ergebnisdaten "as is"
(wie sie sind) im Binärformat. Die Ergebnisse werden vor dem Senden nicht
zu Zeichenketten serialisiert. Die Client-Bibliotheken empfangen die
binären Daten und versuchen, die Werte in geeignete PHP-Datentypen
umzuwandeln. Zum Beispiel werden Ergebnisse aus einer
SQL-<literal>INT</literal>-Spalte als PHP-Integer-Variablen bereitgestellt.
</para>
<para>
<example>
<title>Native Datentypen</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
/* Nicht-vorbereitete Anweisung */
$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>
Dieses Verhalten unterscheidet sich von nicht-vorbereiteten Anweisungen.
Standardmäßig geben nicht-vorbereitete Anweisungen alle Ergebnisse als
Zeichenketten zurück. Diese Vorgabe kann mit einer Verbindungsoption
geändert werden. Wenn diese Verbindungsoption verwendet wird, gibt es keine
Unterschiede.
</para>
<para>
<emphasis role="bold">Ergebnisse über gebundene Variablen abrufen</emphasis>
</para>
<para>
Ergebnisse von vorbereiteten Anweisungen können entweder durch Binden der
Ausgabevariablen oder durch Anfordern eines
<classname>mysqli_result</classname>-Objekts abgerufen werden.
</para>
<para>
Die Ausgabevariablen müssen nach der Ausführung der Anweisung gebunden
werden. Für jede Spalte der Ergebnismenge der Anweisung muss eine Variable
gebunden werden.
</para>
<para>
<example>
<title>Binden der Ausgabevariablen</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
/* Nicht-vorbereitete Anweisung */
$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);
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 = PHP (string)
]]>
</screen>
</example>
</para>
<para>
Vorbereitete Anweisungen geben standardmäßig ungepufferte Ergebnismengen
zurück. Die Ergebnisse der Anweisung werden nicht implizit vom Server
abgerufen und zur clientseitigen Pufferung zum Client übertragen. Die
Ergebnismenge nimmt solange Serverressourcen in Anspruch, bis alle
Ergebnisse vom Client abgerufen wurden. Es wird daher empfohlen, die
Ergebnisse frühzeitig abzurufen. Wenn ein Client nicht alle Ergebnisse
abrufen kann oder der Client die Anweisung schließt, bevor er alle Daten
geholt hat, müssen die Daten implizit mit <literal>mysqli</literal>
abgerufen werden.
</para>
<para>
Mit <methodname>mysqli_stmt::store_result</methodname> ist es auch möglich,
die Ergebnisse einer vorbereiteten Anweisung zu puffern.
</para>
<para>
<emphasis role="bold" >Abrufen der Ergebnisse über die
mysqli_result-Schnittstelle</emphasis>.
</para>
<para>
Anstatt gebundene Ergebnisse zu verwenden, können die Ergebnisse auch über
die mysqli_result-Schnittstelle abgerufen werden.
<methodname>mysqli_stmt::get_result</methodname> gibt eine gepufferte
Ergebnismenge zurück.
</para>
<para>
<example>
<title>Verwendung von mysqli_result zum Abrufen von Ergebnissen</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
/* Nicht-vorbereitete Anweisung */
$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>
Die Verwendung der <classname>mysqli_result</classname>-Schnittstelle
bietet den zusätzlichen Vorteil einer flexiblen clientseitigen Navigation
in der Ergebnismenge.
</para>
<para>
<example>
<title>Gepufferte Ergebnismenge für flexibles Auslesen</title>
<programlisting role="php">
<![CDATA[
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");
/* Nicht-vorbereitete Anweisung */
$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(3) "C++"
}
array(2) {
["id"]=>
int(2)
["label"]=>
string(4) "Java"
}
array(2) {
["id"]=>
int(1)
["label"]=>
string(3) "PHP"
}
]]>
</screen>
</example>
</para>
<para>
<emphasis role="bold">Maskierung und SQL-Injection</emphasis>
</para>
<para>
Die gebundenen Variablen werden getrennt von der Abfrage an den Server
gesendet und können diese daher nicht beeinflussen. Der Server verwendet
diese Werte erst zum Zeitpunkt der der Ausführung, nachdem die
Anweisungsvorlage geparst wurde. Die gebundenen Parameter müssen nicht
müssen nicht maskiert werden, da sie nie direkt in die Abfragezeichenkette
eingefügt werden. Dem Server muss der Typ der gebundenen Variablen
mitgeteilt werden, um eine geeignete Umwandlung zu ermöglichen. Siehe
<methodname>mysqli_stmt::bind_param</methodname> für weitere Informationen.
</para>
<para>
Diese Trennung wird oft als die einzige Möglichkeit angesehen, sich gegen
SQL-Injection zu schützen, aber tatsächlich kann das gleiche Maß an
Sicherheit auch mit nicht-vorbereiteten Anweisungen erreicht werden, wenn
alle Werte korrekt formatiert sind. Es ist wichtig, zu beachten, dass eine
korrekte Formatierung nicht dasselbe ist wie die Maskierung, und mehr Logik
beinhaltet. Daher sind vorbereitete Anweisungen einfach ein bequemerer und
weniger fehleranfälliger Ansatz, um dieses Niveau an Datenbanksicherheit zu
erreichen.