Hallo Tom,
Expertenstreit vor einem Neuling ist nie gut, aber ein paar Anmerkungen habe ich doch.
- verändere nichts, was Du vorher nicht gelesen hast
- arbeite mit Sessions. Datensätze, deren alter Inhalt sich nicht im Sessionpuffer befindet, dürfen nicht verändert werden
Da beschreibst Du Details für ein Grundproblem. Es ist aber nicht zwingend, das Grundproblem mittels dieser Details zu bearbeiten. Es sind auch keine Probleme von PHP oder SQL, sondern Grundprobleme der Web-Entwicklung.
Grundproblem 1 lautet: Ein Web ist keine Einzelbenutzer-Anwendung. Dinge, die bei einem Desktop-Programm mit lokalen Daten problemlos sind, sind im Web-Umfeld tödlich. Bei den beiden von Dir genannten Punkten geht es darum, dass zwei (oder mehr) Anwender den gleichen Datensatz lesen und dann updaten wollen. Wenn der erste Anwender schreibt, ist das noch ok. Aber der zweite Anwender überschreibt die Änderungen des ersten, weil er gar nicht weiß, dass es sie gegeben hat.
Grundproblem 2 lautet: Das pessimistische Sperren von Datensätzen ist in der Multiuser-Welt verpönt (sprich: man setzt ein "In Bearbeitung" Kennzeichen in dem Satz, den man gelesen hat). Solche Kennzeichen neigen dazu, vergessen zu werden. Es reicht schon, wenn der Anwender einfach den Browser schließt.
Deswegen reicht der Hinweis "Verändere nichts, was Du vorher nicht gelesen hast" nicht aus. Tabellen, die man updaten will, brauchen eine Versionsmarke, die sich pro Update verändert. Das kann entweder ein Zähler oder eine Timestamp-Column sein. Die Versionsmarke dient dazu, ein so genanntes "optimistisches Sperren" umzusetzen. Man liest die Daten und präsentiert sie dem User. Die Versionsmarke speichert man in der Session. Manche Programmierer verwenden auch einen Viewstate, d.h. verborgene Eingabefelder im HTML Formular, aber der muss verschlüsselt und signiert sein, damit ihn Anwender nicht fälschen können. Sessions sind normalerweise einfacher.
Der Anwender will vielleicht gar nichts ändern. Aber wenn doch, dann macht man das
- in einer Transaktion, wenn mehr als eine Table zu updaten ist (d.h. InnoDB, nicht MyISAM)
- beim Update wird in der WHERE Bedingung sichergestellt, dass ein Satz nur geupdated wird, wenn er dem letzten gelesenen Stand entspricht, d.h. die Satzversion der gemerkten Satzversion entspricht.
- Und natürlich wird die Versionsmarke geändert (plus 1 oder aktuellen Timestamp einsetzen).
Geht das schief, kam es zu einer Updatekollision. Wie man damit umgeht, ist eine Wissenschaft für sich. Im einfachsten Fall schreibt man eine Fehlermeldung und bittet den Anwender, seine Änderungen zu wiederholen. Im komplexen Fall versucht man, die Änderungen beider Anwender zu vereinigen, aber das ist nicht trivial und auch nicht immer möglich.
- beschaffe dir aus der Datenbank (Metadaten der Tabelle) die Spaltentypen
Hm. Zur Laufzeit? Warum? Man programmiert doch normalerweise gegen eine bekannte Datenbank. Es gibt zwei Typen von Anwendungen, die sich dynamisch an das DB Schema anpassen können müssen: phpMyAdmin (und ähnliche), und ORM-Libraries. Wenn man sein SQL von Hand erstellt, weiß man normalerweise, welche Spaltentypen vorliegen.
Der wichtigere Rat wäre: Streue das SQL nicht kreuz und quer durch die Anwendung. Versuche, mit möglichst wenigen Statements auszukommen (auch wenn sie ein paar Spalten "zuviel" lesen) und fasse alle SQLs in einem Funktionsmodul zusammen. Gerne auch objektorientiert, wenn man das beherrscht. Für jeden DB-Zugriff eine eigene Funktion. Die kann man dann in den Parametern mit Type Declarations versehen. Ein "DB-Zugriff" kann ein SQL Statement sein, aber ggf. auch mehr, je nach Umständen. Ändert sich dann etwas an der DB, hat man alle SQLs, die man anfassen muss, beisammen.
- prüfe bei der Antwort vom Client die Darstellbarkeit der Requestparameter im Zieltyp der Datenbanktabelle.
- beachte den Kontextwechsel und das passende Escaping
Bzw. konvertiere die Daten geeignet. Vom Client kommen ja immer nur Strings, und dem Client kann man nicht vertrauen. Man muss jedem Request unterstellen, dass ein Hacker ihn schickt, der einem die Website zerschießen will. Zum Kontextwechsel gibt es hier Lektüre.
- speichere dir in der Session alle Felder des Formulars, die bei der nachfolgenden Antwort (dem Update Request) des Clients vorhanden sein müssen und dürfen.
Das ist ähnlich wie beim Lesen der Metadaten: Wozu? Normalerweise weißt Du, welche Daten ein Form erwartet. Solche Informationen musst Du nur speichern wenn die Forms dynamisch sind und auf Grund von DB-Inhalten flexibel zusammengestellt werden. Überzählige POST Parameter ignoriert man eigentlich dadurch, dass man gar nicht danach schaut. Es gibt zwar auch Anwendungen, die über die $_POST Parameter iterieren und sie alle irgendwo hin schreiben, aber DAS ist wirklich ein Einfallstor für Hacker und nicht unbedacht zu tun.
Relevant ist: Checkboxen landen nur im $_POST bzw. $_GET Array, wenn sie angehakt wurden. Sonst nicht. Wenn Du also eine Checkbox auf dem Form hast
<label>
<input type="checkbox" name="acceptAGB" value="1"/>
Ich habe die AGB verstanden
</label>
dann musst Du mit isset($_POST['acceptAGB'])
prüfen, ob der Wert überhaupt übergeben wurde. Es kann aber auch nützlich sein, für solche Zwecke eine Helferfunktion zu schreiben statt immer isset(...) zu programmieren. Und wie schön, das gibt's schon fertig:
$agb = filter_input(INPUT_POST, 'acceptAGB', FILTER_VALIDATE_BOOLEAN);
filter_input hat diverse Validatoren und Sanitizer und kann Dir einiges an Arbeit abnehmen.
Rolf
--
sumpsi - posui - obstruxi