aichingm schrieb:
muss ich halt in $where schon escapte Werte übergeben
Puh, PHP raubt mir alle meine Argumente.
Nein, Spaß beiseite: Ich meinte ja, dass mysql_real_escape_string eine bestehende DB-Verbindung benötigt und es deshalb bei deiner Funktion nicht möglich ist, bereits vor dem ersten Aufruf zu escapen (weil keine existierende DB-Verbindung). Das stimmt auch. Nur versucht mysql_real_escape_string im Zweifel eine Verbindung aufzubauen, indem die Funktion mysql_connect ohne Argumente aufgerufen wird. (Die Daten werden dann aus der PHP-Konfiguration geholt, siehe Doku zu mysql_connect.) Meine lokale Standard-Konfiguration scheint auszureichen, um auch tatsächlich auf diese Weise eine Verbindung auf die Reihe zu kriegen. Ich musste für einen Testcase gezielt die Konfiguration überschreiben.
PHP:
<?php
error_reporting(-1);
$_GET['foo'] = 'bar"';
ini_set('mysql.default_host', 'dies-existiert-nicht');
$fooEscaped = mysql_real_escape_string($_GET['foo']);
var_dump($fooEscaped);
Na ja, das ist jedenfalls ein Problem. Default-Angaben könnten falsch gesetzt sein oder es soll zu einem anderen Server verbunden werden, der unter einer anderen MySQL-Version läuft, sodass unter Umständen für die falsche Version escapet wird (etwas akademisches Argument).
Es ist jedenfalls so, dass die Escape-Funktionalität an der Datenbankverbindung hängt und auch nur dann zuverlässig funktionieren kann (etwa wegen Verbindungs-Charset oder speziellen Eigenheiten bestimmter DB-Versionen). Deshalb ist
addslashes auch keine Alternative dazu.
zu dem global Problem:
wenn ich die function verwende verwende ich sie ja mit dem ziel nicht soviel code schreiben zu müssen deswegen wäre mein Ansatz einmal die Konfiguration zu schreiben und immer wieder zu verwenden aber ich muss dir auch recht geben. ich habe den Fall das ich eine andere Verbindung verwenden will nicht bedacht. also am schönsten (finde ich) wäre:
Du hattest die Funktion so geschrieben, dass sie nicht für sich allein funktioniert, sondern von einem weiteren Zustand abhängt (vier globale Variablen müssen gesetzt sein). Die Funktion braucht also zum Arbeiten zum einen die Eingabeparameter und zum anderen diesen Zustand.
Da du
global einsetzt, muss dieser Zustand im globalen Raum abgelegt werden und kann deshalb nur in einer Ausprägung gleichzeitig existieren, weil die notwendigen Variablen eben nur an einer einzigen Stelle im globalen Raum liegen. Wenn die Anwendung nun mit zwei Datenbanken kommunizieren möchte (wieder etwas schwieriges Szenario, aber ab und an kommt das durchaus vor), müsste sie den globalen Zustand vor dem Aufruf der Funktion immer auf die jeweiligen Werte für eine der beiden Datenbanken bringen.
Die große Frage dazu: Wie macht man das?
Vielleicht so:
PHP:
function setDb($which)
{
global $server, $mysqlDb, $mysqlUser, $mysqlUserPassword;
if ($which === 1) {
$server = "localhost";
$mysqlDb = "db1";
$mysqlUser = "UserXY";
$mysqlUserPassword = "password";
} else if ($which === 2) {
$server = "external-host";
$mysqlDb = "db_xy";
$mysqlUser = "Lucas Barrios";
$mysqlUserPassword = "bvb09ole";
}
}
$server = null;
$mysqlDb = null;
$mysqlUser = null;
$mysqlUserPassword = null;
setDb(1); // Initialisierung
Per setDb()-Funktion kann nun der Zustand für getRows() unkompliziert gesetzt werden. (Außerdem können wir die Daten an einer einzigen Stelle zentral anpassen, was natürlich ebenfalls praktisch ist.) Zu Beginn des Scripts setzen wir ihn auf die Daten von DB 1.
Allerdings muss nun vor jedem Aufruf von getRows() einmal das passende setDb() aufgerufen werden, weil wir uns nicht sicher sein können, dass unser globaler Zustand gerade passend für die gewünschte DB steht.
PHP:
setDb(2);
$data = getRows(...);
Wenn diese beiden Funktionen nun ohnehin nur noch zusammen aufgerufen werden können, warum dann nicht setDb() in getRows() setzen und die DB-Nummer als ersten Parameter übergeben?
Und wo ich so darüber nachdenke: Es braucht auch die globalen Variablen nicht mehr, denn wir können das jetzt einfach so machen:
PHP:
function setDb($which)
{
if ($which === 1) {
$server = "localhost";
$mysqlDb = "db1";
$mysqlUser = "UserXY";
$mysqlUserPassword = "password";
} else if ($which === 2) {
$server = "external-host";
$mysqlDb = "db_xy";
$mysqlUser = "Lucas Barrios";
$mysqlUserPassword = "bvb09ole";
}
return array($server, $mysqlDb, $mysqlUser, $mysqlUserPassword);
}
function getRows($dbNumber, $table, $fields, $where){
list($server, $mysqlDb, $mysqlUser, $mysqlUserPassword) = setDb($dbNumber);
/* ... */
}
(Da werden die globalen Variablen allerdings gewissermaßen auch bloß in einer globalen Funktion versteckt.)
Oder auch gleich so:
PHP:
$db1 = array(
'server' => "localhost",
'mysqlDb' => "db1",
'mysqlUser' => "UserXY",
'mysqlUserPassword' => "password"
);
$db2 = array(
'server' => "external-host",
'mysqlDb' => "db_xy",
'mysqlUser' => "Lucas Barrios",
'mysqlUserPassword' => "bvb09ole"
);
function getRows($dbData, $table, $fields, $where){
list($server, $mysqlDb, $mysqlUser, $mysqlUserPassword) = $dbData;
/* ... */
}
Noch mal kurz zusammengefasst: Das Problem bestand darin, die Zugangsdaten von zwei Datenbank-Verbindungen abwechselnd in einen einzigen globalen Zustand zu schreiben, den die getRows()-Funktion zum Arbeiten benötigt. Das konnte gelöst werden, indem eine Austausch-Funktion hinzugefügt wurde, die auf Angabe einer Nummer jeweils die passenden Daten für eine Verbindung in den globalen Zustand geschrieben hat.
Nun war allerdings nicht klar, welche Daten gerade im globalen Zustand stehen, weshalb die Wechsel-Funktion immer vor dem Aufruf von getRows() aufgerufen werden musste. Wenn die beiden Funktionen aber ohnehin nur zusammen aufgerufen werden können, lassen sie sich auch zu einer zusammenfassen, der dann benötigte Sets von Zustandsdaten gleich als Parameter übergeben werden können, was die Verwendung vom
global-Keyword unnötig macht.
Jetzt haben wir also in $db1 und $db2 jeweils einen Zustand gekapselt, der an eine Funktion übergeben werden kann. Die Funktion ergibt für sich genommen nur dann Sinn, wenn ihr diese Daten zur Verfügung gestellt werden. Sie ist also ganz eng mit diesen Zustandsdaten-Sets verknüpft.
Wenn sie aber ohnehin nur mit so einem Set funktioniert, warum packen wir sie nicht gleich mit in das Set rein und sparen den Schritt, die Zustandsdaten immer „indirekt“ von Außen übergeben zu müssen?
PHP:
class DbConnection
{
protected $server;
protected $mysqlDb;
protected $mysqlUser;
protected $mysqlUserPassword;
public function __construct($server, $mysqlDb, $mysqlUser, $mysqlUserPassword)
{
$this->server = $server;
$this->mysqlDb = $mysqlDb;
$this->mysqlUser = $mysqlUser;
$this->mysqlUserPassword = $mysqlUserPassword;
}
public function getRows($table, $fields, $where)
{
//zum Server verbinden:
mysql_connect($this->server, $this->mysqlUser, $this->mysqlUserPassword) or die(mysql_error());
//Datenbank auswählen:
mysql_select_db($this->mysqlDb) or die(mysql_error());
//Das Result vom Server abfragen:
$result = mysql_query("SELECT $fields FROM $table WHERE $where")or die(mysql_error());
//Result zeilenweise in das Array $rows speichern:
while($row = mysql_fetch_assoc($result)) {
$rows[] = $row;
}
//Das Array $rows zurück geben:
return $rows;
}
}
$db1 = new DbConnection('localhost', 'db1', 'UserXY', 'password');
$db2 = new DbConnection('external-host', 'db_xy', 'Lucas Barrios', 'bvb09ole');
$data = $db1->getRows('user', 'name, age, location', 'userId = 1');
Damit haben wir dann die Objektorientierung erfunden. Jedes Objekt verfügt über einen lokalen Zustand und über Funktionen (in diesem Kontext „Methoden“ genannt), die diesen Zustand nutzen können, ohne dass er in irgendeiner Form übergeben werden muss. Wir haben ein hübsches, kleines Paket, das alles beinhaltet, was es zum Arbeiten braucht, und das wir beliebig häufig mit anderen Daten neu erstellen können, weil es seinen Zustand eben nicht global verwaltet, sondern lokal als Teil des Pakets.
Als willkommener Nebeneffekt können die Objekte durchsetzen, dass ihr jeweiliger Zustand „existiert“. Die Methode getRows() kann hier nicht aufgerufen werden, ohne dass die entsprechenden Verbindungsdaten gesetzt sind.