Error Handling bei Prepared Statements


#1

Hallo,

ich beschäftige mich momentan mit Prepared Statements in PHP und bin auf ein Problem gestoßen, das ich mir nicht erklären kann. Konkret geht es um das Abfangen von Problemen (Error Handling) bei Falscheingaben.

Ich habe bereits im Internet dazu recherchiert und Antworten gefunden (bspw. return-Werte checken und nur bei true weiter machen), aber ohne Erfolg.
Habe auch gelesen, dass deshalb keine Fehlermeldung kommt, da das Prepared Statement ja erst später abgesendet wird aber all die Lösungen die genannt wurden haben bei mir nicht funktioniert.
Bspw.

if (mysqli_stmt_bind_param($stmt, 'i', $entry_id))  {
    mysqli_stmt_execute($stmt);
}

In meinem Fall geht es darum, dass (im Standardfall) per POST eine ID in einer Variable übergeben wird. Folgendes ist der HTML-Code:

<form action="validate.php" method="POST">
		<select name="entry" id="entries" size="7">
			<option value="1">Option 1</option>
			<option value="2">Option 2</option>
			<!-- ... -->
			<option value="other">Other Option</option>
		</select>
		<input type="submit" value="Vote" name="vote">
	</form>

Die IDs (bis auf other) kommen aus einer Datenbank und werden danach wieder geprüft.

UPDATE table SET votes = votes + 1 WHERE entry_id = ?; -- ? ist die ID die übergeben wird

Wie man sieht kann aber other auch übergeben werden und eig. erwarte ich, dass das ganze scheitert.
Warum erwarte ich es? Ich sage mit mysqli_stmt_bind_param, dass die Variable ein Integer sein soll.

mysqli_stmt_bind_param($stmt, 'i', $entry_id);

Allerdings liefert diese (und jegliche andere Methode) true bzw. keinen Fehler.

Ich denke ich habe einen simplen Fehler im Programm, weshalb ich keinen Fehler erhalte.

Hier mein ganzes PHP-Script:

// internal method which calls "mysqli_connect" and returns that
$con = open_connection($servername, $username, $password, $database);

$entry_id = $_POST['entry'];

// Lock Database
mysqli_autocommit($con, false);
//mysqli_begin_transaction($con, MYSQLI_TRANS_START_READ_WRITE);  // disabled because too old mysql version

$sql = "UPDATE table SET votes = votes + 1 WHERE entry_id = ?;";
$stmt = mysqli_prepare($con, $sql);
if (!$stmt) {
	die("An Error occurred while updating the Table. Error: '" . mysqli_error($con)) . "'";
}

$success = mysqli_stmt_bind_param($stmt, 'i', $entry_id);
if (!$success)  {
	die("An Error occurred while updating the Table. Error: '" . mysqli_error($con)) . "'";
}
$success  = mysqli_stmt_execute($stmt);
if (!$success)  {
	die("An Error occurred while updating the Table. Error: '" . mysqli_error($con)) . "'";
}
$success = mysqli_stmt_close($stmt);
if (!$success)  {
	die("An Error occurred while updating the Table. Error: '" . mysqli_error($con)) . "'";
}

// Commit
$success = mysqli_commit($con);
if (!$success)  {
	die("An Error occurred while updating the Table. Error: '" . mysqli_error($con)) . "'";
}

// Unlock Database
mysqli_close($con);

Klar könnte ich auch vorher auf $entry_id == "other" prüfen aber ich finde das unsauber und würde es gerne ordentlich lösen (eben mit Fehlerbehandlung).

Ich wäre glücklich, wenn jemand wüsste wo mein Fehler ist, habe gestern 2 Stunden versucht es zu fixen ohne Erfolg.

LG, DMan


#2

Das ‘i’ wird vmtl. intern sowas wie intval('other') oder (int) 'other' machen, und so dein ‘other’ zu 0 übersetzen. Teste doch mal ob das der Fall ist, indem du einen Datensatz für die entry_id 0 anlegst.

Aber wenn du in deinem Formular das ‘other’ als valide Eingabe vorgibst, wieso möchtest du damit dann unbedingt in einen Fehler laufen? Fang das doch vorher ordentlich ab, dann kannst du es auch davon unterscheiden, ob jemand das Formular manipuliert hat und was ganz anderes gesendet hat.


#3

Das ‘i’ wird vmtl. intern sowas wie intval(‘other’) oder (int) ‘other’ machen, und so dein ‘other’ zu 0 übersetzen. Teste doch mal ob das der Fall ist, indem du einen Datensatz für die entry_id 0 anlegst.

i war “expected as int” laut Doku. Aber genau das ist passiert. omg
Vielen lieben Dank dir.

Ich habe schon angefangen das Formular so zu bauen, dass es vor HTML-Injection geschützt ist (Hidden Field das beim Absenden auf den Namen des Select-Elements gesetzt wird + ich prüfe ob das Hidden Field nicht entfernt wurde + ob Name im Hidden Field mit ID übereinstimmt (SELECT * FROM table WHERE id = id AND name = hiddenField, results > 0?)).

Aber wenn du in deinem Formular das ‘other’ als valide Eingabe vorgibst, wieso möchtest du damit dann unbedingt in einen Fehler laufen?

Eben für den Fall, dass jemand aus dem Other etwas anderes gemacht hat.
Also sollte ich am besten vor dem Versenden prüfen, ob (int)$entry_id == 0 ist (oder so ähnlich)? Oder wie kann / sollte ich das prüfen?

Vielen Dank dir auf jeden Fall schon einmal, damit hab ich echt nicht gerechnet…

LG, DMan


#4

Mir ist keine PHP-Funktion bekannt die prüft ob eine Variable nur 0-9 beinhaltet. is_int() prüft ja wirklich auf den Typ der Variable und scheitert bei ‘123’ als String und andere Funktionen erlauben auch Kommazahlen und solche Scherze.

Ich würde daher einfach mit Regex prüfen:

if (preg_match(’/^[0-9]+$/’, $entry_id)) {
// $entry_id beinhaltet nur Zahlen (0-9)
} elseif ($entry_id==‘other’) {
// $entry_id beinhaltet ‘other’
} else {
// $entry_id wurde offenbar manipuliert, unerwarteter Wert
}


#5

Habe es im Endeffekt ein wenig anders gelöst (Anfrage zugelassen + Ergebnis mehr als 0 Zeilen) aber den RegEx auch verwendet für den Fall, dass jemand JS deaktiviert und mein hidden-Field damit austrickst, daher vielen Dank für die Antwort und auch dafür, dass int("irgend ein String") immer 0 in PHP und es keine Fehlermeldung zurück gibt. :smiley:

LG, Daniel