PHP.de Wissenssammlung

SQL-Fehlerbehebung

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:

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.

An diesem Beitrag waren bisher beteiligt: nikosch, Chriz, Manko10, Hoefti, hausl