fopen('data.txt', 'r') otwiera plik i zwraca uchwyt, czyli zasób strumienia, który pozostałe funkcje plikowe przyjmują jako pierwszy argument. Czytaj przez fgets() (jedna linia) albo fread() (określona liczba bajtów), zapisuj przez fwrite(), a zamykaj przez fclose(). Drugi argument to tryb: 'r' do odczytu, 'w' do zapisu, 'a' do dopisywania.
Wynik:
Got: apple
Got: banana
Got: cherry
fopen() zwraca false, gdy nie może otworzyć pliku (nie istnieje w trybie r, brakuje folderu, brak uprawnień), a PHP wypisuje ostrzeżenie, na przykład:
Warning: fopen(missing.txt): Failed to open stream: No such file or directory in /home/index.php on line 2
Dlatego przed użyciem uchwytu sprawdź, czy nie jest false. Do odczytu lub zapisu całego małego pliku jednym wywołaniem krótsze są file_get_contents() i file_put_contents(); fopen() służy do plików czytanych po kawałku albo trzymanych otwartych.
Tryby fopen
| Tryb | Odczyt | Zapis | Jeśli plik istnieje | Jeśli nie istnieje | Zaczyna od |
|---|---|---|---|---|---|
r | tak | nie | otwiera go | zawodzi | początku |
r+ | tak | tak | otwiera go, zachowuje zawartość | zawodzi | początku |
w | nie | tak | opróżnia go | tworzy go | początku |
w+ | tak | tak | opróżnia go | tworzy go | początku |
a | nie | tak | zachowuje zawartość | tworzy go | końca, każdy zapis dopisuje |
a+ | tak | tak | zachowuje zawartość | tworzy go | końca, każdy zapis dopisuje |
x | nie | tak | zawodzi | tworzy go | początku |
x+ | tak | tak | zawodzi | tworzy go | początku |
c | nie | tak | zachowuje zawartość | tworzy go | początku |
c+ | tak | tak | zachowuje zawartość | tworzy go | początku |
Dwa wiersze zaskakują. r+ nadpisuje istniejące bajty w miejscu, bez wstawiania, a a+ zawsze zapisuje na końcu, nawet po przesunięciu wskaźnika przez fseek() (przesunięcie wpływa tylko na odczyt):
Wynik:
Xbc
Xbc
Xbc
end
W Windows możesz zobaczyć 'rb' albo 'wb': flaga b oznacza tryb binarny i powstrzymuje Windows przed tłumaczeniem końców linii. W Linuksie i macOS nic nie robi, więc jej dodanie jest nieszkodliwe i przenośne.
Odczyt pliku linia po linii przez fgets()
fgets() zwraca następną linię razem z jej \n, a na końcu pliku false. Ponieważ w pamięci jest naraz tylko jedna linia, ta pętla obsłuży plik logu dowolnego rozmiaru, a przez break możesz zakończyć wcześniej:
Wynik:
2 errors in 5 lines
line 2: database timeout
line 4: disk full
Porównuj z false przez !==, a nie zwykłym while ($line = fgets($fh)): linia zawierająca tylko "0" bez znaku nowej linii (taka może być ostatnia linia pliku) jest traktowana jak fałsz i zakończyłaby pętlę przedwcześnie.
Zapis i dopisywanie przez fwrite()
fwrite() zapisuje string w bieżącej pozycji i zwraca liczbę zapisanych bajtów albo false przy błędzie. fputs() to ta sama funkcja pod inną nazwą. Otwórz z 'a', aby dopisywać na końcu pliku bez ruszania tego, co już w nim jest:
Wynik:
wrote 12 bytes
wrote 13 bytes
wrote 15 bytes
visit /home
visit /about
visit /contact
visit /home
PHP_EOL to znak końca linii systemu, na którym działa PHP (\n w Linuksie i macOS, \r\n w Windows). W plikach czytanych przez inne programy wybierz jeden jawnie, zwykle "\n".
Odczyt i zapis plików CSV przez fgetcsv() i fputcsv()
fputcsv() zapisuje tablicę jako jeden wiersz CSV i otacza cudzysłowami pola zawierające przecinki, cudzysłowy lub znaki nowej linii. fgetcsv() odczytuje jeden wiersz z powrotem jako tablicę. Przekazuj jawnie separator, znak otaczający i pusty znak ucieczki: daje to standardowy CSV, a PHP 8.4 wypisuje komunikat o przestarzałym użyciu, gdy argument ucieczki zostanie pominięty.
Wynik:
name,city,note
Ada,London,"likes ""math"", a lot"
Linus,Helsinki,kernel
Ada lives in London: likes "math", a lot
Linus lives in Helsinki: kernel
Dzielenie linii przez explode(',', $line) psuje się, gdy tylko pole zawiera przecinek; fgetcsv() obsługuje cudzysłowy za ciebie. Dla pliku rozdzielanego średnikami (częstego w eksportach z Excela w Europie, także w Polsce) przekaż ';' jako separator.
Poruszanie się po pliku przez fseek(), ftell() i rewind()
Otwarty plik ma pozycję. Odczyty i zapisy zaczynają się w niej i przesuwają ją do przodu. ftell() mówi, gdzie jest, fseek() przesuwa ją do przesunięcia w bajtach, a rewind() wraca na początek. Z w+ możesz zapisać plik i odczytać go przez ten sam uchwyt:
Wynik:
11
world
hello world
Blokowanie pliku przez flock()
Dwa żądania, które w tej samej chwili odczytują licznik, dodają jeden i zapisują go z powrotem, mogą oba odczytać 41 i oba zapisać 42. flock() z LOCK_EX każe drugiemu czekać, aż pierwsze zwolni blokadę. Właściwy jest tu tryb c+: tworzy plik w razie potrzeby i, w przeciwieństwie do w+, nie opróżnia go, zanim założysz blokadę.
Wynik:
Visitor number 1
Visitor number 2
Visitor number 3
Blokady są doradcze: zatrzymują tylko inny kod, który też wywołuje flock(). Do dopisania jednej linii file_put_contents($path, $line, FILE_APPEND | LOCK_EX) robi to samo jednym wywołaniem.
Częsty błąd: while (!feof($fh))
Wiele starych poradników używa pętli z feof(). Zwraca ono true dopiero wtedy, gdy odczyt już trafił na koniec, więc pętla wykonuje się o jeden raz więcej, niż jest linii, a ostatnie fgets() zwraca false:
Wynik:
string(2) "a
"
string(2) "b
"
string(2) "c
"
bool(false)
4 rounds for 3 lines
To dodatkowe false staje się pustym wierszem w raporcie albo pustym elementem w tablicy. Steruj pętlą samym odczytem, while (($line = fgets($fh)) !== false), jak robi każdy inny przykład na tej stronie.
Najczęściej zadawane pytania
Jakie są tryby fopen w PHP?
r czyta, w zapisuje po opróżnieniu pliku, a zapisuje na końcu, x tworzy nowy plik i zawodzi, jeśli plik istnieje, a c zapisuje bez opróżniania. Dodanie + (r+, w+, a+, x+, c+) pozwala zarówno czytać, jak i zapisywać. Każdy tryb oprócz r i r+ tworzy plik, gdy go brakuje.
Czym różni się w od a w fopen?
w opróżnia plik zaraz po otwarciu i zapisuje od początku. a zachowuje zawartość, a każde fwrite() trafia na koniec pliku, nawet po fseek(), co czyni go trybem dla plików logów.
Jak odczytać plik CSV w PHP?
Otwórz go przez fopen($path, 'r') i wywołuj fgetcsv() w pętli: while (($row = fgetcsv($fh, null, ',', '"', '')) !== false) { ... }. Każde wywołanie zwraca jeden wiersz jako tablicę pól, a cudzysłowy i przecinki wewnątrz pól są obsługiwane za ciebie.
Czy trzeba wywoływać fclose w PHP?
PHP zamyka wszystkie otwarte pliki na koniec skryptu, więc krótki skrypt zadziała bez tego. Mimo to wywołuj fclose(), gdy tylko skończysz: opróżnia buforowane zapisy, zwalnia blokady założone przez flock() i nie pozwala długo działającemu skryptowi wyczerpać uchwytów plików.