dedlfix: Nächstes Problem: LIMIT mit Variablen

Beitrag lesen

Hi!

Du verwendest die Variable $startposition nicht. Stattdessen steht in $aktion kurz vorm Schluss die Zeichenkette '$startposition'.
Aber wieso? Wenn bei var_dump ein "  3(int)  " rauskommt, dann heißt das doch, die Variable ist eine Zahl mit dem Wert 3 und _kein_ String.

Ja, das stimmt soweit. Du nimmst jedoch nur an, dass du sie in $aktion einbaust. Tust du aber nicht, weil du die falschen Anführungszeichen verwendest. Es nützt nichts, sich nur $startposition anzuschauen, denn letztlich willst du ein ordentliches SQL-Statement in $aktion haben. Also schau dir an, was $aktion beinhaltet. Und dann wechsel mal die Anführungszeichen zu "" und schau noch einmal. (Und mach dir den Unterschied zwischen '' und "" klar!)

Fehler abfangen statt einfach weiterzumachen, als ob immer schönes Wetter ist, zeichnen ein robustes Script aus.
OK, und was soll ich also anders machen? Bei _unverändertem_ Skript funktioniert es ja, wenn ich _statt_ Variablen Zahlen verwende, also was an meiner Zeile ist falsch?

Das mit der Zeile wirst du nun hoffentlich verstanden haben. Ansonsten ist es vorteilhaft, die Abarbeitung des nächsten Schrittes vom Erfolg des jeweils vorhergehenden Schrittes abhängig zu machen.

$mysqli = new mysqli("localhost", "my_user", "my_password", "world");  
  
// prüfe auf Verbindungsfehler  
if (mysqli_connect_errno()) {  
  // Verbindungsaufbau fehlgeschlagen.  
  // Fehler loggen.  
  // Wenn möglich, eine für den Benutzer sinnvolle Alternative anbieten.  
} else {  
  
  if ($result = $mysqli->query("SELECT feldliste FROM tabelle")) {  
    while ($row = $result->fetch_assoc()) {  
      // Ergebnis verarbeiten  
    }  
  
    $result->close();  
  } else {  
    // Abfrage fehlgeschlagen.  
    // Fehler loggen.  
    // Wenn möglich, eine für den Benutzer sinnvolle Alternative anbieten.  
  }  
  
  $mysqli->close();  
}

Beim Erzeugen des mysqli-Objekts wird die Verbindung zum DB-Server aufgebaut. Wenn das misslang, liefert mysqli_connect_errno() einen Wert ungleich 0. $mysqli ist daraufhin nicht brauchbar und wird nicht weiter verwendet. Nur wenn kein Fehler auftrat, geht's in den else-Zweig und dort mit der Methode $mysqli->query() weiter. Auch hier kann wieder ein Fehler auftreten und dann steht in $result kein mysqli_result-Objekt sondern false. Auf $result zuzugreifen ist nur sinnvoll, wenn das SQL-Statement fehlerlos abgearbeitet werden konnte. Deshalb prüft ich wieder mit if ($result... ob etwas anderes als false geliefert wird und lasse nur in dem Fall das while auf das $result los.

Wenn du ohne all diese Prüfungen nur sowas notierst:

$mysqli = new mysqli("localhost", "my_user", "my_password", "world");  
$result = $mysqli->query("SELECT feldliste FROM tabelle");  
while ($row = $result->fetch_assoc()) {  
  // Ergebnis verarbeiten  
}  
$result->close();  
$mysqli->close();

bekommst du solche Folgefehler angezeigt, die du gesehen hast, wenn mal was schief läuft.

Übrigens, die Werte für LIMIT kann man nicht per Platzhalter in einem Prepared Statement übergeben.
Ich hab hier überhaupt kein Prepared Statement, sondern eine einfache Abfrage.

Aber mal angenommen, du machst das so, dann

Die müssen direkt reingeschrieben werden.

muss der MySQL-Server eine Zahl zu Gesicht bekommen und keinen Platzhalter. Das meinte ich mit direkt reinschreiben. Sie müssen direkt im SQL-Statement stehen. Wie du dieses vorher zusammenstückelst, bekommt der DB-Server ja nicht mit. Es zählt das Ergebnis, das fertige SQL-Statement. Darauf bezog sich meine Aussage.

Bei einer Blätterfunktion? Wo sich die Startposition laufend ändert? Wie soll das mit fixen Zahlen gehen? Da _muß_ ich mit Variablen arbeiten.

Beim Zusammenbau ja. Aus Sicht von MySQL sind im SQL-Statement fix und fertige Zahlenwerte zu sehen.

Wenn du nicht absolut sicher bist, dass es Zahlen sind, musst du noch dafür sorgen, dass sie welche werden, sonst hast du da am Prepared-Statement-Mechanismus vorbei eine SQL-Injection-Lücke geschaffen.
Ich sagte doch, ich _weiß_ dass es eine Zahl ist.

Ja, Folgefehler deinerseits :-)

Jetzt nehmen wir mal an, du schriebest sowas:

$aktion = "SELECT feld1,feld2 FROM table WHERE feld=? LIMIT ?,4";

Das geht so nicht als Prepared Statement, denn LIMIT will keine Platzhalter.

$aktion = "SELECT feld1,feld2 FROM table WHERE feld=? LIMIT $_GET[start],4";

So darf es nicht, denn trotz P.S. hast du nun eine SQL-Injection-Lücke. Bei feld=? kümmert sich der P.S.-Mechanismus um eine ordentlich (gefahrlose) Übergabe, beim LIMIT musst du das selbst tun.

$aktion = sprintf("SELECT feld1,feld2 FROM table WHERE feld=? LIMIT %s,4", intval($_GET['start']));

Jetzt sorgt das intval() für einen garantierten Zahlenwert, auch wenn der böse Bube SQL-Code in den Querystring schrieb (in dem Fall ist 0 der Rückgabewert von intval()).

Es mag sinnvoll sein, eine richtige Plausibilitätsprüfung für $_GET['start'] vorzunehmen, doch mit dem intval() bekommt man vielleicht ein eigenartiges Ergebnis, aber wenigstens keine SQL-Injection.

Lo!