PHP und Datenbanken

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • Also das ganze drumher rum mit welche DBs, Interfaces etc. es alles gibt erklärst du ja wirklich schön.
      Selbst das mit "new" (sehr detailiert) usw., aber dann als es los gehen sollte fehlen auf einmal sämtliche Erklärungen.
      Es wird überhaupt nicht auf die Parameter des Verbindungsaufbaus eingegangen.
      Auch wenn das wohl für manche offensichtlich ist, wird es wohl auch Anfänger geben für die das eben nicht der Fall ist.

      Sollte bei deiner Erweiterung nicht fehlen ansonsten super ;)

      P.S.: Wenn das erst später kommen sollte und du erst sämtliche Verbindungsarten erklären willst, würde ich zumindest beim obrigen Quellcode darauf hinweisen, das die Paras weiter unten erläutert werden. Meist überspringt man die Einleitung und schaut sich direkt den vorgehobenen Quellcode an.
    • Ich persönlich würde es mal mit einem orm versuchen. Ich habe zwar keine Ahnung von php, aber habe schon mit einem orm gearbeitet und wenn ich das so vergleich wirkt das alles viel einfacher. propel sieht zb sehr gut aus. Man muss sich dann um SQL garnicht mehr kümmern.

      hier ein kleines Beispiel einer kleinen Tabelle und deren Nutzung mit propel:

      Quellcode

      1. $author = new Author();
      2. $author->setFirstName('Sushi');
      3. $author->setLastName('Fish');
      4. $author->save();
      5. // ...
      6. $firstAuthor = AuthorQuery::create()->findPK(1);
      7. echo $firstAuthor->getFirstName(); // 'Sushi'
      8. $firstAuthor->toJson(); // {"Id":1,"FirstName":"Sushi","LastName":"Fish"}
      9. // ...
      10. $Sushi = AuthorQuery::create()
      11. ->filterByFirstName('Sushi')
      12. ->findOne();
      13. $Sushi->setFirstName('Meh');
      14. $Sushi->save();
      15. $Meh = AuthorQuery::create()->findPK(1); // aus datenbank lesen
      16. $Meh->getFirstName(); // 'Meh'
      Alles anzeigen


      tut zwar nicht wirklich zur sache, aber wollte mal es mal gesagt haben, dass diese möglichkeit auch bestehen würde. In meinen Augen erscheint es sinnvoll da mal reinzuschaun vor allem für große Projekte :)


      ...wie gesagt... hab kaum bis keine ahnung von php von daher weiß ich nicht, ob es noch was besseres als propel gibt ^^
    • Moin,
      da einige offenbar nicht wissen, wie man mit PHP richtig auf Datenbanken zugreift, gibt es hier mal ein Tutorial dazu. In diesem Tutorial behandle ich die beliebtesten Datenbanksysteme (MySQL und SQLite). Andere Systeme funktionieren in der Regel ähnlich.

      Es gibt wichtige Regeln, die man beachten sollte:
      • Sofern vorhanden, sollte man immer die objektorientierte Variante bevorzugen.
      • Man sollte immer die neueste Schnittstelle benutzen. Also MySQLi statt MySQL, SQLite3 statt SQLite etc.
      Wie gesagt, solltet ihr immer die OOP-Variante wählen, da sie einfacher zu handhaben ist als die Handles. Wenn ihr mit der OOP noch nicht in Berührung gekommen seid, legt euch ein vernünftiges PHP-Buch zu.
      Allerdings solltet ihr auch ohne OOP-Kenntnisse in der Lage sein, diese Anleitung zu verstehen.
    • Verbindung zur Datenbank herstellen

      Das Datenbankobjekt wird initialisiert mit new. Danach kommt der Klassenname, der meistens mit dem des Datenbanksystems identisch ist. Der Klasseninstanz (Objekt) werden ein paar Parameter mitgegeben.

      Bei einigen Datenbankschnittstellen (z.B. PostgreSQL) gibt es noch keine Klassen. Hier muss man auf die prozedurale Syntax zurückgreifen.

      So, jetzt aber zur Datenbankverbindung:
      Eine MySQL-Verbindung stellt man folgendermaßen her:

      PHP-Quellcode

      1. <?php
      2. $db = new MySQLi('server','username','passwort','datenbank'); // Es gibt noch zwei weitere Parameter (Port und Socket), die man in der Regel weglassen kann. Meistens sind sie richtig gesetzt.
      3. ?>

      Die Parameter sollten eigentlich selbstverständlich sein, aber ich erkläre sie doch noch mal:
      • Als erstes kommt die Adresse des Servers. Diese Adresse kann eine Domain oder eine IP sein. Läuft der Datenbankserver auf demselben System wie das Skript, kann man localhost oder 127.0.0.1 verwenden.
      • Danach kommen die Logindaten für den Datenbankserver. Hier sollte man einen User wählen, der möglichst wenig Rechte hat. Bei vielen Hostern wird für jeden User eine Datenbank angelegt. Das ist eine gute Methode, um den Zugriff des Skripts auf eine Datenbank zu beschränken.
        Man sollte NIEMALS den Root-User verwenden! Dieser User hat volle Lese- und Schreibrechte auf alle Datenbanken des Servers. Hat das Skript eine Sicherheitslücke in der Query, kann schlimmstenfalls jeder User das komplette Datenbanksystem kontrollieren.
      • Nach den Logindaten kann man die zu verwendende Datenbank auswählen.
        Falls man nur eine Verbindung zum Datenbankserver herstellen möchte, kann man die Datenbank auch weglassen. Man kann sie später wählen (und auch wechseln, wenn man schon in einer Datenbank ist):

        SQL-Abfrage

        1. USE andere_datenbank;

        Diese Syntax lässt sich bei den meisten Schnittstellen anwenden, auch bei den prozeduralen.
      Wenn ein Parameter nicht gesetzt ist, wird er per ini_get(), also aus der globalen oder einer im Skriptverzeichnis befindlichen php.ini, aus der httpd.conf (oder dem entsprechenden Pendant beim HTTP-Server), aus einer .htaccess oder einem vorher per ini_set() festgelegten Wert, gelesen.
      Falls ein Fehler bei der Datenbankverbindung auftritt, wird eine Exception geworfen (siehe weiter unten).


      SQLite ist ein Sonderfall, da sich hier eine (und nur eine) komplette Datenbank in einer Datei befindet. Es gibt auch keine User oder Ähnliches, der einzige Schutz sind hier die Dateirechte (kann man unter Windows also vergessen).

      PHP-Quellcode

      1. <?php
      2. $db = new SQLite3('foo.sqlite');
      3. ?>

      Existiert die Datei nicht, wird sie angelegt. Möchte man das verhindern, kann man als zweiten Parameter von SQLite3::__construct() eine ensprechende Konstante benutzen:

      PHP-Quellcode

      1. <?php
      2. $db = new SQLite3('foo.sqlite',SQLITE3_OPEN_READWRITE);
      3. ?>

      Existiert die Datenbank dann nicht, wird eine Exception (eine Klasse für Fehler (Ausnahmen)) geworfen. Das passiert auch, wenn die Verbindung zur Datenbank fehlschlägt - auch bei MySQLi. Wird die Exception nicht gefangen, gibt es einen Fatal Error. Möchte man also auch bei Fehlschlag der Verbindung das Skript weiter ausführen, sollte man diese Syntax benutzen:

      PHP-Quellcode

      1. <?php
      2. try {
      3. $db = new SQLite3('foo.sqlite',SQLITE3_OPEN_READWRITE);
      4. } catch (Exception $e) {
      5. echo 'Datenbankfehler: '.$e->getMessage();
      6. }

      oder man setzt per set_exception_handler() eine Funktion, die bei nicht aufgefangenen Exceptions aufgerufen wird (und die Exception idealerweise auffängt und behandelt).
      Aber die Exceptions sind viel zu komplex, um hier nebenbei behandelt zu werden. Wer mehr Infos braucht, schaut bitte ins Manual .

      Soweit ein Exkurs zu den Exceptions. Jetzt geht es weiter mit den Datenbanken.
    • Queries ausführen

      Hat man erfolgreich eine Verbindung zur Datenbank hergestellt, kann man Queries (Abfragen) ausführen, die in der Sprache SQL (Structured Query Language) verfasst sind.
      Wer noch keine Erfahrungen mit SQL hat, findet hier ein Tutorial: sql-und-xml.de/sql-tutorial/
      Generell kann man Queries mit der query()-Methode der MySQLi-/SQLite3-Klasse ausführen. Bei Queries mit Ergebnis (SELECT, SHOW DATABASES etc.) ist der Rückgabewert der Methode ein Objekt der Klasse MySQLi_Result.

      PHP-Quellcode

      1. <?php
      2. $mysqli = new MySQLi('localhost','nichtroot','geheim','testdb');
      3. $result = $mysqli->query('SELECT name,pass FROM users WHERE name = "admin";');
      4. echo $result->num_rows(); // Gibt die Anzahl der zurückgegebenen Zahlen aus
      5. ?>

      Die Methode fetch_array() gibt pro Aufruf eine Zeile des Ergebnisses als Array zurück. Nach der letzten Zeile wird false zurückgegeben.

      PHP-Quellcode

      1. <?php
      2. echo '<table border="1"><tr><th>Name></th><th>Passwort</th></tr>';
      3. // Verbindung und Query wie oben
      4. while ($r = $result) {
      5. echo '<tr><td>'.$r['name'].'</td><td>'.$r['pass'].'</td></tr>';
      6. }
      7. echo '</table>';
      8. ?>


      By SQLite sehen die Methoden etwas anders aus, aber sonst ist die Vorgehensweise sehr ähnlich.

      PHP-Quellcode

      1. <?php
      2. echo '<table border="1"><tr><th>Name</th><th>Passwort</th></tr>';
      3. $sqlite = new SQLite3('pfad/zur/datenbank.sqlite');
      4. $result = $sqlite->query('SELECT name,pass FROM users WHERE name IS NOT "admin";');
      5. while ($r = $result->fetchArray()) {
      6. echo '<tr><td>'.$r['name'].'</td><td>'.$r['pass'].'</td></tr>';
      7. }
      8. echo '<tr><td colspan="2">Ergebnisse: '.$result->fetchArray().'</td></tr></table>';
      9. ?>
    • Prepared Statements

      Nun wisst ihr, wie man SQL-Queries mit PHP durchführt. Ein Thema haben wir dabei aber noch nicht behandelt: Die Sicherheit.
      Angenommen, ihr habt ein Kommentarformular mit den Feldern title, mail und comment. Wie bekommt man die in die Datenbank?
      Die bequemste Methode ist diese:

      PHP-Quellcode

      1. <?php
      2. $db = new SQLite3('db.sqlite');
      3. $db->query('INSERT INTO comments (ip,title,mail,comment) VALUES ("'.$_SERVER['REMOTE_ADDR'].'","'.$_POST['title'].'","'.$_POST['mail'].'","'.$_POST['comment'].'");');
      4. ?>

      Leider ist das auch die gefährlichste, weil die Formulardaten ungefiltert in die Query eingebaut werden. Beispiel:

      PHP-Quellcode

      1. $_POST['title'] = 'blubb';
      2. $_POST['mail'] = 'bla';
      3. $_POST['comment'] = 'hallo");DROP DATABASE comments;';

      Die Query sieht also so aus:

      SQL-Abfrage

      1. INSERT INTO comments (ip,title,mail,comment) VALUES ("123.456.789.0","blubb","bla","hallo");DROP DATABASE comments;);

      Wegen der Klammer und dem Semikolon liefert das Datenbanksystem zwar einen Syntaxfehler zurück, aber die ersten beiden Queries werden trotzdem ausgeführt. Das nennt man SQL Injection.
      Bei MySQLi wird das etwas entschärft, weil dort mit der query()-Methode nur eine Query ausgeführt werden kann. Für mehrere Queries gibt es multi_query() (bei SQLite ist es umgekehrt, dort kann man mit query() mehrere Queries und mit singleQuery() nur eine Query durchführen). Nichtsdestotrotz kann man immer noch eine Menge mit solchen Queries anstellen. Man sollte also niemals Usereingaben direkt in eine Query einbauen.
      Was kann man dagegen machen? Eine Methode ist es, die Daten mit Real Escape Strings zu maskieren. Eine sicherere (und performantere) Methode sind Prepared Statements.
      Prepared Statements trennen die Query von den Daten. So ist SQL Injection quasi unmöglich.
      Das Prinzip: Die Query wird mit Platzhaltern an den Datenbankserver gesendet. Danach wird jedem Platzhalter eine Variable zugewiesen. Diese Daten werden dann ebenfalls an den Server gesendet.

      PHP-Quellcode

      1. <?php
      2. $db = new MySQLi('db.sqlite');
      3. $stmt = $db->prepare('INSERT INTO comments (ip,title,mail,comment) VALUES (?,?,?,?);');
      4. $stmt->bind_param('ssss',$_SERVER['REMOTE_ADDR'],$_POST['title'],$_POST['mail'],$_POST['comment']);
      5. $stmt->execute();
      6. ?>

      Der erste Parameter gibt die Datentypen der Variablen an: s steht für einen String, d für Float (bzw. Double; ist in PHP dasselbe), i für Integer und b für BLOB. Die folgenden Parameter schließlich die Variablen selbst.
      Bei bind_param() sollte man beachten, dass man nur Variablen als Parameter übergibt, keine direkten Werte. Ansonsten gibt es einen Fatal Error.

      Bei SQLite verhalten sich die Statements etwas anders: es gibt zwar auch hier bindParam(), es funktioniert aber anders.

      PHP-Quellcode

      1. <?php
      2. $db = new SQLite3('db.sqlite');
      3. $stmt = $db->prepare('INSERT INTO comments (ip,title,mail,comment) VALUES (:ip,:title,:email,:comment);');
      4. $stmt->bindParam(':ip',$_SERVER['REMOTE_ADDR']);
      5. $stmt->bindParam(':title',$_POST['title']);
      6. // etc.
      7. $stmt->execute();
      8. ?>

      bindValue() funktioniert im Prinzip genauso wie bindParam(), nur dass man hier auch direkte Werte übergeben kann.

      PHP-Quellcode

      1. <?php
      2. // Verbindung und prepare wie oben
      3. $stmt->bindValue(':ip','127.0.0.1');
      4. $stmt->bindValue(':title','Das ist ein Kommentar');
      5. // etc.
      6. $stmt->execute();
      7. ?>


      So, das wars erstmal mit dem Tutorial. Auf Wunsch erweitere ich es natürlich gerne.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Malachite ()

    • Sushi schrieb:

      ...wie gesagt... hab kaum bis keine ahnung von php von daher weiß ich nicht, ob es noch was besseres als propel gibt ^^
      Würde jetzt nicht direkt besser sagen, ist wohl an einigen Stellen mehr eine Geschmackssache, aber eine sehr gute alternative zu propel ist defenitiv doctrine .
      Ist ein Blick wert, für alle die sich mit ORMs bzgl. PHP beschäftigen wollen ;)

      @Malachite: Ist sehr ausführlich geworden, hab nichts weiter hinzuzufügen. Gute Arbeit!
    • um sein programm vielseitiger zu machen, was die datenbankanbindung angeht.
      1. kann man sich sicher sein, dass der code optimiert ist und der ganze datenbankkram wahrscheinlich schneller geht als man es selber vll hinbekommen würde auf die schnelle. Interessiert bei kleineren Sachen natürlich eher weniger.
      2. Das ist der wichtiger Punkt. Es werden alle möglichen Datenbanken unterstützt. Für propel wäre das MySQL, MS SQL Server, PostgreSQL, SQLite und Oracle. Man kann einfach zwischen denen wechseln ohne eine einzige Zeile neu zu schreiben. Das könnte schon wichtig sein, wenn man sein Programm verkaufen möchte oder überhaupt für die Öffentlichkeit anbietet.
      3. warum mit SQL arbeiten, wenn man es nicht braucht?

      natürlich gibt es auch gründe die dagegen sprechen. Vor allem bei kleinen Projekten, weil das doch das Programm entwas aufbläht.


      Versteh mich nicht falsch. Ich finde keinesfalls, dass dein Tutorial unnötig ist oder schlecht. Ehrlich gesagt weiß ich es nicht, aber wenn die andern sagen, dass es gut ist, dann wird es wohl so sein. Ich wollte nur darauf hinweisen, dass es auch diese Möglichkeit gibt, weil es für einige Nutzer durchaus eine Option sein könnte.
      Vll kannst du dafür soagar ein Tutorial machen?!