SQL-Fehlerbehebung
- Lass Dir Fehler ausgeben
- Lass Dir die Query ausgeben
- Keywords
- Proaktive Maßnahmen
- Mehrzeilige Queries, Backticks, Stringbegrenzer
- Last but not least
Lass Dir Fehler ausgeben
Nachfolgend Beispiele zur Fehlerausgabe. Natürlich kann man auch bei der Entwicklung schon vernünftig mit Exceptions arbeiten. In jedem Fall haben im Live-System die()
-Anweisungen nichts zu suchen! Technische Fehlermeldungen sind nicht für Anwender gedacht und beinhalten ein Gefahrenpotential, weil Sie potentiellen Angreifern wertvolle Informationen liefern.
In der Entwicklungsumgebung
mysqli_query($dblink, $query) or die(mysqli_error($dblink));
// oder besser:
if (false === mysqli_query($dblink, $query)) {
die(mysqli_error($dblink));
}
In der Produktivumgebung
if (false === mysqli_query($dblink, $query)) {
// Deine Fehlerbehandlung hier, z.B.
throw new Exception(mysqli_error($dblink), mysqli_errno($dblink));
}
Zentrale Angabe
Die Art der Fehlerausgabe kann auch zentral für die DB-Verbindung definiert werden:
- mysqli: http://php.net/manual/de/mysqli-driver.report-mode.php
- PDO: http://php-de.github.io/jumpto/pdo/#create-conn -> Parameter
PDO::ATTR_ERRMODE
Lass Dir die Query ausgeben
PHP-generierte Queries sind meistens dynamisch (also mit variablen Parametern) und werden damit schnell unübersichtlich:
- Wo befinden sich Leerzeichen
- Wo befinden sich Stringbereiche
- Welche Zeichen sind escaped
- Welche Variableninhalte wurden eingetragen
Bei der Fehlersuche sollte die erste Maßnahme sein, sich die „gerenderte“ Query ausgeben zu lassen, also den Querystring, der auch an die Datenbank via mysqli_query()
übergeben wird.
Keywords
Keywords sind heiße Kandidaten für unbestimmbare Fehler. Konsultiere das Datenbank-Manual und prüfe die Liste der reservierten Wörter.
Datenbanken benutzen Keywords in Ihrer Syntax, die dann nicht gleichzeitig Feldnamen sein sollten. Das kann zu schwer bestimmbaren Syntaxfehlern führen. So sind bspw. „Alter“, „Show“ oder „Limit“ klassische Feldnamen, die zu Fehlern führen. Merke: Am besten durchweg Bezeichner in Backticks einschließen.
Proaktive Maßnahmen
Schreibe mehrzeilig
Negativbeispiel - unübersichtliches Statement
$query = "SELECT Name , Age FROM Users WHERE Name LIKE '%Horst%' OR `Register` < '2008-01-01'";
Mehrzeilige Statements
- sind besser zu lesen und damit leichter zu debuggen
- ergeben in Fehlermeldungen genauere Positionsmeldungen (
near ... at line 2
)
Aber richtig!
Mehrzeilig heißt nicht, jede Zeile in PHP aus eigenenständigen Strings zu konkatenieren:
Negativbeispiel - mehrzeilig aber syntaktisch falsch
$query = "SELECT Name, Age " .
"FROM Users" . // hier entsteht ein Fehler (fehlendes Leerzeichen)
"WHERE Name LIKE '%Horst%'" .
"OR `Register` < '2008-01-01'";
Dieses Verfahren ist unnötig und auch fehleranfällig. An der oben kommentierten Stelle entsteht in der Query später ein UsersWHERE
, ohne dass das im Quellcode offenkundig ist.
MySQL stört sich nicht an Zeilenumbrüchen und auch PHP kann damit problemlos umgehen.
Positivbeispiel - übersichtliches Statement
$query = "
SELECT Name,
Age
FROM Users
WHERE Name LIKE '%Horst%'
OR `Register` < '2008-01-01'
";
Benutze Backticks (MySQL)
In Backticks eingeschlossene Begriffe kollidieren nicht mit den Syntaxbestandteilen der Sprache. Damit können Keyword-Probleme umgangen werden.
Achtung! Backticks ist MySQL-spezifisch. Wird ein anderes DBMS als MySQL benutzt (z.b. MS-SQL, PostgreSQL, …), so sind sehr warscheinlich andere Zeichen zu verwenden, um die selbe Wirkung zu erzielen. Ein Blick in die jeweilige Doku hilft hier weiter.
Benutze richtige Backticks
Backticks sind keine String-Begrenzer!
Begrenzer Bedeutung
' String
" String
` Backtick, schließt Bezeichner ein
Strings gehören nicht in Backticks, Feldnamen nicht in Stringbegrenzer. Auch wenn MySQL hier manchmal etwas weniger strikt ist, bitte gar nicht erst angewöhnen!
Benutze Backticks richtig
Richtig:
`Feldname`
`Tabellenname`.`Feldname`
`Datenbankname`.`Tabellenname`.`Feldname`
Falsch:
`Feldname `
`Tabellenname.Feldname`
Benutze Stringbegrenzer konsequent
Wie eben geschrieben erlaubt SQL zwei verschiedene Stringbegrenzer - einfache und doppelte Hochkommata. Da das gleiche für PHP gilt, ist es eine gute Idee, konsequent einen Stringbegrenzer für PHP und einen für SQL zu benuzten, um Escaping vermeiden zu können. Diese Überlegungen haben nichts mit Feldinhalten zu tun (und dort evtl. auftretenden Hochkommata); hierfür ist allein mysqli_real_escape_string()
verantwortlich!
Negativbeispiele - kollidierende Stringbegrenzer benötigen Escaping
$id = (int)$id;
$query = "SELECT `Name` ,
`Age` ,
\"Mitarbeiter\" AS Type
FROM Users
WHERE ID = $id";
$query = 'SELECT `Name` ,
`Age` ,
IF(Sex=\'m\' , \'Mitarbeiter\' , \'Mitarbeiterin\') AS Type
FROM Users';
Auch den Wechsel der für PHP und SQL zuständigen Begrenzer sollte man vermeiden (vgl. auch Ausführungen zu mehrzeiligen Statements).
Negativbeispiel - Wechsel der Stringbegrenzer ist unübersichtlich
$name = mysqli_real_escape_string($dblink, $name);
$query = 'SELECT `Name` ,
`Age`
FROM Users
WHERE `Name` LIKE "%' . $name . '%" ' .
"OR `Register` < '2008-01-01'";
Positivbeispiele - je ein Stringbegrenzer pro „Sprache“
$id = (int)$id;
$query = "SELECT `Name` ,
`Age` ,
'Mitarbeiter' AS Type
FROM Users
WHERE ID = $id";
// bzw.
$query = 'SELECT `Name` ,
`Age` ,
"Mitarbeiter" AS Type
FROM Users
WHERE ID = ' . (int)$id;
$query = 'SELECT `Name` ,
`Age` ,
IF(Sex="m" , "Mitarbeiter" , "Mitarbeiterin") AS Type
FROM Users';
$name = mysqli_real_escape_string($dblink, $name);
$query = 'SELECT `Name` ,
`Age`
FROM Users
WHERE `Name` LIKE "%' . $name . '%"
OR `Register` < "2008-01-01"';
In den jeweils ersten Beispielen wird eine Variable im String verwendet (Variablenparsing), was als Stringbegrenzer für PHP die doppelten Hochkommata erfordert. Im Positivbesipiel ist die selbe Query in Alternativschreibweise mit einfachen Hochkommata ergänzt.
Last but not least
Wenn Du trotzdem im Forum posten musst, gib uns alle Informationen, die Dir die obigen Schritte geliefert haben: sauber formatierte Query, SQL-Fehlermeldung.
Dieser Beitrag ist fertiggestellt und wurde zuletzt von hausl bearbeitet.