Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Jak bezpiecznie zapisać do pliku?
Forum PHP.pl > Forum > PHP
WebCM
Problem jest dobrze znany przy licznikach odwiedzin, które lubią się resetować. Chcę zabezpieczyć skrypt sondy. Jest kilka plików: log.php, glosy.php... Jakich metod użyć?

Przykładowy zapis wygląda tak:
1. Odczytaj plik glosy.php za pomocą include()
2. Odczytaj plik log.php za pomocą include()
3. Zmień wartości w tablicach odczytanych z plików glosy.php i log.php
4. Zapisz plik log.php za pomocą file_put_contents() albo fwrite()
5. Zapisz plik glosy.php za pomocą file_put_contents() albo fwrite()

Hipoteza resetów liczników odwiedzin
1. Żądanie A odczytuje zawartość z pliku, zwiększa i zapisuje za pomocą file_put_contents()
2. W tym samym czasie żądanie B odczytuje zawartość pliku - być może jest wymazana
3. Żądanie B zwiększa pustą wartość i zapisze "1" do pliku

Jak się przed tym zabezpieczyć? Jest kilka sugestii:
1. Blokować dostęp zarówno do zapisu i do odczytu przy file_put_contents()
2. Sprawdzać is_writable() i is_readable() i dopiero include()
3. Po include() sprawdzić, czy istnieje tablica (która tam ma być) i nie jest pusta
4. Zapisywać do plików tymczasowych (np. glosy_new.txt), a następnie rename() do glosy.txt
5. Inny sposób - jaki jest na 100% skuteczny?

Jaki macie skuteczny sposób na hazard podczas odczytu i zapisu plików?
Crozin
Samo zablokowanie pliku przy zapisywaniu uchroni Cię przed wyzerowaniem licznika, ale nie przed fałszowaniem wyników. Dwie osoby mogą odczytać plik w tym samym czasie i obie zapiszą do niego wynik o jeden większy tym samym w rezultacie otrzymasz sfałszowany wynik - ostatecznie licznik zwiększy się o 1, nie o 2.

fopen, flock (read+write), fread, fwrite, fclose - najlepiej odczyt i zapis umieścić jedno pod drugim by blokada nie wstrzymywała zbyt długo innych żądań.
WebCM
Z doświadczenia tak nie jest. Oto kod licznika:
  1. if(file_exists('licznik.txt'))
  2. {
  3. $licznik = file_get_contents('licznik.txt');
  4. }
  5. else
  6. {
  7. $licznik = 0;
  8. }
  9. file_put_contents('./cfg/visits.txt', ++$licznik, LOCK_EX);
Działa bez zarzutów, ale skarżą się na resety. Prawdopodobnie dochodzi do hazardu.

Chcę uniknąć takich sytuacji z danymi w skrypcie, który jest oparty na plikach (logi IP, indeks sond, ilość głosów). Jeszcze nie doszło do takiej sytuacji, ale lepiej być przygotowanym na czarną godzinę smile.gif

Cytat
Samo zablokowanie pliku przy zapisywaniu uchroni Cię przed wyzerowaniem licznik
No dobrze, zablokujemy plik przy zapisie, ale nic nie stoi na przeszkodzie, aby w tym samym czasie inny wątek do odczytał. A wtedy tam nic nie będzie albo jakieś krzaczki. Nie wiem, jak zareaguje include(), bo tam będzie kod PHP, a nie liczba - wolę uniknąć fread() i eval(). Lubię używać file_put_contents() z LOCK_EX do zapisu.

A może pomysł z zapisem do pliku tymczasowego i zmiana nazwy to dobry pomysł? Nawet unikniemy fragmentacji plików - tak mi się wydaje - bo nowe dostaną tyle klastrów, ile potrzeba.
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.