Prepared Statements sollen ja im wesentlichen zwei Vorteile bieten:
1) Bessere Performance, weil nicht jedesmal dasselbe Statement neu
vorbereit werden muß und
2) Mehr Sicherheit gegen SQL-Injection u.ä. Böswilligkeiten.
Da ein solches Statement aber nur so lange "lebt" wie das laufende
Script (bzw. die aktuelle DB-Verbindung), und dieses wohl nur höchst
selten dieselbe Anweisung mehrfach ausführen wird, scheint mir Punkt 1)
im Fall von Webanwendungen irrelevant zu sein. Und die von der
jeweiligen DB-Erweiterung bereitgestellten Escape-Funktionen sollten
doch ihren Job tun und damit -- Punkt 2) -- die gleiche Sicherheit
bieten. Da außerdem die Verwendung von prepared Statements etwas
umständlicher ist als normales Escapen ohne Verwendung letzterer, frage
ich mich, ob es sinnvoll ist, dieses Feature bei Webanwendungen
überhaupt zu verwenden.
Hat jemand ein paar konstruktive Denkanstöße bzw. Argumente zu dem Thema?
Gruß,
Thomas
--
Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
(Coluche)
Das stimmt nicht, in jeder Anwendung also auch in Webanwendungen ist der
Punkt sehr wichtig. Es gibt nun mal Fälle in denen man das gleiche
Statement mit verschiedenen Werten (am Besten auch noch in einer
Schleife) ausführen muss. Dafür sind diese Statements gedacht.
Das was du vielleicht meinst ist solche Prepared Statement für die
komplette Anwendung einal vorzubereiten und nur noch dieses zu
verwenden. Dafür sind diese aber glaub ich weniger gedacht.
> jeweiligen DB-Erweiterung bereitgestellten Escape-Funktionen sollten
> doch ihren Job tun und damit -- Punkt 2) -- die gleiche Sicherheit
> bieten. Da außerdem die Verwendung von prepared Statements etwas
> umständlicher ist als normales Escapen ohne Verwendung letzterer, frage
> ich mich, ob es sinnvoll ist, dieses Feature bei Webanwendungen
> überhaupt zu verwenden.
Beispiel 3 aus der PHP Seite[1]:
<?php
/* Execute a prepared statement by passing an array of insert values */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < ? AND colour = ?');
$sth->execute(array($calories, $colour));
?>
Was soll daran umständlich sein? Dir ist es also lieber für jeden
Parameter die selbe Methode auzurufen, als sie einfach im Query durch
"?" zu ersetzen und im Execute mitzugeben?
[1] http://de2.php.net/manual/de/pdostatement.execute.php
--
Mit freundlichen Grüßen,
Christoph Herrmann
Hättest Du ein paar Beispiele für solche Fälle? Da will mir nämlich
partout nichts einfallen.
> Beispiel 3 aus der PHP Seite[1]:
>
> <?php
> /* Execute a prepared statement by passing an array of insert values */
> $calories = 150;
> $colour = 'red';
> $sth = $dbh->prepare('SELECT name, colour, calories
> FROM fruit
> WHERE calories < ? AND colour = ?');
> $sth->execute(array($calories, $colour));
> ?>
>
> Was soll daran umständlich sein?
Mein PHP-Buch erwähnt nur die Varianten mit diesem umständlich-lästigen
bindParam()-Zeugs. Wenn es denn auch wie in diesem Beispiel geht, umso
besser. Da sollte ich mich wohl definitiv nochmal genauer mit PDO
befassen. Aber Folgendes geht wohl nicht:
"SELECT ... WHERE farbe IN ( ? )"
und dann ein Array mit Farbnamen übergeben? Und was ist mit
"INSERT ... SET ?", wobei ich hier ein Array mit Key-Value-Paaren
übergeben möchte, aus denen dann automatisch "SET key1 = val1, key2 =
val2" usw. gemacht wird? Für solcherlei Dinge müßte ich dann doch wieder
auf die "klassische" Escape-Variante zurückgreifen, oder unterschätze
ich PDO? Okay, ich werde mir dann gleich mal das PDO-Kapitel in der Doku
vornehmen.
MySQL profitiert von PS recht wenig. PS werden pro Connection gecached,
und nicht zwischen den Connections geshared. Auch sie sind also ein
Fall von CSS (Connection Scoped State).
PS nutzen Dir für 1. also nur, wenn ein Statement in einer Schleife
ausgeführt wird.
Zu 2.: Den Effekt von PS kann man in MySQL auch durch konsequent
korrekte Verwendung von Escape-Funktionen erreichen. Der Trick liegt in
der PS-API, die es sehr schwer macht, das korrekte Escapen zu
vergessen. In MySQL kann man die PS-API jedoch mit Server Side Prepares
Statements ('echten Prepared Statements') oder mit Client Side Prepared
Statements ('simulierten Prepared Statements') nutzen.
Es ist also in jedem Fall möglich, die PS API zu verwenden.
Server Side PS machen die Nutzung des Query Cache in MySQL 5.0
unmöglich. Es kann also auch passieren, daß Client Side PS schneller
sind.
Kris
Hoffe das geht so aus dem Bauch raus geschrieben, Prinzip sollte aber
klar sein:
$nummernumstellen = array(...);
$statement = $pdo->prepare('update tabelle set nummer = ? where nummer =
?');
foreach ($nummernumstellen as $alt => $neu) {
$statement->execute(array($neu, $alt));
}
> Mein PHP-Buch erwähnt nur die Varianten mit diesem umständlich-lästigen
> bindParam()-Zeugs. Wenn es denn auch wie in diesem Beispiel geht, umso
> besser. Da sollte ich mich wohl definitiv nochmal genauer mit PDO
> befassen. Aber Folgendes geht wohl nicht:
Mir gefällt PDO ja, wobei ich ja die Datenbankklassen des Frameworks
nutze (Zend).
> "SELECT ... WHERE farbe IN ( ? )"
>
> und dann ein Array mit Farbnamen übergeben? Und was ist mit
Ehrlich gesagt keine Ahnung. :) Musst du schauen, hab ich bisher eher
selten gebraucht. Da es sich hier nur um Werte handelt könnte es aber
technisch gesehen gehen.
> "INSERT ... SET ?", wobei ich hier ein Array mit Key-Value-Paaren
> übergeben möchte, aus denen dann automatisch "SET key1 = val1, key2 =
> val2" usw. gemacht wird? Für solcherlei Dinge müßte ich dann doch wieder
> auf die "klassische" Escape-Variante zurückgreifen, oder unterschätze
Das geht auf jeden Fall nicht. In Prepared Statements muss die Struktur
definiert werden, es können nur Werte durch Platzhalter ersetzt und
gebunden werden. Daher Tabellennamen oder Spaltennamen dürfen nicht
ersetzt werden.
> Aber Folgendes geht wohl nicht:
>
> "SELECT ... WHERE farbe IN ( ? )"
>
> und dann ein Array mit Farbnamen übergeben?
Das geht schon. Du musst nur genügend Fragezeichen einfügen:
$farbnamen = array(...); // Array mit Farbnamen
$sql = 'SELECT ... WHERE farbe IN ('
. str_repeat('?,', count($farbnamen) - 1) . '?)';
$statement = $pdo->prepare($sql);
$statement->execute($farbnamen);
> Und was ist mit
> "INSERT ... SET ?", wobei ich hier ein Array mit Key-Value-Paaren
> übergeben möchte, aus denen dann automatisch "SET key1 = val1, key2 =
> val2" usw. gemacht wird?
Das geht nicht mehr ganz so einfach, da PDO nur Platzhalter für Daten,
nicht aber für Namen kennt. Aber es geht:
$keyval = array(...); // Array mit Key-Value-Paaren
$sql = 'INSERT ... SET '
. join(' = ?, ', array_keys($keyval)) . ' = ?');
$statement = $pdo->prepare($sql);
$statement->execute($farbnamen);
Gruß. Claus
> $sql = 'INSERT ... SET '
> . join(' = ?, ', array_keys($keyval)) . ' = ?');
Besser wäre wohl:
$sql = 'INSERT ... SET `'
. join('` = ?, `', array_keys($keyval)) . '` = ?');
Dann klappt's auch mit Keys, die einem reservierten Wort entsprechen.
Gruß. Claus
[Beispiel für Mehrfachnutzung von prepared Statements]
> $nummernumstellen = array(...);
> $statement = $pdo->prepare('update tabelle set nummer = ? where nummer =
> ?');
> foreach ($nummernumstellen as $alt => $neu) {
> $statement->execute(array($neu, $alt));
> }
Okay, das wäre so ein Fall. Wobei ich mich allerdings frage, ob SQL hier
nicht doch irgendeine Möglichkeit anbietet, die es erlaubt, die
Umstellung mit einer einzigen Query zu bewerkstelligen.
Gruß und Dank,
>> "SELECT ... WHERE farbe IN ( ? )"
>> und dann ein Array mit Farbnamen übergeben?
> Das geht schon. Du musst nur genügend Fragezeichen einfügen:
>
> $farbnamen = array(...); // Array mit Farbnamen
> $sql = 'SELECT ... WHERE farbe IN ('
> . str_repeat('?,', count($farbnamen) - 1) . '?)';
> $statement = $pdo->prepare($sql);
> $statement->execute($farbnamen);
Stimmt, so würde es gehen. Aber um das wirklich so generisch
hinzubekommen, wie ich es gerne hätte, nämlich:
$query = 'SELECT * FROM fruits WHERE calories < ? AND color IN ( ? )';
$calories = 150;
$colors = array( 'red', 'green', 'yellow' );
$db->query( $sql, $calories, $colors );
müßte meine $db->query()-Methode das Statement erstmal selbst nach dem
zweiten Fragezeichen durchsuchen, und dieses entsprechend
"vervielfältigen", bevor sie das Ganze an PDO weiterreicht. Und das
stelle ich mir nicht ganz so einfach vor, da Fragezeichen in Strings und
Kommentaren ja nicht zählen und MySQL je nach Modus unterschiedliche
Escape-Formen in Strings unterstützt, wenn ich mich recht erinnere.
>> "INSERT ... SET ?", wobei ich hier ein Array mit Key-Value-Paaren
>> übergeben möchte, aus denen dann automatisch "SET key1 = val1, key2 =
>> val2" usw. gemacht wird?
> Das geht nicht mehr ganz so einfach, da PDO nur Platzhalter für Daten,
> nicht aber für Namen kennt. Aber es geht:
>
> $keyval = array(...); // Array mit Key-Value-Paaren
> $sql = 'INSERT ... SET '
> . join(' = ?, ', array_keys($keyval)) . ' = ?');
> $statement = $pdo->prepare($sql);
> $statement->execute($farbnamen);
Okay, mit etwas Bastelei geht also auch das. Inzwischen habe ich mir
auch mal den Abschnitt zu PDO in der PHP-Doku durchgelesen und einen
sehr positiven Eindruck bekommen. Für weitere Fragen zu PDO werde ich
ggf. aber lieber einen neuen Thread aufmachen.
Gruß und Dank,
> Besser wäre wohl:
>
> $sql = 'INSERT ... SET `'
> . join('` = ?, `', array_keys($keyval)) . '` = ?');
>
> Dann klappt's auch mit Keys, die einem reservierten Wort entsprechen.
Stimmt. Und um gaaanz sicher zu gehen, sollte ich noch sicherstellen,
daß die Keys keine unliebsamen Zeichen (wie eben z.B. "`") enthalten.
Oder lieber gleich "festlegen", daß Keys nur /[A-Za-z_][A-Za-z0-9_]*/
sein dürfen? Das wäre zwar theoretisch eine Einschränkung gegenüber dem,
was SQL zuläßt, aber praktisch ist mir bisher noch kein Tabellen- oder
Spaltenname untergekommen, der sich nicht an obigen Regex gehalten hätte.
> Claus Reibenstein schrieb:
>
>> Das geht schon. Du musst nur genügend Fragezeichen einfügen: [...]
>
> Stimmt, so würde es gehen. Aber um das wirklich so generisch
> hinzubekommen, wie ich es gerne hätte, nämlich:
>
> $query = 'SELECT * FROM fruits WHERE calories < ? AND color IN ( ? )';
> $calories = 150;
> $colors = array( 'red', 'green', 'yellow' );
> $db->query( $sql, $calories, $colors );
>
> müßte meine $db->query()-Methode das Statement erstmal selbst nach dem
> zweiten Fragezeichen durchsuchen, und dieses entsprechend
> "vervielfältigen", bevor sie das Ganze an PDO weiterreicht.
Wenn Du es derart generisch haben möchtest, leite Dir Deine eigene
Klasse aus PDO ab und erweitere diese entsprechend.
> Und das
> stelle ich mir nicht ganz so einfach vor, da Fragezeichen in Strings und
> Kommentaren ja nicht zählen und MySQL je nach Modus unterschiedliche
> Escape-Formen in Strings unterstützt, wenn ich mich recht erinnere.
Welche Strings? Welche Kommentare? Und welche "unterschiedlichen
Escape-Formen"?
In einem Prepared Statement hast Du keine Strings (die kommen in die
Daten) und auch keine Kommentare (ich wüsste nicht, wozu die gut sein
sollten), und gerade das Escapen ist meines Erachtens eines der
Hauptargumente für PDO, weil man sich darum eben nicht mehr kümmern muss.
Gruß. Claus
> Stimmt. Und um gaaanz sicher zu gehen, sollte ich noch sicherstellen,
> daß die Keys keine unliebsamen Zeichen (wie eben z.B. "`") enthalten.
> Oder lieber gleich "festlegen", daß Keys nur /[A-Za-z_][A-Za-z0-9_]*/
> sein dürfen?
Würde ich nicht machen.
> Das wäre zwar theoretisch eine Einschränkung gegenüber dem,
> was SQL zuläßt, aber praktisch ist mir bisher noch kein Tabellen- oder
> Spaltenname untergekommen, der sich nicht an obigen Regex gehalten hätte.
Ist bei mir leider schon viel zu oft gewesen als ich z.B. Selbstversuche
verschiedener PHP-Anfänger richten/ausbauen sollte.
Aber ist ja nun wirklich kein Problem den Regex entsprechend anzupassen.
Allerdings ist halt gerad bei Nutzung von PDO darauf zu achten das damit
ein Werkzeug genutzt wird das dafür entworfen wurde möglichst flexibel
bezüglich der Nutzbarkeit unterschiedlicher Datenquelltypen zu sein.
Wenn damit SQL genutzt wird das fuer nur eine oder zwei DBMS-Arten
lauffähig ist dann machts wenig Sinn PDO nutzen zu wollen. Da kann man
auch auf MySqlI ausweichen welches nur für MySql ist und ebenfalls
Prepared Statements ermöglicht.
Ist zwar letztendlich nicht sooooo schlimm wenn man trotzdem PDO nutzt,
aber wenn es ein besser passendes Werkzeug gibt, warum nicht das?
MfG, Ulf
> Und welche "unterschiedlichen Escape-Formen"?
Lt. MySQL-Doku 'Wie geht''s?' oder 'Wie geht\'s?' und je nach
eingestelltem Modus kann oder kann man nicht auch doppelte
Anführungszeichen verwenden, die je nach Modus auch zur Kennzeichnung
von Bezeichnern dienen, dort dann aber wohl ohne
Backslash-Escape-Möglichkeit, und schließlich müßten auch noch die
Backticks berücksichtigt werden (so wie ich die Doku verstanden habe,
darf ein Bezeichner z.B. durchaus ein Fragezeichen enthalten):
<http://dev.mysql.com/doc/refman/5.0/en/string-syntax.html>
<http://dev.mysql.com/doc/refman/5.0/en/identifiers.html>
Das alles ohne Kopfschmerzen zu verstehen und auch noch angemessen zu
berücksichtigen, ist nicht trivial.
> In einem Prepared Statement hast Du keine Strings (die kommen in die
> Daten)
Kann es nicht vorkommen, daß ein Teil der Daten eben nicht variabel sein
soll, sondern immer einen festen Wert hat?
... WHERE calories < ? AND genehmigung = "zum Verzehr freigegeben"
Zugegeben, ein idiotisches Beispiel, aber kann ich wirklich ohne
Beschränkung der Allgemeinheit ausschließen, daß ein fester String
drinsteht? Wäre schließlich gültige SQL-Syntax. Man könnte natürlich für
solche Fälle aus dem String noch ein Fragezeichen machen und einen
zusätzlichen Parameter nehmen, der den konstanten Wert "zum Verzehr
freigegeben" hat. Doch das erscheint mir weniger elegant zu sein.
Und was ist eigentlich mit
... WHERE name LIKE '%?%'
was in dieser Form wohl so nicht geht. In der PDO-Doku steht, daß es
angeblich mit $name = "%$name%" und bindParam() funktionieren soll, aber
warum das funktionieren soll, verstehe ich irgendwie nicht.
> und auch keine Kommentare (ich wüsste nicht, wozu die gut sein
> sollten)
Natürlich kann man sie weglassen, aber da sie gültiger Bestandteil der
SQL-Syntax (und bei komplexeren Statements vielleicht durchaus nützlich)
sind, sollte meine Wrapper-Klasse damit umgehen können.
> und gerade das Escapen ist meines Erachtens eines der
> Hauptargumente für PDO, weil man sich darum eben nicht mehr kümmern muss.
Da gebe ich Dir recht -- außerdem ist PDO im Vergleich zu mysql[i]_*
wesentlich übersichtlicher.
Gruß,
[Keys nur /[A-Za-z_][A-Za-z0-9_]*/ ...]
> Würde ich nicht machen.
[... meiner Erfahrung nach kommen in der Praxis keine anderen Keys vor]
> Ist bei mir leider schon viel zu oft gewesen als ich z.B. Selbstversuche
> verschiedener PHP-Anfänger richten/ausbauen sollte.
Gut, bei Anfängern. Aber bei Profis?
> Aber ist ja nun wirklich kein Problem den Regex entsprechend anzupassen.
Auch wieder wahr. Trotzdem ist es natürlich "schöner", wenn ich die
Spaltennamen in PHP direkt als Attribute eines Row-Objekts ansprechen kann.
> Allerdings ist halt gerad bei Nutzung von PDO darauf zu achten das damit
> ein Werkzeug genutzt wird das dafür entworfen wurde möglichst flexibel
> bezüglich der Nutzbarkeit unterschiedlicher Datenquelltypen zu sein.
> Wenn damit SQL genutzt wird das fuer nur eine oder zwei DBMS-Arten
> lauffähig ist dann machts wenig Sinn PDO nutzen zu wollen.
Ist es nicht in jedem Fall praktischer, eine Klasse zu haben, die sich
"um den ganzen DB-Kram kümmert" und mich von den Eigenheiten der
jeweiligen konkreten DB verschont?
> Claus Reibenstein schrieb:
>
>> Und welche "unterschiedlichen Escape-Formen"?
>
> Lt. MySQL-Doku 'Wie geht''s?' oder 'Wie geht\'s?' und je nach
> eingestelltem Modus kann oder kann man nicht auch doppelte
> Anführungszeichen verwenden, die je nach Modus auch zur Kennzeichnung
> von Bezeichnern dienen, dort dann aber wohl ohne
> Backslash-Escape-Möglichkeit, und schließlich müßten auch noch die
> Backticks berücksichtigt werden (so wie ich die Doku verstanden habe,
> darf ein Bezeichner z.B. durchaus ein Fragezeichen enthalten):
Um die Bezeichner kümmert sich PDO nicht. Das bleibt Dir überlassen. Dem
Rest muss ich wohl zustimmen.
>> In einem Prepared Statement hast Du keine Strings (die kommen in die
>> Daten)
>
> Kann es nicht vorkommen, daß ein Teil der Daten eben nicht variabel sein
> soll, sondern immer einen festen Wert hat?
>
> .... WHERE calories < ? AND genehmigung = "zum Verzehr freigegeben"
So etwas kann natürlich vorkommen. Aber dann hast Du wieder ein Element,
um dessen korrektes "Escapen" Du Dich wieder selber kümmern musst.
> Man könnte natürlich für
> solche Fälle aus dem String noch ein Fragezeichen machen und einen
> zusätzlichen Parameter nehmen, der den konstanten Wert "zum Verzehr
> freigegeben" hat.
Damit bist Du das Escape-Problem los.
> Doch das erscheint mir weniger elegant zu sein.
Geschmackssache.
> Und was ist eigentlich mit
>
> .... WHERE name LIKE '%?%'
>
> was in dieser Form wohl so nicht geht.
... WHERE name LIKE CONCAT('%', ?, '%')
Oder ich baue die '%' auf PHP-Ebene in die Daten mit ein.
>> und auch keine Kommentare (ich wüsste nicht, wozu die gut sein
>> sollten)
>
> Natürlich kann man sie weglassen, aber da sie gültiger Bestandteil der
> SQL-Syntax (und bei komplexeren Statements vielleicht durchaus nützlich)
> sind, sollte meine Wrapper-Klasse damit umgehen können.
Wenn ich Kommentare brauche, schreibe ich sie in den PHP-Code und nicht
ins SQL-Statement.
Gruß. Claus
[Zusätzlicher Parameter, um Strings in SQL-Statement zu vermeiden]
>> Doch das erscheint mir weniger elegant zu sein.
> Geschmackssache.
Du hättest demnach kein geschmackliches Problem mit dieser
Vorgehensweise? Gut, wenn es keinerlei objektive Bedenken gibt, sollte
ich das wohl als die vernünftigste Lösung in Betracht ziehen.
> ... WHERE name LIKE CONCAT('%', ?, '%')
> Oder ich baue die '%' auf PHP-Ebene in die Daten mit ein.
Danke für das Entfernen des Bretts vor meinem Kopf. Die Welt sieht nun
wieder etwas klarer aus :-)
>Thomas Mlynarczyk schrieb:
>
>> Und was ist eigentlich mit
>>
>> .... WHERE name LIKE '%?%'
>>
>> was in dieser Form wohl so nicht geht.
>
> ... WHERE name LIKE CONCAT('%', ?, '%')
>
>Oder ich baue die '%' auf PHP-Ebene in die Daten mit ein.
Ich würde definitiv die 2.Methode wählen. Hier ein CONCAT() verursacht
mir ziemliche Magenschmerzen. Die '%' gehören zum String und sollten
daher auf PHP-Ebene eingefügt werden, so daß im SQL dann nur noch steht
... WHERE name LIKE ?
Micha
Das kannst Du machen, wenn Deine Attribute statisch sind. Ich persönlich
arbeite eigentlich viel lieber mit assoziativen Arrays, statt mit
Row-Objekten.
>> Allerdings ist halt gerad bei Nutzung von PDO darauf zu achten das
>> damit ein Werkzeug genutzt wird das dafür entworfen wurde möglichst
>> flexibel bezüglich der Nutzbarkeit unterschiedlicher Datenquelltypen
>> zu sein.
>> Wenn damit SQL genutzt wird das fuer nur eine oder zwei DBMS-Arten
>> lauffähig ist dann machts wenig Sinn PDO nutzen zu wollen.
>
>
> Ist es nicht in jedem Fall praktischer, eine Klasse zu haben, die sich
> "um den ganzen DB-Kram kümmert" und mich von den Eigenheiten der
> jeweiligen konkreten DB verschont?
Wenn Du das willst, kannst Du ja gleich Symphonie/Prospel benutzen. Bei
einfachen Problemstellungen kannst Du mehr oder weniger ohne Probleme
PDO oder Ado benutzen. In komplexeren Datenbanken benötigt man aber auch
oft SQL-Statements, die dann doch wieder sehr datenbankspezifisch sind.
Zumindest wenn das ganze dann noch performant bleiben soll.
[Spaltennamen in PHP direkt als Attribute eines Row-Objekts ansprechen]
> Das kannst Du machen, wenn Deine Attribute statisch sind. Ich persönlich
> arbeite eigentlich viel lieber mit assoziativen Arrays, statt mit
> Row-Objekten.
Verstehe ich nicht ganz. Bei einer Abfrage wie
"SELECT name, id FROM users WHERE id = ?"
weiß ich doch, daß ich entweder nichts oder eine Zeile mit den
Attributen name und id zurückbekomme. Und ob ich nun mit
$zeile['id'] bzw. $zeile['name'] oder $zeile->id bzw. $zeile->name
darauf zugreife ist ja eigentlich egal. Nur, daß ich statt eines
generischen Row-Objekts sinnvollerweise hier ein User-Objekt nehmen
würde: $user->id, $user->name. Ich frage die DB nach einem bestimmten
User -- und bekomme ihn (bzw. "sein" Objekt) zurückgeliefert (oder NULL,
falls er nicht existiert).
[eine Klasse, die sich "um den ganzen DB-Kram kümmert"]
> Wenn Du das willst, kannst Du ja gleich Symphonie/Prospel benutzen.
Ja, könnte ich. Aber ich gehöre zu der Sorte Leute, die alles selbst
entwickeln wollen, auch wenn's umständlicher ist. ;-)
> In komplexeren Datenbanken benötigt man aber auch
> oft SQL-Statements, die dann doch wieder sehr datenbankspezifisch sind.
Nur die Statements austauschen/anpassen ist ja eigentlich kein Problem.
Ich will nur nicht bei jedem DB-Zugriff über Aufrufdetails nachdenken
müssen:
// Irgendwo in der Config - bei Bedarf leicht anpaßbar
$verbindungsdaten = 'mysql:user@password//server/database';
$getUsersByIdList = 'SELECT * FROM users WHERE id IN ( ? )';
// Im eigentlichen Script
$userDb = new UserDb( $verbindungsdaten );
$users = $userDb->query( $getUsersByIdList, $ids );
foreach ( $users as $user ) $view->addToOutput( $user );
Irgendwo brauche ich natürlich noch eine Möglichkeit anzugeben, welche
Klasse für die zurückgegebenen User verwendet werden soll. Unter
direkter Verwendung von PDO ginge obiges wohl nicht mit nur 3 Zeilen.
Am sinnvollsten wäre es wohl, wenn ich -- wie hier schon vorgeschlagen
wurde -- einfache eine Klasse von PDO ableite und ihr ein einfacheres
Interface verpasse. (Ist das nicht das Facade-Pattern?) Das entlastet
mich bei Routineabfragen und erlaubt mir trotzdem, bei Bedarf auf alle
PDO-Funktionen zuzugreifen.
Ganz einfach. Du hast nicht ein "SELECT name,id FROM...", sondern ein
"SELECT name,id,$additionalFields FROM..." wo sich die übergebenen
Parameter ändern können. Dann finde ich das mit einem Objekt nicht so
elegant, wenn man z.B. ein foreach ($additionalFieldArray as $name ...)
macht. Das geht mit einem Objekt zwar auch ($user->$name) aber ich finde
es mit einen assoziativen Array eleganter. Und ich kann mein
Ergebnisarray dann einfach per foreach
Ich habe halt in einigen Projekten sehr oft wechselnde Felder, abhängig
davon, wie das Programm für den Kunden konfiguriert ist. Dummerweise
lässt sich das auch nicht elegant über Vererbungen lösen.
>
> [eine Klasse, die sich "um den ganzen DB-Kram kümmert"]
>
>> Wenn Du das willst, kannst Du ja gleich Symphonie/Prospel benutzen.
>
>
> Ja, könnte ich. Aber ich gehöre zu der Sorte Leute, die alles selbst
> entwickeln wollen, auch wenn's umständlicher ist. ;-)
>
>> In komplexeren Datenbanken benötigt man aber auch oft SQL-Statements,
>> die dann doch wieder sehr datenbankspezifisch sind.
>
>
> Nur die Statements austauschen/anpassen ist ja eigentlich kein Problem.
Naja, das ist ja eigentlich das Problem. Das Austauschen der Statements
ist ja gerade das Problem. Es wäre ja eigentlich schön, wenn ich nur die
DB austauschen bräuchte. Ich muss mir dann aber immer die Statements
anschauen und Anpassen. Aber leider geht es nicht anders, zumindest wenn
man ein DB auf Performance trimmen muss.
> Ich will nur nicht bei jedem DB-Zugriff über Aufrufdetails nachdenken
> müssen:
>
> // Irgendwo in der Config - bei Bedarf leicht anpaßbar
> $verbindungsdaten = 'mysql:user@password//server/database';
> $getUsersByIdList = 'SELECT * FROM users WHERE id IN ( ? )';
>
> // Im eigentlichen Script
> $userDb = new UserDb( $verbindungsdaten );
> $users = $userDb->query( $getUsersByIdList, $ids );
> foreach ( $users as $user ) $view->addToOutput( $user );
>
> Irgendwo brauche ich natürlich noch eine Möglichkeit anzugeben, welche
> Klasse für die zurückgegebenen User verwendet werden soll. Unter
> direkter Verwendung von PDO ginge obiges wohl nicht mit nur 3 Zeilen.
>
> Am sinnvollsten wäre es wohl, wenn ich -- wie hier schon vorgeschlagen
> wurde -- einfache eine Klasse von PDO ableite und ihr ein einfacheres
> Interface verpasse. (Ist das nicht das Facade-Pattern?) Das entlastet
> mich bei Routineabfragen und erlaubt mir trotzdem, bei Bedarf auf alle
> PDO-Funktionen zuzugreifen.
So in der Form ist das natürlich ohne weiteres machbar. Allerdings dann
nicht so DB-unabhängig, wie man sich das wünschen würde.
Gruß
Stefan
> Ganz einfach. Du hast nicht ein "SELECT name,id FROM...", sondern ein
> "SELECT name,id,$additionalFields FROM..." wo sich die übergebenen
> Parameter ändern können. Dann finde ich das mit einem Objekt nicht so
> elegant, wenn man z.B. ein foreach ($additionalFieldArray as $name ...)
Ich kann doch auch über Objekte iterieren:
class Row implements Iterator { ... }
foreach ( $row as $col => $val ) { ... }
Ich kann sogar noch ArrayAccess und Countable implementieren und dann
wahlweise auch mit Array-Syntax auf das Row-Objekt zugreifen. In Deinem
Beispiel "SELECT name,id,$additionalFields FROM..." greife ich ja in
jedem Fall auf dieselbe Tabelle zu, z.B. "Users". Dann stehen mir
$user->name und $user->id immer zur Verfügung, aber $user->haarfarbe
liefert mir ggf. einfach `null` zurück, wenn die Haarfarbe nicht bei den
$additionalFields dabei war. Das Ganze funktioniert natürlich auch mit
assoziativen Arrays, aber meinem Row-(User-)Objekt kann ich noch ein
bißchen Intelligenz verpassen zur Validierung der zurückgelieferten
Werte, Ändern derselben u.ä.
> Ich habe halt in einigen Projekten sehr oft wechselnde Felder, abhängig
> davon, wie das Programm für den Kunden konfiguriert ist. Dummerweise
> lässt sich das auch nicht elegant über Vererbungen lösen.
Eventuell mit Komposition:
$car->id, $car->type, $car->price // "feste" Felder
$car->extras->swimmingpool // Extras als Unterobjekt
Aber ansonsten: das Row-Objekt hat Attribute für alle Felder der Tabelle
und die "unbenutzten" liefern halt `null` (oder '' oder `false`) zurück
(oder schmeißen eine Exception). Ich sehe da keinen prinzipiellen
Unterschied zu oder gar Nachteil gegenüber assoziativen Arrays.
[DB-Wechsel: Statements austauschen/anpassen ist kein Problem]
> Naja, das ist ja eigentlich das Problem. Das Austauschen der Statements
> ist ja gerade das Problem. Es wäre ja eigentlich schön, wenn ich nur die
> DB austauschen bräuchte. Ich muss mir dann aber immer die Statements
> anschauen und Anpassen. Aber leider geht es nicht anders, zumindest wenn
> man ein DB auf Performance trimmen muss.
Stimmt. Aber gerade komplexere Statements würde ich ggf. sogar in eine
extra Datei auslagern bzw. zumindest nicht direkt "hartkodieren". Dann
habe ich *eine* Stelle, wo ich alle Statements beieinander habe und in
Ruhe anpassen kann (bzw. einfach nur die Statements-Datei austauschen).
Wenn ich zusätzlich noch den PHP-Code ändern müßte, wäre das Ganze schon
aufwendiger.
[Fassadenklasse von PDO ableiten]
> So in der Form ist das natürlich ohne weiteres machbar. Allerdings dann
> nicht so DB-unabhängig, wie man sich das wünschen würde.
Hängt natürlich davon ab, was man in die Fassade einbaut. Aber da die
Fassade ja intern auch nur alles an PDO weiterreicht (und ich bei Bedarf
auch direkt auf die PDO-Funktionen zugreifen kann), wüßte ich jetzt
nicht, inwiefern mir da wesentliche Unabhängigkeit verloren ginge, die
mir PDO bietet. Für "normale Durchschnittsanwendungen" sollte es allemal
genügen. Und wenn nicht, kann ich immer noch die Fassade austauschen
(immer noch das gleiche Interface für die Anwendung, aber unter der
Haube wird die Sache anders gehandhabt). Dann wäre das auch nur eine
Datei, die ich austauschen muß und der Aufwand hält sich in Grenzen.
Wenn das Row-Objekt ein reines Row-Objekt ist, dann stimmt das. Aber oft
ist es ja so, dass ein Objekt mehr ist, als seine Repäsentation in der
DB. Wenn ich dann also über die Attribute iteriere, erhalte ich ja nicht
nur die Felder der DB, sondern auch die Attribute, die nicht in der DB
stehen.
> Wenn das Row-Objekt ein reines Row-Objekt ist, dann stimmt das. Aber oft
> ist es ja so, dass ein Objekt mehr ist, als seine Repäsentation in der
> DB. Wenn ich dann also über die Attribute iteriere, erhalte ich ja nicht
> nur die Felder der DB, sondern auch die Attribute, die nicht in der DB
> stehen.
Aber mit implementiertem Iterator kannst Du doch völlig frei selbst
bestimmen, worüber iteriert wird und worüber nicht.
Dann ist der Aufwand aber deutlich größer, als wenn ich das über mein
assoziatives Array mache.