Skrypt liczący ilość wejść w ciągu 24h - a resetuje się co X czas

0

Cześć, mam skrypt który zlicza ilość unikalnych wejść na moją stronę w ciągu 24h, wszystko fajnie ale nie resetuje się co 24h tylko co mniej, na razie raz zresetował się po 12h raz po połtorej, ktoś widzi jakiś problem? dodatkowo chciałbym by nie zerował się co 24h (tak jak jest dotychczas) tylko by usuwał adresy IP które są dłużej niż 24h i żeby była taka płynna wymiana, ale to drugoplanowo, najbardziej zależy mi by naprawić działanie skryptu :)

<?php
 
//add_comment.php
 
         $plik = "online_24.txt";
             $czas = 24*60*60;
                 $t = time();
             $ip=$_SERVER['REMOTE_ADDR'];  
         $u = explode("\n", str_replace("\r", "", @join("", @file($plik))));
         foreach($u as $i => $v) {
             $e = explode("|", $v);
             if($e[0] == $ip || $e[1] < $t) unset($u[$i]);
         }
         $u[] = $ip . "|" . ($t + $czas);
         $fp = fopen($plik, "w");
             flock($fp, 2);
                 fputs($fp, join("\n", $u));
             flock($fp, 3);
         fclose($fp);
         $output='<span class="icon-user-2" >24h: '.count($u).'</span>';
echo $output;
?>

5

Po pierwsze - jest to napisane paskudnie. Rozumiem, że się dopiero uczysz, więc nie będę się znęcać, ale jedynie dam kilka rad:

  • używaj bardziej opisowych zmiennych, bo kod z $e, $fp, $u itp. się fatalnie czyta. No i nie wiadomo, za co dana zmienna odpowiada, trzeba zaglądać wyżej, żeby sprawdzić, co zostało do niej podstawione
  • w temacie nazw zmiennych i funkcji - staraj się używać angielskich słów. Oczywiście - program będzie tak samo działac (albo i nie ;) ) ale jest to taki przyjęty standard, w ten sposób kod wygląda o wiele bardziej profesjonalnie, a poza tym ułatwia się czytanie innym
  • nie używaj tzw. magicznych stałych - zamiast napisać $czas = 24*60*60; lepiej by było najpierw zadeklarować #godziny = 24; $minuty = 60; $sekundy = 60;, a potem napisać coś w stylu $czas = $minuty * $godziny * $sekundy.
  • powinieneś to podzielić, bo na razie masz jeden wielki mix: wczytywanie pliku, przetwarzanie danych, wypluwanie na ekran itp. Tak się nie powinno działać. Nie bój się funkcji, one bardzo pomagają i zwiększają czytelność kodu.
  • trzymanie tego w pliku tekstowym nie jest dobrym pomysłem, zainteresuj się SQLite. W necie jest pełno tutoriali jak się z tego korzysta, pierwszy z brzegu - http://zetcode.com/php/sqlite3/

Ostatni punkt jest także jest odpowiedzią na fragment by usuwał adresy IP które są dłużej niż 24h - korzystając z SQL możesz przerzucić całą logikę na bazę, wywalić paskudne explode. Tworzysz tabelę, w której trzymasz adres IP oraz tzw. timestamp. Następnie podczas sprawdzenia robisz coś w stylu SELECT ID FROM daty_wpisow WHERE IP=xxxx i patrzysz, czy dostaniesz jakiś wynik. Jeśli tak, to znaczy, że informacja w bazie o danym adresie istnieje. Jeśli nie, to dodajesz. W SQL możesz zrobić, żeby pole timestamp się samo aktualizowało z wykorzystaniem aktualnego czasu, więc cała akcja z Twojej strony ogranicza się do wydania polecenia INSERT INTO daty_wposow (adres_ip) VALUES (xxxx); gdzie xxxx jest adresem IP, który chcesz zablokować na jakiś czas. A samo czyszczenie wpisów przeterminowanych będzie miało postać DELETE FROM daty_wpisow WHERE timestamp xxxx i zamiast xxx podajesz warunek, na podstawie którego wpisy mają zostać usunięte - np. że timestamp jest wcześniejszy niż 24h od teraz. Zobacz - Ty jedynie dajesz polecenie żeby usunąć wpisy o akcjach sprzed doby (albo spełniających jakikolwiek inny warunek), a całą robotę za Ciebie odwala SQL. Czy to nie jest piękne?

I ważna uwaga - nie bój się SQL. W powaznych zastosowaniach to jest kobyła, która wymaga niezłego skilla, ale w takich podstawowych rzeczach to jest dosłownie kilka linijek, które ogarniesz w 15 minut. Na pewno mniej będzie z tym problemów niż z tym, co pokazałeś powyżej ;)


EDIT

A odpowiadając na Twoje pytanie - taka pierwsza myśl to może być problem z równoczesnym dostępem do pliku. Zobacz https://www.php.net/manual/en/function.flock.php, a w szczególności te komentarze:

I've been having trouble getting Flock to work when I read a file, delete it, and then output slightly changed information back to the same location. When deleting with Unlink, there's a very brief period of time where no file exists. But, if you do an fopen using the "w" mode, it keeps the file in existence, but deletes all of its data when you go to write to it. That way, the file never actually disappears, and another script accessing the same file with flock won't get a "file doesn't exist" error.

oraz

I just spent some time (again) to understand why a reading with file_get_contents() and file was returning me an empty string "" or array() whereas the file was existing and the contents not empty.
In fact, i was locking file when writing it (file_put_contents third arg) but not testing if file was locked when reading it (and the file was accessed a lot).
So, please pay attention that file_get_contents(), file() and maybe others php files functions are going to return empty data like if the contents of the file was an empty string.
To avoid this problem, you have to set a LOCK_SH on your file before reading it (and then waiting if locked).

Być może czyszczenie się pliku wynika z tego, że gdzieś był zablokowany/miałeś włączony dostęp w trybie zapisu, potem starałeś się go odczytać, została odczytana pusta wartość, a następnie dodałeś nowa informację do pliku, który wydawał się pusty, więc jego zawartość została stracona. Tak mi przyszło do głowy. Sprawdź proszę, czy w takiej sytuacji o której piszesz - czyli że wpisy się zgubiły, cała zawartość pliku jest czyszczona, czy może plik jest OK, tylko nie udało się z niego wczytać danych i przez to skrypt nie odczytał informacji, że dany IP już występował.

4

Do tego co napisał @cerrato dodam, że nie zaleca się stosować @ bo to ukrywa potencjalny błąd. No i jak piszesz warunek to używaj nawiasów i część wykonywalną w warunku umieszczaj w następnej linijce.

0

@cerrato, @jurek1980: w porządku, ogólnie mówiąc to skrypt który stworzył mi znajomy także i jemu to prześle, dzięki za wszystkie uwagi. Jeszcze pytanie, na czas nie działania tego skryptu ogarnąłem inny, podany niżej. Całość opisana, działa poprawnie tylko jest w nim jeden słaby punkt otóż gdy przejdziemy na tryb przeglądarki incognito lub wyłączymy ciasteczka w przeglądarce to jak wiemy skrypt będzie każde nasze wejście dodawał a tego nie chcemy, chcemy tylko unikalne, więc pytanie, czy jest na to jakieś rozwiązanie, ew blokada by nie naliczało w takich wypadkach? załóżmy że fajnie by było gdyby w/w przypadkach po prostu nie naliczało tych osób, da się tak? chyba że ma ktoś lepsze rozwiązanie typu zapisywanie wtedy po IP np

<?php

/* --- OPERACJA NA CIASTECZKACH, DODAJEMY WARTOŚCI */
/* warunek: jeżeli nie ma ciasteczka w pamięci przeglądarki o nazwie licznik_cook, z wartością 1 to: */

if(!$_COOKIE['LicznikCook']){

/* jeżeli plik z bazą danych (licznik.db) istnieje i jest zapisywalny (chmod: 666), to: */
if(file_exists("licznik.db")){
    if(is_writeable('licznik.db')){

    /* pobieramy zawartość tego pliku i podwyższamy jego wartość, np. z 34 na 35 */
    $bdpobierz = file_get_contents("licznik.db") + "1";

    /* otwieramy plik licznik.db - bazę danych */
    $bdzapisz = fopen("licznik.db", "w");

    /* nadpisujemy istniejącą wartość na tą ze zmiennej $bdpobierz, następnie zamykamy */
    fwrite($bdzapisz, $bdpobierz);
    fclose($bdzapisz);
    }
}

/* wysyłamy przeglądarce ciasteczko o nazwie licznik_cook, z wartością 1 (przykładowa wartość) oraz ustawiamy jego ważność na 24 godziny */
setcookie("LicznikCook", '1', time()+3600*24);
}

/* --- WYŚWIETLAMY ILOŚĆ ODWIEDZIN */
/* sprawdzamy, czy plik bazy danych istnieje */
if(file_exists("licznik.db")){

    /* sprawdzamy, czy plik bazy danych jest zapisywalny (666) */
    if(is_writeable('licznik.db')){

        /* pobieramy plik */
        $bdpokaz = file_get_contents("licznik.db");

        /* wyświetlamy zawartość, czyli ilość odwiedzin */
		echo "<span style='color: #bdc1c6'>W ciągu 24h:</span> <b><span style='color: white'> $bdpokaz </b></span> <span style='color: #bdc1c6'>os.</span>";
		

        /* jeżeli plik nie jest zapisywalny wyświetlamy komunikat o błędzie: */
        }else{echo "Plik nie jest zapisywalny. Ustaw chmody na 666.";}

}else{echo "Plik nie istnieje. Utwórz plik licznik.db i wgraj go na serwer.";}

?>
1

A nie czujesz się na siłach podjąć jakiejś próby modyfikacji tego skryptu samemu?
Technicznie najlepiej znane mi rozwiązanie jeśli chodzi o rozpoznawanie użytkowników to odcisk palca przeglądarki. Jak poszukasz w google "browser fingerprint" i ewentulanie dodasz do wyszukiwania "PHP" to będziesz miał pewnie jakieś przykładowe kody nawet. Poczytaj o tym.

0

A macie może pomysł jak zrobić zliczanie ze wszystkich podstron, nie tylko ze strony na której jest skrypt? mówimy o skrypcie nr 1 ten który zlicza po IP.

1

Musisz jakoś dodać go w takim razie do każdej ze stron. Np. z użyciem include lub require

1

Tak jak napisał ktoś wyżej - zamiast pliku podepnij bazę danych. Będziesz miał dużo łatwiej i więcej możliwości zarówno wyciągania danych jak i ich obróbki, generowania raportów itp.

1

Jeszcze mnie jedna rzecz naszła - skoro chcesz kasować wpisy po jakimś czasie, to możesz skorzystać z REDIS. To jest jeszcze prostsze niż SQL - czyli sposób, który podałem wcześniej. W SQL byś musiał ręcznie kasować wpisy o określonym wieku, a REDIS ma możliwość automatycznego kasowania wpisów po określonym czasie. W sensie - tworząc wpis podajesz czas, po jakim ma on zostać automagicznie zaorany.

https://redis.io/commands/expire

Set a timeout on key. After the timeout has expired, the key will automatically be deleted.

1 użytkowników online, w tym zalogowanych: 0, gości: 1