Datei sperren
Shadowcrow
- php
0 frankx0 Shadowcrow0 frankx
0 Christian Seiler
hi $name,
hi,
ich habe einen kleinen, primiven admin bereich erstellt, der es erlaubt texte auf der HP zu ändern, das vorgehen ist folgerndermaßen:
in einer datei stehen die texte und ich lese
$lines = file ('includes/home.txt');
mithilfe von foreach()
in ein formular ein, dort können sie verändert werden und mit
if (!empty($_POST)) {
$z = count ($_POST);
if (!$handle = fopen($file_home, "w")) {
print "Kann die Datei $file_home nicht öffnen";
exit;
}
foreach ($_POST as $key => $value) {
if (!fwrite($handle, $_POST [$key]."\n")) {
print "Kann in die Datei $file_home nicht schreiben";
exit;
} else {
echo 'OK';}
}
}
wird das ganze wieder abgespeichert. auf der hp wird der inhalt der datei wieder mit file()
ausgelesen und dargestellt.
ok, das ich die datei sperren muss ist mir klar da "w" die datei ja plättet, aber es ist ja möglich das mehrere gleichzeitig den admin bereich aufrufen (A+B) und auslesen.
A liest die datei aus (inhalt:42)
B liest die datei aus (inhalt:42)
A sperrt die datei zum lesen
A schreibt neue daten (inhalt:666)
A hebt die sprerre auf
B sperrt die datei zum lesen
B schreibt neue daten (inhalt:777)
B hebt die sprerre auf
wenn die datei erst beim schreiben gesperrt könnte B ja garnicht mitbekommen das A in der zwischenzeit etwas geändert hat - auch wenn die datei über LOCK_NB
gesperrt wurde.
bei der alternative die datei schon beim lesen zu sperren sehe ich das problem, das zwischen lesen und schreiben ja unendlich viel zeit vergehen kann (da benutzereingabe) und das ganze system dadurch blockiert wird, wie löse ich das problem? ich hoffe ich habe das verständlich beschrieben :-)
gruss
shadow
p.s.
den artikel von christian seiler habe ich gelesen, aber so richtig schlau bezüglich meines problems bin ich nicht draus geworden.
Ahoi,
naja:
a liest,
a sperrt, wenn er bearbeiten will
b liest
b will bearbeiten, geht nicht
alles gut.
c liest
a speichert
c sperrt zum bearbeiten
und bekommt eine mitteilung, wann die datei zuletzt bearbeitet wurde.
a,b und c müssen immer davon ausgehen, dass sie die datei erst wenn sie sie gesperrt haben wirklich in der aktuellen fassung vorliegen haben.
und sie müssen wissen, dass sie sie, wenn sie sie zum bearbeiten sperren, für die anderen blockieren.
ergänzend könnte ein javascript in kurzen abständen den lastmodified timestamp abfragen und ggf. ein "alarm" geben dem adminleser, dass aktuell jemand die datei zum bearbeiten sperrt.
Dank und Gruß,
hi $name,
a liest,
a sperrt, wenn er bearbeiten will
ja, aber wie umsetzen, solange A den zugriff auf die datei blockert kann sie ja nicht auf der HP dargestellt werden, a sperrt die datei, liest daten ein, geht aufs klo und alle besucher schauen in die röhre :-(
b liest
wenn die datei gesperrt ist sollte er doch erst garnicht lesen können?
b will bearbeiten, geht nicht alles gut.
c liest
a speichert
c sperrt zum bearbeiten und bekommt eine mitteilung, wann die datei zuletzt bearbeitet wurde.
da aber die datei erst/nur zum schriben gesperrt werden soll/darf nicht umsetztbar
a,b und c müssen immer davon ausgehen, dass sie die datei erst wenn sie sie gesperrt haben wirklich in der aktuellen fassung vorliegen haben.
ja klar, nur s.o.
und sie müssen wissen, dass sie sie, wenn sie sie zum bearbeiten sperren, für die anderen blockieren.
für die anderen im admin bereich ok (gebt ja nur zwei, für die HP besucher ist das sch***
ergänzend könnte ein javascript in kurzen abständen den lastmodified timestamp abfragen und ggf. ein "alarm" geben dem adminleser, dass aktuell jemand die datei zum bearbeiten sperrt.
jo, aber mit ein wenig pech und schlechtem timing nützt das auch nichts, aber mir kommt eine idee, was wenn ich die datei (DATEIA) auslese, "last modified" abfrage (LM1) aber die änderungen in DATEIB speichere dann aus DateiB DateiA mache aber vorher auch
"last modified abfrage (LM2) und wenn LM1+LM2 nicht identisch sind mit einer fehlermeldung den inhalt von DateiA wieder einlese, so das der benutzer weiß es wurde was geändert, sind LM1+LM2 identisch werden die änderungen übernommen. macht das sinn?
gruss
shadow
Ahoi,
naja, die admins sollten erst bearbeiten können (und nur untereinander ausmachen, wer da grad an was fummelt) und dann die funktion "online-stellen" haben.
gelocked wird dann nur die arbeitskopie des admins.
Dank und Gruß,
hi $name,
naja, die admins sollten erst bearbeiten können (und nur untereinander ausmachen, wer da grad an was fummelt) und dann die funktion "online-stellen" haben.
da es nur zwei admins gibt (meinereiner und der hp besitzer) und was textänderungen angeht der eine vorrang hat (besitzer), ist das kein problem, da ich ja das ding auch noch, nach und nach, erweitere brauchts sowieso ein wartungsfenster in der der besitzer nicht auf den admin bereich zugreifen kann.
gruss
shadow
Hallo,
A liest die datei aus (inhalt:42)
B liest die datei aus (inhalt:42)
A sperrt die datei zum lesen
A schreibt neue daten (inhalt:666)
A hebt die sprerre auf
B sperrt die datei zum lesen
B schreibt neue daten (inhalt:777)
B hebt die sprerre auf
Meine Idee wäre die folgende (stark vereinfacht gesagt):
Auslesen des Inhalts: Datei mit LOCK_SH sperren, Inhalt ausgeben, Datei schließen. Hidden-Feld generieren, das den ursprünglichen Inhalt der Datei enthielt (oder einen SHA1-Hash davon oder was auch immer).
Schreiben des Inhalts: Datei mit LOCK_EX sperren, Inhalt auslesen, vergleichen mit mitgesendetem Hidden-Feld;
* wenn übereinstimmt: Datei neu schreiben mit neuem Inhalt des Users,
Datei schließen.
* wenn nicht übereinstimmt: Datei schließen, Warnmeldung an den User
ausgeben und ihm mitteilen, was er überschreiben würde + neuen Inhalt
im jetztigen Hidden-Feld mitsenden so dass er das Formular erneut
absenden kann nur dass diesmal das Überschreiben klappen würde.
Hinweis: Mit file() solltest Du keine Dateien auslesen, weil file() sie nicht sperrt. Nimm lieber die Methoden die ich in meinem Artikel beschrieben habe.
Viele Grüße,
Christian
hi $name,
Meine Idee wäre die folgende (stark vereinfacht gesagt):
Auslesen des Inhalts: Datei mit LOCK_SH sperren, Inhalt ausgeben, Datei schließen. Hidden-Feld generieren, das den ursprünglichen Inhalt der Datei enthielt (oder einen SHA1-Hash davon oder was auch immer).
Schreiben des Inhalts: Datei mit LOCK_EX sperren, Inhalt auslesen, vergleichen mit mitgesendetem Hidden-Feld;
* wenn übereinstimmt: Datei neu schreiben mit neuem Inhalt des Users,
Datei schließen.
* wenn nicht übereinstimmt: Datei schließen, Warnmeldung an den User
ausgeben und ihm mitteilen, was er überschreiben würde + neuen Inhalt
im jetztigen Hidden-Feld mitsenden so dass er das Formular erneut
absenden kann nur dass diesmal das Überschreiben klappen würde.
danke, das hört sich super an ich hatte ja eher einen anderen ansatz aber bei diesem wären die user eingaben verworfen worden (wäre jetzt auch nicht tragisch, tippen stärkt die finger ;-)) das ist wesentlich eleganter.
Hinweis: Mit file() solltest Du keine Dateien auslesen, weil file() sie nicht sperrt. Nimm lieber die Methoden die ich in meinem Artikel beschrieben habe.
hm ja, mir erschien es nur einfacher, weil bei file die einzelnen zeilen schon automatisch durch die zeilenumbrüche getrennt sind und so auch in ein array eingelesen werden, ich wußte nicht wie ich die werte sonst trennen und einlesen sollte, aber ich könnte ja die datei öffnen, mit LOCK_SH sperren, mit file() auslesen und dann wieder schließen.. hm, warum hab ich jetzt das gefühl das das krude ist...
gruss
shadow
Hallo,
Hinweis: Mit file() solltest Du keine Dateien auslesen, weil file() sie nicht sperrt. Nimm lieber die Methoden die ich in meinem Artikel beschrieben habe.
hm ja, mir erschien es nur einfacher, weil bei file die einzelnen zeilen schon automatisch durch die zeilenumbrüche getrennt sind und so auch in ein array eingelesen werden, ich wußte nicht wie ich die werte sonst trennen und einlesen sollte, aber ich könnte ja die datei öffnen, mit LOCK_SH sperren, mit file() auslesen und dann wieder schließen.. hm, warum hab ich jetzt das gefühl das das krude ist...
Warum liest Du die Datei nicht einfach per fread() ein und trennst den Inhalt per preg_split?
$fp = fopen ('dateiname', 'rb+');
if (!is_resource ($fp)) {
// Fehlerbehandlung
}
if (!flock ($fp, LOCK_SH)) {
// Fehlerbehandlung
}
$data = fread ($fp, filesize ('dateiname'));
$hash = sha1sum ($data);
$lines = preg_split ("/\r\n|\r|\n/", $data);
fclose ($fp);
Oder, falls Du wie bei file() die Zeilenende-Zeichen mitnehmen willst:
$lines = preg_split ("/(?<=\r\n|\r|\n)/", $data);
Alternativ könntest Du natürlich auch in einer Schleife per fgets() das Zeilenweise auslesen und in einem Array speichern - oder direkt wieterverarbeiten - oder was auch immer.
Viele Grüße,
Christian
hi $name,
Warum liest Du die Datei nicht einfach per fread() ein und trennst den Inhalt per preg_split?
weil ich nicht auf diese idee gekommen bin :-(
außerdem wußte ich nicht wie ich die einzelnen zeilen dann weiterverarbeite.
Alternativ könntest Du natürlich auch in einer Schleife per fgets() das Zeilenweise auslesen und in einem Array speichern - oder direkt wieterverarbeiten - oder was auch immer.
das mit dem fgets() ist eine gute idee, ich brauch halt die zeilen einzeln um sie in das formular zu setzen, einzeln deshalb weil sie auf der hp unterschiedliche formatierungen haben (überschrift, normaler text, link,...).
danke, ich werde mir das in ruhe mal anschauen und umsetzen :-)
das mit dem datei sperren hab ich jetzt (dank deiner idee) so gelöst:
wenn die datei zum lesen geöffnet wird frage ich filemtime() ab und gebe es dem formular als hidden field mit, vor dem schreiben vergleiche ich es mit dem aktuellen wert und gebe bei einem unterschied eine fehlermeldung aus und zeige den neuen dateiinhalt an, das die eingegebenen "alten" werte auch angezeigt werden werde ich aber auch noch einbauen.
gruss
shadow
Hallo,
Warum liest Du die Datei nicht einfach per fread() ein und trennst den Inhalt per preg_split?
weil ich nicht auf diese idee gekommen bin :-(
außerdem wußte ich nicht wie ich die einzelnen zeilen dann weiterverarbeite.
preg_split() liefert Dir einen array der Zeilen zurück - genau so wie file() eben auch.
das mit dem datei sperren hab ich jetzt (dank deiner idee) so gelöst:
wenn die datei zum lesen geöffnet wird frage ich filemtime() ab und gebe es dem formular als hidden field mit, vor dem schreiben vergleiche ich es mit dem aktuellen wert und gebe bei einem unterschied eine fehlermeldung aus und zeige den neuen dateiinhalt an, das die eingegebenen "alten" werte auch angezeigt werden werde ich aber auch noch einbauen.
filemtime() halte ich für eine schlechte Idee. Die kann nämlich durch alle möglichen Prozesse noch geändert werden, was dann entweder dazu führt, dass Du zu oft false positives hast oder teilweise sogar false negatives. Was spricht gegen einen SHA-1-Hash des Dateiinhalts?
Viele Grüße,
Christian
Moin!
filemtime() halte ich für eine schlechte Idee. Die kann nämlich durch alle möglichen Prozesse noch geändert werden, was dann entweder dazu führt, dass Du zu oft false positives hast oder teilweise sogar false negatives. Was spricht gegen einen SHA-1-Hash des Dateiinhalts?
Ich finde die filemtime gar nicht so verkehrt, allerdings wäre als nächster Schritt dann ein Diff-Merger ganz nett. Mein PHPEditor aus dem SELFHTML-Dev-Bereich tut sowas, unter Zuhilfenahme von "diff" und ganz viel Kommandozeile, wenn ich mich recht erinnere. :)
- Sven Rautenberg
hi $name,
preg_split() liefert Dir einen array der Zeilen zurück - genau so wie file() eben auch.
das ist perfekt, danke das wußte ich nicht, ich hatte halt im manual gestöbert und bin über files() gestolpert, aber wie gesagt ich werds mit preg_split() umsetzen...
filemtime() halte ich für eine schlechte Idee. Die kann nämlich durch alle möglichen Prozesse noch geändert werden, was dann entweder dazu führt, dass Du zu oft false positives hast oder teilweise sogar false negatives. Was spricht gegen einen SHA-1-Hash des Dateiinhalts?
öh? das ist ja fies :-( gegen den SHA-1-Hash spricht eigentlich nur die tatsache das ich eine faule socke bin und mir das mit filemtime() als der einfachste weg erschien ;-) naja, auf zum HASH :-)
gruss
shadow