Fehler bei Prepared Statements

Dsimon24

Lieutenant
Registriert
Aug. 2016
Beiträge
595
Hallo zusammen,

ich versuche gerade ein kleines Script mit Prepared Statements zu realisieren.

Die erste Ausführung der Funktion ($alleDatensaetze) funktioniert reibungslos.
Die zweite Ausführung der Funktion ($nurWindows) macht allerdings Probleme.
Ich vermute, dass es an dem LIKE liegt - wahrscheinlich an der Klammern-
setzung. Was mache ich da falsch bzw. wie sollte ich es richtig machen?

PHP:
function neuerName($datum, $sql)
{

  require_once 'inc/datenbank.inc.php';

  $daten = [$datum];

  $statement = $db->prepare($sql);
  $statement->execute($daten);
  $count = $statement->rowCount();

  return $count;

}

$alleDatensaetze = neuerName(bereinigeEingabe($_GET['datum']), 'SELECT * FROM log WHERE DATE(erstellt_am) = ?');

$nurWindows = neuerName(bereinigeEingabe($_GET['datum']), 'SELECT * FROM log WHERE DATE(erstellt_am) = ? AND browser LIKE '%Windows%'');

VG, David
 
Hi,

nutze " und nicht ' bei deinem LIKE, dein ' schließt das Statement

VG,
Mad
 
  • Gefällt mir
Reaktionen: sebbolein und Fusion.Tio
Ich nehm mal an Du meinst das richtige, dennoch, zur Sicherheit, vor allem, weil das Problem noch nicht adressiert ist:

— SQL will einfache Anführungszeichen als Stringbegrenzer.
Doppelte haben eine andere Bedeutung (je nach Dialekt zB Tabellenbegrenzer).
Es gibt Clients, die einen beides verwenden lassen, aber da wär ich ARG vorsichtig damit. Irgendwann WIRD es auf die Füße fallen, wenn man versucht, doppelte Anführungszeichen zu verwenden.

— PHP hat Anführungszeichen als Stringbegrenzer. Je nachdem ob die einfach oder doppelt sind, hat man unterschiedliches Verhalten.

— Beides adressiert aber das Problem nicht. So funktionieren prep'd statements nicht. Der Compiler weiß mit der Eingabe nicht, was er vorbereiten soll, und entsprechend muß jeder Aufruf neu kompiliert werden. So kann man genausogut <connection>.Execute(SQL) sagen.

Ein Prepared Statement ist mehrteilig. Es gibt den statischen Befehlstext und es gibt einen oder mehrere Parameter. Oder auch gar keinen, wenn die Abfrage in sich schon statisch ist (SELECT TOP 1 id,name FROM tabelle oä).

Prepared Statements arbeiten daher mit Platzhaltern und Parameterbindung. Der Compiler kann den Befehl kompilieren und per Aufruf Parameter an die Abfrage binden. Das sichert den Vorgang ab und es verbessert die Performance.

Beispiel, pseudo-code-mäßig:
Code:
cmd = new SQLCommand
cmd.CommandText = SELECT col1, col2, col3 FROM tabelle WHERE id = :param_id
cmd.Parameters("param_id").Value = 15 // WERT des Parameters
cmd.Parameters("param_id").Type = dbInteger // ART des Parameters, hier Integer
cmd.Parameters("param_id").Direction = dirInput  // RICHTUNG des Parameters; hier Eingabe
und jetzt kann man entweder einmal result = cmd.Execute() sagen ODER man hat eine Schleife (warum auch immer) und sagt dann beispielsweise sowas:
Code:
for i = 1 to 1000 
cmd.Parameters("param_id") = i
thisResult = cmd.Execute() // jetzt steht per Schleifendurchlauf in thisResult derDatensatz (col1,col2,col3) mit der ID "i"
end for

Verschachtelte Anführungszeichen SIND NICHT erforderlich und stören an dieser Stelle sogar.
 
  • Gefällt mir
Reaktionen: DubZ
Hi,

Ein Prepared Statement ist mehrteilig. Es gibt den statischen Befehlstext und es gibt einen oder mehrere Parameter. Oder auch gar keinen, wenn die Abfrage in sich schon statisch ist (SELECT TOP 1 id,name FROM tabelle oä).
Prepared Statements arbeiten daher mit Platzhaltern und Parameterbindung.

alles richtig, sein LIKE hat aber weder einen Platzhalter noch einen Parameter, es ist lediglich ein Syntax-Fehler, weil der innerhalb des Prep das selbe Hochkomma verwendet wie er nutzt um den Statement-String zu erstellen.

Ich kann deine Einwände auch gut verstehen, hatte aber in PHP in Kombination mit MySQL weder mit Hochkomma noch mit Anführungszeichen jemals irgendein Problem.

dennoch, zur Sicherheit, vor allem, weil das Problem noch nicht adressiert ist

naja, das Problem des TE habe ich sehr konkret addressiert - wenn er es abändert und laufen lässt wirds tun

Es wäre sicherlich wünschenswert, wenn sich der TE nochmal genauer mit dem Thema Prepared Statements auseinandersetzt, aber als schnelle Lösung sollte mein Vorschlag mehr als ausreichend sein in diesem konkreten Fall

VG,
Mad
 
Erstmal danke für die Infos.

Mit Prepared Statements werde ich mich auf jeden Fall nochmal mehr auseinandersetzen.

Leider klappt es in diesem Fall aber auch nicht mit den "-Zeichen:

PHP:
$nurWindows = neuerName(bereinigeEingabe($_GET['datum']), "SELECT * FROM log WHERE DATE(erstellt_am) = ? AND browser LIKE '%Windows%'");

Der folgende Code hingegen funktioniert einwandfrei:

PHP:
$alleDatensaetze = neuerName(bereinigeEingabe($_GET['datum']), "SELECT * FROM log WHERE DATE(erstellt_am) = ?");

Mache ich denn mglw. bei dem LIKE noch einen Fehler?

Folgenden Fehler konnte ich nun aus den logs ermitteln - leider nur nicht viel damit anfangen:
[Wed Jul 17 17:32:01.582773 2019] [php7:error] [pid 14125] [client 93.215.166.95:54650] PHP Fatal error: Uncaught Error: Call to a member function prepare() on null in /var/daten/teilnehmer/www15791/htdocs/zitat_pdo_ea/details.php:13\nStack trace:\n#0 /var/daten/teilnehmer/www15791/htdocs/zitat_pdo_ea/details.php(23): neuerName('2019-07-17', 'SELECT * FROM l...')\n#1 {main}\n thrown in .../details.php on line 13, referer: https://www.......
 
Zuletzt bearbeitet:
Dsimon24 schrieb:
Call to a member function prepare() on null
Das bezieht sich vermutlich auf
Code:
$statement = $db->prepare($sql);
Ergo ist $db nicht richtig initialisiert.
Ist das in #1 der ganze Code? Weil es schon seltsam ist, dass die 1. db-Abfrage läuft und die 2. nicht.
 
Ja, das ist der gesamte Code, der dafür zuständig ist.
 
Hi,

wenn du nur den nicht funktionierenden Aufruf machst? Geht es dann? Also den funktionierenden auskommentieren und nur den nicht funktionierenden drin lassen?

VG,
Mad
 
Ich denke was hier abläuft ist folgendes:
In der datenbank.inc.php steht vermutlich
Code:
$db = ...
Dieses wird beim 1. Aufruf von neuerName() inkludiert. Bei 2. Aufruf jedoch nicht.
Da $db nur eine lokale variable innerhalb der Funktion ist, wird diese nach jedem Aufruf auf null gesetzt. Daher funktioniert der 2. Aufruf der Funktion nicht so, wie du dir das vorstellst.
Ein Stichwort wäre hier global.
Schön geht anders (bin nur noch Objektorientierung und Namespaces gewöhnt), aber der Code sollte erst mal laufen.
 
Zurück
Oben