PHP Problem, Textdatei wird zerstört!

Belee

Lt. Commander
Registriert
Dez. 2006
Beiträge
1.518
Hallo

Habe ein Problem mit meinem Counterscript für die Streambenutzung. Wenn zuviele Zugriffe auf einmal stattfinden wird die Textdatei quasi zerschossen. Die Einträge darin sind durcheinander oder fehlen und das Script zum auslesen kann die Daten nicht ausgeben "weil sie halt nicht da sind wo sie sein sollten oder ganz weg sind".

Hier das Script was die Hits zählt.
Das Script wird mit ?id=diswinamp usw. gestartet und der Hit in die counter.txt geschrieben bzw. addiert.

PHP:
     $whitelist = array(
     'diswinamp','displs','diswmp','disreal','disflash',
     'itawinamp','itapls','itawmp','itareal','itaflash',
     'oldwinamp','oldpls','oldwmp','oldreal','oldflash');
 
     $file = file("data/links.txt"); //hier sind die echten Streamlinks eingetragen
     foreach($file as $line) {
     list($id,$url) = explode("|",$line);
     $foo[$id] = $url;
}
     if(empty($_GET['id'])) {
     Header('Location: http://www.domain.eu');
} 
     elseif(isset($_GET['id']) & in_array($_GET['id'],$whitelist)) {
     $url = $foo[$_GET['id']];
 
     Header('Location: '.$url);
     Header('Cache-Control: must-revalidate, pre-check=0, no-store, no-cache, max-age=0, post-check=0');
 
     $id = $_GET['id'];
     $date = date("d.m.y");
     $time = date("H:i");
     $countfile = "data/counter.txt";  //hier werden die ganzen Hits reingeschrieben
     $file = file($countfile);
     $fd = fopen($countfile,"w+");    
 
     foreach ($file as $line) {
 
     $exp_line = explode("|", $line);
 
     if ($exp_line[0] == $id) {
     $count = $exp_line[1];
     $count ++;
     $new_line = "$id|$count|$date|$time\n";
     fputs($fd, $new_line);
}
     else {
 
     fputs($fd, $line);
}
}
     fclose($fd);
}
     else {
 
     Header('Location: http://www.domain.eu');
}


Ist das bei Flatfiles so oder habe ich etwas vergessen? bzw. gibt es eine Möglichkeit dieses Problem zu umgehen ohne auf Datenbank umzusteigen.

Edit:
Habe jetzt was gefunden wie man das Problem im Griff bekommt nur werde ich nicht schlau daraus wie ich das nun in meinem Script umsetzten kann, eventuell kennt sich hier ja ein Profi aus?

PHP:
For those who just want to check if a file is available for locking and return immediately (without blocking), use the following syntax:
 
<?php
$file = fopen('file.txt', 'w');
 
if(flock($file, LOCK_EX | LOCK_NB)){
    echo 'Got lock, continue writing to file';
    // Code to write to file
}else{
    echo 'File is locked by another process, aborting writing';
    // Couldn't obtain the lock immediately
}
?>
 
This is a quick, easy way to determine if another process is using the file or not, without blocking your script.

Gruß
Belee
 
Zuletzt bearbeitet:
Ist das bei Flatfiles so...
Ja, es ist in der Tat nicht optimal, dass ein Skript, das sehr oft und auch parallel ausgeführt werden kann, auf die selbe txt-Datei zugreift.
Wenn sich das nicht vermeiden lässt, kannst du dir mal flock() ansehen, va auch die Kommentare dort, da finden sich einige Ideen.
 
Zuletzt bearbeitet:
Habs jetzt anders gemacht und es scheint zu halten...

PHP:
..
$file = file($countfile);
 
     $fd = fopen($countfile,"r+");    
     flock($fd, LOCK_EX) or die('error');
 
weiter...

so wie ich das jetzt intepretiere passiert da folgendes...
Leseschreibzugriff findet statt, File wird geöffnet und gesperrt bis das file wieder geschlossen wird, ist das korrekt?
Das habe ich jetzt einfach ausm Gefühl so eingebaut nur wozu das DIE('..'); ? kosmetik?
 
uiui... das ist wirklich nicht optimal.
Das hier ist ohne Gewähr. Das Script bricht nicht ab, wenn nicht geschrieben werden kann, sondern probiert einfach 100 mal ob es Schreibzugriff bekommt. Ist aber keine gute Programmiertechnik.

edit 2: Auweia! Habe im Script die Datei mit "w+" statt "c+" geöffnet. Ist nun korrigiert ;)
Möchte auch noch weiter im Thread auf eine neue Version des Scripts verweisen (Seite 2)

PHP:
$countfile = "data/counter.txt";  //hier werden die ganzen Hits reingeschrieben
// testen, ob wir das file wirklich öffnen können.
if (($fd = fopen($countfile,"c+") === true) {
   // schleife um zu testen, ob wir einen lock bekommen
    for($i=0, $t=false;($i<100 || $t===true);$i++) {
        // wenn wir einen lock bekommen
        if (flock($fd, LOCK_EX | LOCK_NB)) {
            // dann erst einmal die abbruchbedingung für die schleife setzen
            $t = true;
            $lines = array();
            // die daten lesen
            while (!feof($fd)) {
                $lines[] = fgets($fd);
            }
            // pointer wieder auf anfang der datei setzen und datei leeren
            ftruncate($fd,0);
            // dein programmteil
            foreach ($lines as $line) {
                $exp_line = explode("|", $line);
                if ($exp_line[0] == $id) {
                    $count = int($exp_line[1]) + 1;
                    $new_line = "$id|$count|$date|$time\n";
                    fputs($fd, $new_line);
                } else {
                    // man könnte auch einfach das hier schreiben, um fehler zu vermeiden
                    // fputs($fd, trim($line) . "\n");
                    fputs($fd, $line);
                }
            }
            // lock aufheben
            // flock($fd, LOCK_UN);
        }
    }
    // handle schliessen
    flose($fd);
}

edit: Kommentare hinzugefügt.
Ja ok - das lock muss nicht aufgehoben werden wenn danach gleich das fclose kommt. falls du aber weitermachen möchtest dort im script, dann besser den lock aufheben wenn er nicht mehr gebraucht wird. habs auskommentiert ;)
Das Script hilft dir einfach dabei, dass keine hits verloren gehen und du auch nicht die datei mit alten daten (aus einem früheren read) überschreibst, wenn schon ein anderer prozess neue daten reingeschrieben hat :)
 
Zuletzt bearbeitet:
Ich bin Anfäger Herr Ober :)

Was genau macht dein Script jetzt? wäre ja nett wenn du mehr als nur uiui geschrieben hättest, ich möchte das ja lernen bzw. begreifen, uiui kenn ich schon, weiß das es nix gutes heisst.

Aber wenn du kein bock darauf hast ist es auch gut ;)

Das Script scheint mir aber irgendwie zu viel des guten oder?

Warum lock aufheben? wenn die Datei geschlossen wird ist der lock doch aufgehoben oder nicht?

Edit:
Ach so, jetzt verstehe ich...ne das soll es nicht 100 mal probieren :) es ist jetzt nicht wichtig wenn paar
Hits nicht geschrieben werden, ich kann eh nicht alle erfassen das ist mehr oder wneiger nur was für die Kosmetik, das was wichtig ist das die Textdatei nicht zerschossen wird.
 
Zuletzt bearbeitet:
Belee schrieb:
Ich bin Anfäger Herr Ober :)
Edit:
Ach so, jetzt verstehe ich...ne das soll es nicht 100 mal probieren :) es ist jetzt nicht wichtig wenn paar
Hits nicht geschrieben werden, ich kann eh nicht alle erfassen das ist mehr oder wneiger nur was für die Kosmetik, das was wichtig ist das die Textdatei nicht zerschossen wird.

Dann mach die for-schleife eben raus und teste es ohne :D

Nochmals edit: Zu deiner anderen Frage: DIE() bricht das ganze Script mit einer Fehlermeldung ab (in diesem Fall, wenn flock false zurückgibt). Das willst du ja hier nicht, da du später noch einen Header sendest.
 
Zuletzt bearbeitet:
Hmm...die Textdatei sieht so aus und muss so auch immer bleiben...

HTML:
diswinamp|23|26.09.10|01:46
disreal|1|26.09.10|00:58
diswmp|1|26.09.10|00:58
displs|1|26.09.10|00:58
disflash|0|26.09.10|00:17
itawinamp|1|26.09.10|01:01
itareal|1|26.09.10|01:01
itawmp|1|26.09.10|01:01
itapls|0|26.09.10|00:00
itaflash|0|26.09.10|00:00
oldwinamp|0|25.09.10|04:27
oldreal|0|25.09.10|04:28
oldwmp|0|25.09.10|04:28
oldpls|0|25.09.10|04:58
oldflash|0|25.09.10|09:26

Mit dem Eingangsscript sah die ja auf einmal so aus...

HTML:
diswinamp|23|26.09.10|01:46
disreal|1|26.09.10|00:58
diswmp|1|26.09.10|00:58displs|1|26.09.10|00:58
 
             disflash|0|26.09.10|00:17
itawinamp|1|26.09.10|01:01
 
 
 
 
itapls|0|26.09.10|00:00
                                                                       itaflash|0|26.09.10|00:00
oldwinamp|0|25.09.10|04:27
oldreal|0|25.09.10|04:28
            oldwmp|0|25.09.10|04:28
oldpls|0|25.09.10|04:58
                              oldflash|0|25.09.10|09:26

EDIT:

Der letzte Header wird gesendet wenn jemand versucht was anderes als das aus der $whitelist am script zu hängen, dann wird einfach die Startseite neu geladen. Wenn aber gelesen/geschrieben wird dann wird garnicht dieser Header angesprungen der sitzt ja hinterm else!
 
Zuletzt bearbeitet:
Nimm doch für so Tabellen lieber SQLite. Das sorgt auch dafür, dass alles sauber abläuft.
 
Wie mein Vorposter schon meinte, entweder ne SQL(ite) Datenbank oder aber du machst was an der Architektur - etwas mit einer Pipeline oder Queue. So könntest du eine Klasse machen, die eine solche Queue hat und nur diese Klasse greift auf die Datei zu. Diese Klasse schreibt natürlich nur das in die Datei was in der Queue steht (schön nacheinander). Meine PHP Zeiten sind zwar längst vorbei, aber queue gibt es bestimmt noch. Am besten google einfach mal danach.
 
Finde ich immer wieder spannend von manchen...wenn ich das mit einer Datenbank machen wollte hätte ich es, möchte das aber nicht...man kann jedes Problem irgendwie im Griff bekommen und nicht sofort das Handtuch werfen und Datenbank benutzen nur weil man sich mit der Materie nicht weiter auseinandesetzen möchte.
 
Was du machst ist aber unnötiger Aufwand und SQLite ist quasi geschaffen dafür.
Keine Ahnung, was dein Problem ist. Ich benutze ja auch gern lose Sammlungen von
Textdateien. Aber bei der Sache die du beschreibst ist das einfach nicht sinnvoll.
 
Es wäre nicht sinnvol wenn ich JEDEN HIT 100% erfassen möchte, das ich das aber nicht kann schrieb ich oben aber schon, der User hat immer noch die Möglichkeit die *.m3u oder *.asx usw. direkt im Player anzugeben und zu speichern, dann hat es sich mit dem erfassen und zählen eh erledigt da ich diese nicht erfassen kann da sie ja nicht über das Script laufen/können. Dann läuft der Stream ja auch noch über den Streaminghoster wo sich auch User tummeln und von dort der Stream auch gestartet werden kann, da habe ich absolut keinen Zugriff und auch die User kann ich nicht erfassen.

Darum ist das mehr oder weinger nur Kosmetik/Spielerei, wozu also eine Datenbank benutzen wenn die Daten eh nicht stimmen? jetzt sage mir nicht das du trotzdem eine Datenbank benutzen würdest :)

Alles was ich erreichen will ist das einige Hits geshchrieben werden und die Textdatei hält, also nicht zerschossen wird und das kann doch machbar sein oder?

EDIT:
Aber mal was anderes..die Daten also Summe wird ja jetzt so geschrieben 2134 ist es möglich das man nach der 2 einen Punkt einsetzt damit es dann halt so ausschaut 2.134? oder wäre das zuviel Aufwand? ich denke mal man muss die Anzahl der Zeichen von hinten zählen und dann nach dem dritten ein Punkt einsetzen oder?
 
Zuletzt bearbeitet:
Hey, ich finds ja auffälig das noch keiner die Idee mit einer "Sperr-Datei" Hatte.

Also: Wenn dein Script schreibt (oder schreiben will) prüft es ob die datei: stream.sperre exestiert (While 1 == 1 oder halt nen bestimter wert wenn na variable mit eingebaut wird) Wenn nicht wird diese erzeugt. Dann wird in die richtige datei mit den daten geschrieben. Wenn das schreiben fertig ist wird stream.sperre wieder gelöscht.

Die Methode ist gut wenn man komplett bei Dateien bleiben will. Mach ich selbst auch so da mir SQL nicht liegt

MFG Bratack
PS: Hoffe ich konnte helfen
 
Interessant, auf sowas muss man ja erst kommen :)
Nur wie setzt man sowas um? ich Plan = 0, würde es aber liebend gerne so machen weil es sich gut anhört. Hast du mal paar Zeilen? "wichtige" die das ganze erklären da .. What you see is what you can learn" :)
 
Ich code das mal schnell. Ich füge das dann gleich mal ein

EDIT:
So:
Mein PHP ist zwar etwas eingerostet und es ist nur das wichtigste abgebildet aber meines erachtenb sieht das dann so aus:

PHP:
<?php
// Text und so

if(!file_exists("stream.sperre")){
	// Schreibe Daaten
}
else {
	while( 1 == 1 ){
	
		if(!file_exists("stream.sperre")){
			// Schreibe Daten
			break;
		}
		else{
			// Tuhe nix
		}
	}
}
?>

Die Zweite else ist zwar unnötig aber falls nboch etwas getan werden soll wenn die Datei doch existiert hab ichs einfach mal drinne gelassen


PS: Ist alles ungetestet aber müsste (nach anpassen) gehen
 
Zuletzt bearbeitet:
Belee schrieb:
Es wäre nicht sinnvol wenn ich JEDEN HIT 100% erfassen möchte, das ich das aber nicht kann schrieb ich oben aber schon, der User hat immer noch die Möglichkeit die *.m3u oder *.asx usw. direkt im Player anzugeben und zu speichern, dann hat es sich mit dem erfassen und zählen eh erledigt da ich diese nicht erfassen kann da sie ja nicht über das Script laufen/können.

Also, mal ganz ehrlich, für mich gibt es hier nur 2 Argumentationen: Entweder ich mach es richtig oder ich lass es ganz. Solltest du dich für letzeres entscheiden, spar dir den Filezugriff und verwende stattdessen
PHP:
int rand  ( int $min  , int $max  )

Wenn du es richtig machen willst, dann argumentiere nicht damit, das ein paar Klicks oder so egal sind. Wenn du _alle_ Hits zählen willst, dann kannst du auch ein Programm schreiben, das den eingehenden Netzwerkverkehr analysiert und alle Requests auf eine bestimmte Ressource mitzählt. Und diesen Wert solltest du dann in eine Datenbank schreiben ;)

Wenn dir eine klassische Datenbank nicht zusagt, da du mit der Struktur usw nicht zurecht kommst, dann schau dir mal NoSQL Datenbanken an. Da brauchst du dir keine Gedanken über ein Datenbankschema usw machen.


so long
Renegade
 
-=Renegade=- schrieb:
Also, mal ganz ehrlich, für mich gibt es hier nur 2 Argumentationen: Entweder ich mach es richtig oder ich lass es ganz. Solltest du dich für letzeres entscheiden, spar dir den Filezugriff und verwende stattdessen
PHP:
int rand  ( int $min  , int $max  )

Wenn du es richtig machen willst, dann argumentiere nicht damit, das ein paar Klicks oder so egal sind. Wenn du _alle_ Hits zählen willst, dann kannst du auch ein Programm schreiben, das den eingehenden Netzwerkverkehr analysiert und alle Requests auf eine bestimmte Ressource mitzählt. Und diesen Wert solltest du dann in eine Datenbank schreiben ;)

Wenn dir eine klassische Datenbank nicht zusagt, da du mit der Struktur usw nicht zurecht kommst, dann schau dir mal NoSQL Datenbanken an. Da brauchst du dir keine Gedanken über ein Datenbankschema usw machen.


so long
Renegade

Mach doch mal einen vorschlag wie es geht.
Achso, was soll den der Zufall jetzt bringen?

-=Renegade=- schrieb:
Also, mal ganz ehrlich, für mich gibt es hier nur 2 Argumentationen: Entweder ich mach es richtig oder ich lass es ganz. Solltest du dich für letzeres entscheiden, spar dir den Filezugriff und verwende stattdessen
PHP:
int rand  ( int $min  , int $max  )

Meinst du statt auf der Homepage anzuzeigen: Der Stream wurde schon 1000 mal geöffnet soll der Zufall entscheiden???
Das wäre doch etwas Sinnlos.
 
@renega..

Danke hat mir sehr geholfen, ich bin jetzt schlauer. Ich weiß jetzt zum 10ten mal das ich dafür eine Datenbank nehmen könnte :(
 
Zurück
Oben