Tach!
Mit php/mysqli möchte ich alle Datensätze ausgeben, in denen "Hans" der Name ist. Hier der Weg ohne prepared Statements:
$sql="SELECT id
,name
, stadt
FROM foo
WHERE name
='Hans'";
$result=$db->query($sql);
while($line=$result->fetch_object())
{
echo"<p>".$line->id." - ".$line->name." - ".$line->stadt."</p>\n";
}
Dem fehlt noch die kontextgerechte Behandlung für HTML, also htmlspecialchars() um die auszugebenden Variablen.
> Das funktioniert einwandfrei. So, nun möchte ich die Ausgabe mit einem prepared Statement machen. Meine erste Überlegung war, hier einfach einen prepared Statement-Teil einzufügen. Das sah dann so aus:
> ~~~php
$eingabe_name="Hans";
> $sql="SELECT `id`,`name`, `stadt` FROM `foo` WHERE `name`=?";
> $result=$db->prepare($sql);
> $result->bind_param('s',$eingabe_name);
> $result->execute();
> while($line=$result->fetch_object())
> {
> echo"<p>".$line->id." - ".$line->name." - ".$line->stadt."</p>\n";
> }
Das endet in der Fehlermeldung "Fatal error: Call to undefined method mysqli_stmt::fetch_object() in ..." -betreffend die Zeile mit der while-Schleife. Da verstehe ich schon mal nicht, wieso es hier ein Problem gibt. Das prepared Statement betraf doch den Abfrage-Weg _zur_ DB. Das Ergebnis, also die Ausgabe der DB, sind doch einfach, wie im ersten Fall, die betreffenden Datensätze.
Die Klasse mysqli_stmt hat keine Methoden zum direkten Fetchen. Der vorgesehene Weg beim Verwenden von P.S. ist, die Felder im Resultset an Variablen zu binden. Dann läuft man mit der Methode fetch() durch die Ergebnismenge und hat jeweils die Werte eines Datensatzes in diesen Variablen stehen.
OK, nach dem Besuch von sicher mehr als 50 Seiten zu dem Thema und 10 Stunden vor dem PC, habe ich vor kurzem das erste Mal eine funktioiernede Lösung ohne Fehlermeldung zustandegebracht. Und ich habe keine Ahnung, wieso:
$eingabe_name="Hans";
$sql="SELECT
id
,name
,stadt
FROMfoo
WHEREname
=?";
$result=$db->prepare($sql);
$result->bind_param('s',$eingabe_name);
$result->execute();
$result->bind_result($id,$name,$stadt);
while($result->fetch())
{
echo"<p>".$id." - ".$name." - ".$stadt."</p>\n";
}
Nun, vielleicht hast du einfach aus dem Handbuch das passende Beispiel nachgebaut.
> Ich verstehe es einfach nicht. Wieso muss ich, nur weil die Übergabe des Suchparameters mit einem prepared Statement geschehen ist, plötzlich die betreffenden Datensätze mit "`bind_result`{:.language-php}" behandeln und in Variablen überführen?
Da musst du die Entwickler der MySQL-Client-API fragen, warum sie da einen anderen Weg gehen als bei herkömmlichen Abfragen. PHP ist hier nur geringfügig schuldig, weil es einfach diesen Weg nur mit PHP-Mitteln nachgebaut hat. Die PDO-Extension geht jedenfalls auch bei P.S. den herkömmlichen Fetch-Weg und bietet den Bind-Fetch-Weg optional an.
> Wieso funktioniert im ersten Fall eine Schleife mit "`$line=$result->fetch_object(`{:.language-php})" - während im prepared Statement Fall plötzlich nur eine Schleife mit "`$result->fetch()`{:.language-php}" funktioniert? Wieso plötzlich nicht mehr die Syntax "`$line->id`{:.language-php}"?
Weil es so entworfen wurde.
> Wie gesagt, ich verstehe nicht, wieso sich beim Code für die Ausgabe der betreffenden Datensätze plötzlich so viel ändert, nur weil die Suchbedingung nicht direkt im Query, sondern als prepared Statement eingefügt worden ist.
Muss man nicht verstehen, man muss aber diesen Weg gehen, wenn man P.S. mit mysqli haben will.
> Ich bitte darum, dass mir jemand in verständlichen Worten, also nicht in Techniksprech, die Unterschiede zwischen den beiden Situationen erklärt und wieso das nicht so funktioniert, wie ich es laut 2. Beispiel ursprünglich gedacht hatte.
Du hast ja nun schon herausgefunden, wie es geht. Mehr kann ich dir daran auch nicht erklären. Ich finde es auch nicht besonders glücklich gelöst. Die ganze Sache arbeitet nämlich mit Referenzen.
Referenzen kurz erklärt: Eine Variable besteht aus zwei Teilen - einem Container, der den Wert enthält, und ihrem Namen, der auf den Container zeigt. Bei $a = $b wird der Wert von $b nach $a kopiert - genauer: der Wert aus dem Container von $b wird in den Container von $a kopiert (noch genauer: PHP-intern passiert das etwas anders, aber das führt jetzt zu weit, für den Anwender sieht das jedenfalls wie eine Kopie aus). Ändert sich danach $a oder $b, betrifft das den jeweils anderen Container-/Variableninhalt nicht. Bei $a =& $b (man beachte das & )wird $a eine Referenz auf $b, was heißt, dass nun $a (ebenfalls) auf den Container von $b zeigt und damit beide auf denselben Variableninhalt. Man kann nun über $a und $b auf diesen einen Wert (Container) lesend oder schreibend zugreifen.
Beim Param-Bind wird jeweils vom Platzhalter eine Referenz zu einer bestimmten Variable (das heißt auf ihren Container) erstellt. Erst beim Execute wird deren (dessen) Inhalt ausgelesen. Wenn zum Beispiel bei einem Insert mehrere Datensätze eingefügt werden sollen, braucht man zwar nur ein Prepare und einmal Param-Binds, kann dann aber das Execute mehrfach aufrufen. Zwischen den Aufrufen muss man nur den Inhalt der Variablen (also ihres Container, angesprochen über den uns bekannten Variablennamen) ändern.
Beim Fetch-Vorgang wird ebenfalls eine Referenz auf die beim Result-Bind angegebenen Variablen erstellt. Ein Fetch schreibt über die in PHP intern gemerkten Referenen in die Container und du liest die Werte über die dir bekannten Variablennamen aus.
Das ganze funktioniert, wenn man einmal weiß, wie es geht, recht unkompliziert, wenn man den Wert gleich beim Fetchen ausgibt und ihn dann nicht mehr braucht. Will man die Werte aber zur späteren Verwendung in einem Array of Arrays speichern, muss man jede Variable einzeln anfassen und umkopieren. Noch bescheidener wird es, wenn man eine generische Datenbankabstraktionsfunktion schreiben will, die mit einer unterschiedlichen Anzahl von Parametern als auch Ergebnismengenfeldern umgehen soll. Dann kann man schlecht einzelne Variablen verwenden und fängt an, sich mit Referenzen auf Array-Felder rumzuschlagen.
So, mal wieder drei Fragen geklärt ... und zehn neue aufgeworfen ;)
dedlfix.