Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [php] mega prosty licznik odwiedzin, a jednak zawodny
marcinek37
post
Post #1





Grupa: Zarejestrowani
Postów: 239
Pomógł: 0
Dołączył: 2.06.2011

Ostrzeżenie: (0%)
-----


  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ć?
Go to the top of the page
+Quote Post
Sephirus
post
Post #2





Grupa: Zarejestrowani
Postów: 1 527
Pomógł: 438
Dołączył: 28.06.2011
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Możliwe, że są równoległe odwołania do tego pliku. Proponuje zapoznać się z flock (IMG:style_emoticons/default/wink.gif)
Go to the top of the page
+Quote Post
cudny
post
Post #3





Grupa: Zarejestrowani
Postów: 387
Pomógł: 66
Dołączył: 31.03.2005
Skąd: Kielce

Ostrzeżenie: (0%)
-----


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.  
Go to the top of the page
+Quote Post
stealz
post
Post #4





Grupa: Zarejestrowani
Postów: 3
Pomógł: 0
Dołączył: 22.01.2010

Ostrzeżenie: (0%)
-----


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.
Go to the top of the page
+Quote Post
marcinek37
post
Post #5





Grupa: Zarejestrowani
Postów: 239
Pomógł: 0
Dołączył: 2.06.2011

Ostrzeżenie: (0%)
-----


  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?
Go to the top of the page
+Quote Post
cudny
post
Post #6





Grupa: Zarejestrowani
Postów: 387
Pomógł: 66
Dołączył: 31.03.2005
Skąd: Kielce

Ostrzeżenie: (0%)
-----


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 (IMG:style_emoticons/default/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ć

Ten post edytował cudny 28.11.2012, 23:19:49
Go to the top of the page
+Quote Post
stealz
post
Post #7





Grupa: Zarejestrowani
Postów: 3
Pomógł: 0
Dołączył: 22.01.2010

Ostrzeżenie: (0%)
-----


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.

Ten post edytował stealz 29.11.2012, 01:18:03
Go to the top of the page
+Quote Post
marcinek37
post
Post #8





Grupa: Zarejestrowani
Postów: 239
Pomógł: 0
Dołączył: 2.06.2011

Ostrzeżenie: (0%)
-----


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ć?
Go to the top of the page
+Quote Post
cudny
post
Post #9





Grupa: Zarejestrowani
Postów: 387
Pomógł: 66
Dołączył: 31.03.2005
Skąd: Kielce

Ostrzeżenie: (0%)
-----


  1. file_put_contents($file_name,$content,LOCK_EX);
Go to the top of the page
+Quote Post
marcinek37
post
Post #10





Grupa: Zarejestrowani
Postów: 239
Pomógł: 0
Dołączył: 2.06.2011

Ostrzeżenie: (0%)
-----


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ę!
Go to the top of the page
+Quote Post
Crozin
post
Post #11





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


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)

Ten post edytował Crozin 4.12.2012, 22:13:44
Go to the top of the page
+Quote Post
marcinek37
post
Post #12





Grupa: Zarejestrowani
Postów: 239
Pomógł: 0
Dołączył: 2.06.2011

Ostrzeżenie: (0%)
-----


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 :/
Go to the top of the page
+Quote Post
Crozin
post
Post #13





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Ż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.
Go to the top of the page
+Quote Post
marcinek37
post
Post #14





Grupa: Zarejestrowani
Postów: 239
Pomógł: 0
Dołączył: 2.06.2011

Ostrzeżenie: (0%)
-----


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);
Go to the top of the page
+Quote Post
Crozin
post
Post #15





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Jeżeli potrzebujesz jedynie odczytać zawartość licznika, nie ma potrzeby korzystania z blokad.
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 22.12.2025 - 21:18