saftmeister hat gesagt.:
Da die Frage beantwortet wurde, kann man etwas OT werden
Jo. Immer sehr gerne.
PS löst nicht nur das Problem der unescapten Daten. Sie bringen auch Performance-Vorteile und bessere Wartbarkeit durch übersichtlicheren Code.
Performance-Vorteile vor allem dann, wenn die Query mehrfach mit unterschiedlichen Parametern aufgerufen wird. Das ist in PHP prinzipbedingt (Skripte werden ständig neu gestartet) nicht
unbedingt der absolute Regelfall. Natürlich gibt es das auch häufig, manchmal fehlt da aber auch bloß mal ein JOIN, der etwa eine Schleife von Queries ersetzt.
Wenn die Abfrage nur einmal ausgeführt wird, ist ein PS theoretisch auch mal langsamer, weil zwei Roundtrips zur DB benötigt werden. Wie relevant das hinsichtlich der Gesamtperformance ist, sei dahingestellt.
Übersichtlicherer Code… Will ich nicht widersprechen, aber es ist irgendwie auch Ansichtssache.
PHP:
$query = sprintf(
"SELECT row1 FROM table WHERE row2 = '%s' AND row3 = '%s'",
$mysqli->real_escape_string($x),
$mysqli->real_escape_string($y)
);
Mir ging es vor allem darum, dass man die Methode/Funktion auf Ebene des Library-Designs nicht abschaffen sollte, weil sie einen Zweck erfüllt. Das ist keine Aussage darüber, ob man das als Anwender der Library so nutzen sollte.
es birgt auch die Anfälligkeit mal was zu übersehen, das beste Beispiel dafür ist die Frage des TO
Wobei man bei
bind_param genauso einen Wert vergessen kann.
Da kann man geteilter Meinung sein. Was soll es an der Schnittstelle noch zu verbessern geben? Warum sollte man da noch mal was drum herum bauen wollen/sollen/müssen?
Vor allem die Geschichte, dass
bind_param und
bind_result eine Liste von einzelnen Variablen-Referenzen (grob gesagt: einzelne Variablen) übergeben werden muss. Aus der Doku:
PHP:
$stmt = $mysqli->prepare("INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
$stmt->bind_param('sssd', $code, $language, $official, $percent); // Einzelvariablen --merms
PHP:
if ($stmt = $mysqli->prepare("SELECT Code, Name FROM Country ORDER BY Name LIMIT 5")) {
$stmt->execute();
/* bind variables to prepared statement */
$stmt->bind_result($col1, $col2); // Die Variablen sind für "Code" und "Name" --m
Im Vergleich dazu nutzt etwa PDO Arrays/keine Referenzen:
PHP:
$sql = 'SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':calories' => 150, ':colour' => 'red'));
PHP:
$sth = $dbh->prepare("SELECT name, colour FROM fruit");
$sth->execute();
/* Fetch all of the remaining rows in the result set */
print("Fetch all of the remaining rows in the result set:\n");
$result = $sth->fetchAll();
print_r($result); // Hier muss nicht $col1 und $col2 angelegt werden --m
Der Vorteil von PDO ist an der Stelle vor allem, dass die Anzahl der Parameter, die in die Query eingefügt werden und die von der Query geliefert werden, nicht vorher bekannt sein muss. Das muss sie bei mysqli erst mal, weil für jeden dieser Werte eine Referenz existieren muss.
In PDO kann man deshalb sehr einfach generischen Code schreiben, der für eine Vielzahl von Szenarien funktioniert:
PHP:
$sth = $pdo->prepare($query);
$sth->execute($params);
$data = $sth->fetchAll();
In mysqli muss man da ungleich mehr mit Variablen rumhampeln oder sich generische Funktionen mit call_user_func_array und Arrays von Referenzen basteln, die ca. so aussehen:
-
http://de1.php.net/manual/en/mysqli-stmt.bind-result.php#102179
Das meine ich, wenn ich sage, dass das mysqli-API an der Stelle nicht so toll ist beziehungsweise dass man mehr Infrastruktur-Code braucht (was natürlich generell kein Beinbruch ist), der einem so was abnimmt.
Das Anwendungsszenario hier wäre: Ich will eine SELECT-Query mit beliebiger Anzahl an Parametern ausführen und die Rückgabe als Liste von Elementen mit Key-Value-Paaren erhalten. Ich denke, das ist eine übliche Anforderung.
Wenn man es nicht so generisch will: Die Sache mit den Einzelreferenzen bei mysqli ist einfach umständlich, weil man seine Daten doch eigentlich meist als assoziative Arrays haben will. Als einzelne Variablen braucht man sie nicht immer.
Was ist ein dynamischer Query?
Eine Abfrage, bei der etwa Filterkriterien je nach Nutzerauswahl ergänzt werden. Beispielsweise ein Suchformular, in dem du nach Vorname und Nachname und Alter und Adresse suchen/filtern kannst oder auch nur nach etwa Adresse. Das würde man umsetzen, indem man den WHERE-Teil dynamisch zusammenbaut und eben auch eine dynamische Anzahl an Eingabeparametern für die Query hat. Das wird in mysqli mit bind_param dann schnell mal umständlich, wenn man nicht mit call_user_func_array oder dergleichen arbeitet.
Für diesen Fall ja, es handelt sich um ein Tutorial. Sobald es aber mal etwas mehr sein darf und man auf den Trichter kommt, dass durch PS auch mehr Leistung (Wortspiel ) in die Applikation kommt, darf man etliche Funktionen und Scripte umbauen. Dann lieber doch von Anfang an auf PS zurück greifen.
Dazu habe ich oben ja schon was gesagt, deshalb hier nur noch mal Stichwörter: Notwendigkeit mehrerer gleichartiger Abfragen, JOIN, Query-Cache.
Dann sollte sich der Leser überlegen, ob er die richtige Motivation an den Tag legt.
Jip.
Dein Wort in… Ohr.
Bei so sicherheitsrelevanten Dingen sollte man gerade Anfängern abverlangen, den richtigen Weg zu gehen. Sonst hat man das 1mrdste Gästebuch, was defaced oder für XSS missbraucht wird. Das kann unser aller Wunsch nicht sein.
Nur so als Gedanke, ist nicht wirklich als Argument gemeint und auch nicht speziell auf dich bezogen:
Sagst du jedem Nutzer in jedem Thread, dass da bei seinen Ausgaben Aufrufe von htmlspecialchars fehlen? Escapest du in jeder deiner Antworten an der Stelle korrekt? Weist du auf jede mögliche Locking-Problematik oder Race-Condition hin? Oder dass URLs mit „javascript:“ beginnen könnten? Leitest du Leute dazu an, jede Nutzereingabe, die UTF-8-NFC sein sollte, auch darauf zu überprüfen, ob sie tatsächlich UTF-8-NFC ist? Weist du darauf hin, dass ein $ in einem regulären Ausdruck das Eingabeende matcht oder das Eingabeende mit einem \n davor? Oder dass \d und \w von Locale-Einstellungen abhängen können? Oder dass es
sehr abstrakte Probleme mit Zeichensätzen geben kann? Was XSRF ist? (Ein einziges Elend ist das!) Erklärst du, wie man einen URL behandeln muss, um ihn passend escapet in einen String in einem JS-Eventhandler eines HTML-Attributs einzutragen? Wie erklärst du das Leuten, die sich zum ersten Mal in ihrem Leben mit Programmierung oder EDV-Zeugs beschäftigen?
Es gibt da ein wünschenswertes Ideal und es gibt „didaktischen Realismus“.
Escaping in SQL-Queries ist dabei allerdings noch ein übersichtliches Problem, das gebe ich gerne zu.
Das also bitte alles auch als Offtopic vom Offtopic verstehen.
PS: „Wir“ haben da als Community in den letzten Jahren aber glaube ich schon kleine Fortschritte gemacht, was Qualitätssicherung angeht. SQL-Injections und die veraltete mysql-Extension sind etwa Standards, die eigentlich immer angemerkt werden. „Kontextwechsel“ als Begriff fällt auch ständig. Mein nächstes Ziel ist, den Stand auch für XSS/htmlspecialchars zu erreichen. Dass es zum Reflex wird, dazu was zu sagen, wenn es fehlt. Das ist halt sehr anstrengend.