Kunden und Adressen auf Prepared Statements umgebaut
incs/saveNchange.php neu geschrieben: Adressen, Telefonnummern, Kontakte, Notizen und Konten laufen ueber db_abfrage() mit Platzhaltern, mehrschrittiges Speichern in einer Transaktion. Weil sich die Speicherfunktionen gegenseitig aufrufen, klammern beginneSpeichern()/beendeSpeichern() die Verschachtelung; incs/rechnung.php nutzt sie ebenfalls. Kunden- und Lieferantenseiten waren zwei fast gleiche Kopien und teilen sich jetzt partnerAddSeite(), partnerChangeSeite() und partnerShowDaten(). Dabei behoben: - SQL-Injektion ueber die Suchbegriffe der Kunden-, Lieferanten-, Adress-, Kommissions- und Projektsuche sowie ueber die Artikel-Autovervollstaendigung - gespeichertes XSS: Notizen (nl2br escapt nicht), Bankname, Dokumenttyp, Namen und Adressen in allen Trefferlisten - sites/liefershow.php war unter PHP 8 ein Fatal ($kdnres.id) - "Telefonnummer loeschen" im Kundenbereich lief in einen Fatal, die Nummern einer geloeschten Adresse blieben stehen (Spalte hiess adress_id statt adresse_id, mysql_fetch_assoc gibt es nicht mehr) - Geburtstag ohne Jahr wurde im Formular verschluckt, die IBAN kam beim Bearbeiten aus $_GET statt aus dem Konto - die Versionskette eines Kunden brach nach einem Schritt ab - Kommissionsadressen speicherten die Ortsangabe der Adresse statt der des Telefonformulars und einen Adresstyp als Telefontyp - Blaettern in den Trefferlisten: ohne ORDER BY konnte dieselbe Zeile zweimal oder gar nicht erscheinen; ajax_searchadress() bekam die Seite nicht - Hauptadresse laesst sich nicht mehr loeschen, Kundendaten nicht mehr ueber die Lieferantenseite (und umgekehrt) - ajax/auto/* baute JSON von Hand zusammen, ein Anfuehrungszeichen im Ort machte die Liste unbrauchbar Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -91,12 +91,14 @@ Eine Schemaänderung besteht also aus: neuer Eintrag **vorn** in `update/version
|
||||
Regeln für den Umbau:
|
||||
|
||||
- **Ergebnisse sind Strings** wie früher bei `mysqli_query()`: `db_zeilen()`, `db_zeile()`, `db_wert()` und `db_zuordnung()` wandeln die nativen int/float-Werte der Prepared Statements um. Wer `db_abfrage()` selbst ausliest, bekommt native Typen.
|
||||
- **Formularwerte** über `formularText()`, `formularZahl()` (Komma als Dezimaltrenner, leer wird 0) und `formularGanzzahl()` aus `incs/rechnung.php` holen – der strikte Modus lehnt `''` in Zahlenspalten ab.
|
||||
- **Formularwerte** über `formularText()`, `formularZahl()` (Komma als Dezimaltrenner, leer wird 0) und `formularGanzzahl()` aus `incs/saveNchange.php` holen – der strikte Modus lehnt `''` in Zahlenspalten ab.
|
||||
- **Änderungsprotokoll:** `saveLog()` bekommt den Text aus `db_sql_text()`. Die Tabelle `log` ist reine Dokumentation, nichts daraus wird ausgeführt.
|
||||
- **Mehrschrittiges Speichern** in eine Transaktion (`mysqli_begin_transaction` / `commit` / `rollback`), damit bei Fehlern keine halben Datensätze bleiben.
|
||||
- **Mehrschrittiges Speichern** in eine Transaktion, damit bei Fehlern keine halben Datensätze bleiben — aber über `beginneSpeichern()` / `beendeSpeichern()` aus `incs/saveNchange.php`, nicht über `mysqli_begin_transaction()` direkt. Die Speicherfunktionen rufen sich gegenseitig auf (`savePart()` → `saveAdress()` → `updateKunde()` → `saveAdress()`, `loeschePartner()` → `delRechnung()`), MariaDB kennt aber keine geschachtelten Transaktionen; ein Zähler sorgt dafür, dass nur der äußerste Aufruf öffnet und schließt.
|
||||
- **Tabellen- und Spaltennamen** lassen sich nicht binden; nur feste Werte aus dem Code einsetzen, `LIMIT` als `(int)`.
|
||||
- **Suchabfragen** (`create*Qry()` in `incs/listfuncs.php`) liefern `array("all", "limited", "params")`. Aufrufer nutzen `db_abfrage($dbconn, $q["limited"], $q["params"])` und `zaehleTreffer()`. Die Suchbegriffe sind Platzhalterwerte, `%` und `_` darin bleiben absichtlich Jokerzeichen — so ist die Suche dokumentiert. In der Autovervollständigung (`ajax/auto/`) werden sie dagegen über `db_like_praefix()` entwertet.
|
||||
- **Werte aus der Datenbank sind kein HTML.** `GROUP_CONCAT` trennt mit einem Zeilenumbruch; zusammengebaut und escaped wird erst in `zeilenHtml()` bzw. `makenameline()`/`makeadressline()`/`maketelline()`.
|
||||
|
||||
Umgebaut: Login/Passkeys, Benutzereinstellungen, Änderungsprotokoll, Rechnungsmodul (`incs/rechnung.php`, `sites/rechnungadd.php`, `sites/umschlagshow.php`, `ajax/autosave.php`, `ajax/preview.php`, Rechnungssuche). Offen: Kunden/Adressen (`incs/saveNchange.php`), Termine, Projekte, Zeiten, Benutzerverwaltung, übrige Suchen, `incs/makepdf.php`, `update/`.
|
||||
Umgebaut: Login/Passkeys, Benutzereinstellungen, Änderungsprotokoll, Rechnungsmodul (`incs/rechnung.php`, `sites/rechnungadd.php`, `sites/umschlagshow.php`, `ajax/autosave.php`, `ajax/preview.php`, Rechnungssuche), Kunden und Lieferanten (`incs/saveNchange.php` samt `sites/kunden*.php`, `sites/liefer*.php`, Kunden-, Lieferanten-, Adress- und Kommissionssuche, `ajax/auto/`). Offen: Termine, Projekte, Zeiten, Benutzerverwaltung, `incs/makepdf.php`, `update/`.
|
||||
|
||||
Prozedurales `mysqli`. `$dbconn` wird zusätzlich in `$_SESSION["dbconn"]` abgelegt, damit `incs/mysql_ecape_wrapper.php` eine globale Funktion `mysql_escape_string()` als Shim über diese Verbindung bereitstellen kann — ein Überbleibsel der `mysql_*`-Ära, das in altem Code noch aufgerufen wird.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user