Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [php] mega prosty licznik odwiedzin, a jednak zawodny
Forum PHP.pl > Forum > PHP
marcinek37
  1. <?
  2. $file = fopen('counter.txt', 'r'); $counter = (int)fgets($file); fclose($file);
  3. if($_SESSION['opened'] != '1'){ $counter++; $file = fopen('counter.txt', 'w'); fwrite($file, $counter); fclose($file); $_SESSION['opened'] = '1'; }
  4. ?>


raz na jakiś czas nie zapisuje wartości i liczy od nowa... ale dlaczego? jak można temu zaradzić?
Sephirus
Możliwe, że są równoległe odwołania do tego pliku. Proponuje zapoznać się z flock wink.gif
cudny
Znając życie wkrada ci się biały znak na początku i dostajesz 0 - czyli to co jest w pierwszej linii.
Daj zamiast fopen i tak dalej file_get_contents('counter.txt');

przykład;

  1.  
  2. $path = 'counter.txt';
  3. $counter = trim( file_get_contents($path) );
  4.  
  5. file_put_contents( $path,++$counter );
  6.  
  7. echo $counter;
  8.  
stealz
Inny sposób: zamiast odczytywać wartość, dopisuj do pliku pojedynczy bajt (np. jakikolwiek znak) za pomocą file_put_contents z flagą FILE_APPEND.
Stan licznika zwróci Ci funkcja filesize().
To rozwiązanie jest szybsze i bardziej niezawodne, natomiast bardziej obciążające dysk. Przy milionowej odsłonie plik będzie ważył już 1 MB. Up to you.
Pozdrawiam.
marcinek37
  1. <?
  2. $path = 'counter.txt';
  3. $counter = trim( file_get_contents($path) );
  4.  
  5. file_put_contents( $path,++$counter );
  6. ?>


czy ten kod jest bezpieczny, jeśli naraz wejdzie kilka osób na stronę? może ten flock to nie taki głupi pomysł? co sądzicie?
cudny
Jeśli się o to boisz to daj flagę LOCK_EX

  1. file_put_contents($file,$content,LOCK_EX);


Bez obrazy ale bez sensu jest pytać na forum o rzeczy, które są w manualu smile.gif

@stealz - no co ty ? Szybsze ? Bardziej niezawodne ? Otwieraj potem plik aby zapisać dane... Rób tą inkrementację i się nie zastanawiaj - to tylko licznik odwiedzin, a jeśli chodzi o file lock to masz napisane wyżej co robić
stealz
Ok, zgadzam się. Ale tylko po części : )
Musiałem sprawdzić. Nie myślałem, że dopisywanie danych do dużego pliku przez file_put_contents jest tak wolne.
Z mojego testu wynika, że odczytywanie stanu licznika przez filesize() jest prawie 5x szybsze. Natomiast zwiększanie jego stanu w przypadku zasugerowanego przeze mnie algorytmu - od 2 do 4 razy wolniejsze.
Ponieważ kolega chce, aby zwiększenie licznika nastąpiło tylko raz w ciągu sesji, to z obliczeń, które poczyniłem wynika, że jeżeli użytkownik odwiedzi średnio 15 i więcej podstron, wówczas opcja z filesize() jest szybsza.

I jeszcze jedno: LOCK_EX. Jeżeli jeden użytkownik zablokuje dostęp do pliku przy podanym przez Ciebie file_put_contents($file,$content,LOCK_EX), wówczas drugi użytkownik w ciągu tych kilku milisekund nie ma dostępu do odczytu jego wartości linijkę wyżej. Odczytana wartość będzie równa '' i licznik zostanie wyzerowany. LOCK powinien nastąpić przed odczytaniem danych.
marcinek37
czyli w ten sposób?

  1. <?
  2. $path = 'counter.txt';
  3. $counter = trim( file_get_contents($path, LOCK_EX) );
  4.  
  5. file_put_contents( $path,++$counter );
  6. ?>


chyba jednak nie... więc możesz mi powiedzieć, gdzie mam LOCK_EX wpisać?
cudny
  1. file_put_contents($file_name,$content,LOCK_EX);
marcinek37
czyli generalnie chodzi o coś takiego:
  1. <?
  2. $path = 'counter.txt';
  3. $counter = trim( file_get_contents($path) );
  4.  
  5. file_put_contents( $path, ++$counter, LOCK_EX);
  6. ?>


dziękuję!
Crozin
Odnośnie blokady: musisz założyć blokadę (zarówno odczytu jak i zapisu) na plik przed jego odczytaniem oraz zdjąć ją dopiero po zapisaniu danych do pliku - inaczej może dojść do przekłamań w liczniku. Czyli:
  1. $fh = fopen('plik.txt', 'r+');
  2. flock($fh, LOCK_EX);
  3.  
  4. $cnt = (int) trim(fread($fh, 1024));
  5. $cnt++;
  6.  
  7. fseek($fh, 0);
  8.  
  9. fwrite($fh, $cnt);
  10. flock($fh, LOCK_UN);
  11. fclose($fh);
  12.  
  13. // Pominąłem siedem(!) IF-ów związanych z obsługą błędów...
  14. // Skorzystanie z SplFileObject niestety również będzie ich wymagało
Blokada przy samym zapisie jest niewystarczająca, jeżeli chcesz mieć solidny kod.

Odnośnie prędkości działania: jeżeli powyższy fragment będzie wąskim gardłem najwyższy czas pomyśleć nad zmianą dysku bądź platformy.

EDIT: Był drobny błąd w pow. przykładzie (typ otwarcia pliku, brak fseek)
marcinek37
Crozin, czyli jeśli użyję tego kodu, który zaproponowałeś powyżej, mam większą szansę, że licznik nie zepsuje się?
aktualnie licznik jest na malutkiej stronce, ale pechowo co jakiś czas się zerował...

zależy mi jedynie na tym, aby, kiedy już osiągnie jakiś wynik, np. 10000, żeby nagle nie wyskoczyło mi 0 :/
Crozin
Żeby licznik nagle nie zwariował musisz zadbać o to by w jednym momencie był przetwarzany (odczyt/zapis) przez jeden proces.
Żeby sprawdzić czy wszystko jest OK, możesz przy pomocy Apache Benchmarka zrobić prosty test: 10.000 wywołań skryptu/30 wątków jednocześnie. Po zakończeniu testu w pliku musi znajdować się liczba 10000.
marcinek37
chyba można stwierdzić, że skrypt, który zaproponowałeś, właśnie taki powinien być - blokuje plik dla jednego wywołania, więc powinno być w porządku

przy okazji zapytam, czy te kody mają w ogóle sens, jeśli system ma jedynie odczytać wynik, bez zwiększania wielkości

  1. flock($fh, LOCK_EX);
  2. flock($fh, LOCK_UN);
Crozin
Jeżeli potrzebujesz jedynie odczytać zawartość licznika, nie ma potrzeby korzystania z blokad.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.