Większość skryptów PHP musi przechowywać szereg informacji: treści wyświetlane na stronach, szczegóły o użytkownikach, komentarze itd. Najczęściej w celu przechowywania tych informacji używa się baz danych, takich jak np. MySQL. Jednak na bazę danych przyjdzie jeszcze czas. Dziś zapoznamy się z operowaniem plikami.
Otwieramy i zamykamy plik
Przed zapisem bądź odczytem czegokolwiek z pliku, plik należy otworzyć. Służy do tego funkcja {stala}fopen{/stala}. Przyjmuje ona cztery parametry, z czego wymagane są jedynie dwa pierwsze i nimi się tu zajmiemy:
- nazwa pliku – pełna ścieżka dostępu do pliku wraz z jego nazwą,
- tryb otwarcia pliku – patrz tabela poniżej.
Tryb | Opis |
---|---|
r | Otwiera plik w trybie tylko do odczytu, ustawiając wskaźnik na jego początku (dzięki czemu możemy czytać go od pierwszego znaku). |
r+ | Otwiera plik w trybie do odczytu i zapisu, ustawiając wskaźnik na jego początku (dzięki czemu możemy czytać go od pierwszego znaku oraz nadpisywać starą zawartość). |
w | Otwiera plik tylko do zapisu, ustawiając wskaźnik na początku pliku i obcinając go do zerowej długości. Jeśli plik nie istnieje, próbuje go utworzyć. |
w+ | Otwiera plik do zapisu i odczytu, ustawiając wskaźnik na początku pliku i obcinając go do zerowej długości. Jeśli plik nie istnieje, próbuje go utworzyć. |
a | Otwiera plik tylko do zapisu. Umieszcza wskaźnik pliku na jego końcu. Jeśli plik nie istnieje, próbuje go utworzyć. |
a+ | Otwiera plik do odczytu i zapisu. Umieszcza wskaźnik pliku na jego końcu. Jeśli plik nie istnieje, próbuje go utworzyć. |
x | Tworzy i otwiera plik tylko do zapisu. Umieszcza wskaźnik na jego początku. Jeśli plik istnieje, funkcja fopen zwróci false, a PHP wygeneruje ostrzeżenie. Jeśli plik nie istnieje, nastąpi próba jego stworzenia. |
x+ | Tworzy i otwiera plik do odczytu i zapisu. Umieszcza wskaźnik na jego początku. Jeśli plik istnieje, funkcja fopen zwróci false, a PHP wygeneruje ostrzeżenie. Jeśli plik nie istnieje, nastąpi próba jego stworzenia. |
Funkcja {stala}fopen{/stala} zwraca uchwyt otwartego pliku (jest to identyfikator, dzięki któremu PHP zawsze wie, o który z otwartych plików nam chodzi gdy próbujemy dokonywać operacji – bo można mieć wiele jednocześnie otwartych plików), lub false, jeśli otwarcie się nie powiedzie. Plik do odczytu można więc otworzyć w następujący sposób:
$uchwyt = fopen(\'/scieżka/do/pliku/plik.txt\', \'r\');
Ważne jest, aby przechowywać w zmiennej (tu {stala}$uchwyt{/stala}) uchwyt do otwartego pliku, gdyż jest on wykorzystywany przez inne funkcje pozwalające na zapis, odczyt czy zamknięcie pliku.
Po otwarciu pliku, należy sprawdzić czy operacja się powiodła:
$uchwyt = fopen(\'/scieżka/do/pliku/plik.txt\', \'r\');
if ($uchwyt === false) {
exit (\"Błąd podczas próby otwarcia pliku!\"); //zakończenie wykonywania skryptu
}
Gdy na otwartym pliku wykonamy już odpowiednie operacje, musimy pamiętać o tym aby go zamknąć. Służy do tego funkcja {stala}fclose{/stala}, przyjmująca jeden parametr – uchwyt otwartego pliku:
fclose($uchwyt);
Zwraca ona wartość true, gdy zamkniecie pliku powiedzie się, lub false w przeciwnym razie (patrz rys. 1).
Podstawowe operacje na plikach
Gdy mamy już otwarty plik, możemy wykonywać na nim rożne operacje, w zależności od tego z jakimi prawami plik ten został otwarty. Do odczytu danych z pliku można użyć funkcji {stala}fread{/stala}. Przyjmuje ona dwa parametry: uchwyt otwartego pliku oraz ilość znaków do odczytu. Po zakończonym odczycie, wskaźnik pliku zostanie przesunięty o odpowiednią liczbę znaków, co spowoduje, że następne użycie funkcji {stala}fread{/stala} odczyta kolejną porcję danych z pliku. Funkcja ta zwraca tekst pobrany z pliku.
Dla testów załóżmy, że posiadamy plik tekstowy o następującej treści: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas velit… (itd.) Tego rodzaju teksty, które można sobie wygenerować na stronie http://www.lipsum.com, są używane do testowego wypełniania stron, testowania wyglądu czcionek itp. My użyjemy go do testowania funkcji {stala}fread{/stala}. Oto przykładowy skrypt, który odczytuje plik tekstowy w porcjach po 7 znaków i wyświetla jego zawartość w przeglądarce.
Oczywiście odczytywaną treść można przechowywać w zmiennej, w tym celu wystarczy zmodyfikować pętlę:
$text = \'\';
while (!feof($uchwyt)) {
$text .= fread($uchwyt, 7); // pamiętaj - to to sam co $text = $text.fread($uchwyt, 7);
}
echo $text;
Wyjaśnienia wymaga tutaj funkcja {stala}feof{/stala}. Sprawdza ona czy wskaźnik pliku, którego identyfikator został podany jako jej parametr, nie osiągnął końca. Jeśli tak, funkcja {stala}feof{/stala} zwraca true, w przeciwnym razie zwraca false. Dzięki temu pętla {stala}while{/stala} wykonuje się tak długo, aż {stala}fread{/stala} nie odczyta całego pliku – zob. rys. 2.
Często dane do odczytu mają określony format. Załóżmy, że w pliku uzytkownicy.txt przechowujemy dane o użytkownikach w postaci: {stala}imie nazwisko login hasło{/stala}. Czyli poszczególne wartości oddzielone są pojedynczymi spacjami, a informacja o każdym z użytkowników znajduje się w nowej linii pliku. Wówczas z pomocą w odczytaniu takiego pliku przychodzi nam funkcja {stala}scanf{/stala}. Przyjmuje ona dwa parametry (jest jeszcze opcjonalny trzeci, który nie będzie nas teraz interesował): uchwyt otwartego pliku oraz opis formatu danych do odczytu. Format danych opisany jest za pomocą tekstu zawierającego następujące znaki specjalne, poprzedzone znakiem procenta (%):
- % – oznacza ze chodzi nam o znak procenta, a więc znak specjalny ma postać %%,
- c – pobierany jest znak ASCII,
- d – pobierana jest cyfra całkowita ze znakiem,
- e – pobierana jest cyfra w postaci notacji naukowej (np. 1.2e+2),
- u – pobierana jest cyfra w postaci całkowitej bez znaku,
- f – pobierany jest ułamek,
- o – pobierana jest cyfra w postaci ósemkowej,
- s – pobierany jest ciąg znaków,
- x – pobierana jest cyfra w postaci szesnastkowej z małymi literami,
- X – pobierana jest cyfra w postaci szesnastkowej z dużymi literami.
W naszym przypadku funkcja {stala}scanf{/stala} przyjmie następujący opis formatu – \”%s %s %s %s\” – gdyż interesują nas cztery ciągi znaków oddzielone spacją. Funkcja ta zwraca tablicę zawierającą pobrane informacje:
while (!feof($uchwyt)) {
$dane = scanf($uchwyt, \"%s %s %s %s\");
echo \"Imie: {$dane[0]}, Nazwisko: {$dane[1]}, Login: {$dane[2]}\";
}
W przypadku gdy chcemy zapiąć dane w pliku, możemy skorzystać z funkcji {stala}fwrite{/stala}. Funkcja ta przyjmuje jako pierwszy parametr uchwyt otwartego pliku, a jako drugi treść do zapisania w nim. Aby wykonać zapis do pliku, musi on zostać otwarty w odpowiednim trybie oraz musi mieć odpowiednie prawa:
Funkcja {stala}fwrite{/stala} po dokonaniu zapisu zwraca liczbę zapisanych bajtów lub wartość false, jeśli zapis się nie powiódł.
Problemy z dostępem do pliku
Często się zdarza, że podczas testowania skryptu, zwłaszcza w Windows, wszystko działa poprawnie i skrypt nie ma żadnych problemów z zapisem plików. Po umieszczeniu skryptu na serwerze okazuje się jednak, że nie ma on odpowiednich uprawnień, aby mógł dokonać zapisu. Powodem są prawa dostępu do plików. Zanim więc uruchomimy skrypt na serwerze, zadbajmy o to, aby skrypt miał prawo odczytu oraz zapisu do danego pliku.
Jeśli chcemy tworzyć nowe pliki za pomocą skryptów PHP, musimy dodatkowo nadać podobne prawa katalogowi, w którym te pliki będziemy umieszczali (w systemach uniksowych katalog musi dodatkowo mieć prawo wykonania, inaczej nie będzie można się do niego dostać).
Inne funkcje operujące na plikach
PHP oferuje bardzo wiele funkcji pozwalających na dokonywanie operacji na plikach. Powyżej wymienione zostały te najbardziej podstawowe. Poza nimi pomocne mogą się okazać także {stala}unlink{/stala}, {stala}ftell{/stala} oraz {stala}fseek{/stala}:
- unlink() – jest to funkcja kasująca plik. Przyjmuje ona w parametrze nazwę pliku do usunięcia.
- ftell() – przyjmuje w parametrze uchwyt otwartego pliku, a w wyniku zwraca aktualną pozycję wskaźnika pliku. Nie działa dla trybów otwarcia a oraz a+.
- fseek() – przyjmuje jako pierwszy parametr uchwyt otwartego pliku, jako drugi offset o jaki ma zostać przesunięty wskaźnik pliku, oraz opcjonalny trzeci parametr, przyjmujący jedną z następujących wartości:
- SEEK_SET – ustawia pozycje wskaźnika na pozycji wskazanej przez drugi parametr (jest to wartość domyślna – to ona jest ustawiana w przypadku pominięcia trzeciego parametru podczas wywołania funkcji fseek),
- SEEK_CUR – ustawia wskaźnik o przesuniecie podane w drugim parametrze w stosunku do aktualnej pozycji wskaźnika pliku,
- SEEK_END – ustawia wskaźnik pliku o przesuniecie znaków za końcem pliku.
A oto przykład użycia {stala}fseek{/stala} do odczytu ostatnio zapisanej informacji o użytkownikach:
Często przydatna okazuje się także funkcja {stala}file{/stala}, odczytująca zawartość pliku (którego nazwę podaje się w jej parametrze wywołania) i umieszczająca poszczególne jego linie w komórkach tablicy:
$tablica = file(\'uzytkownicy.txt\');
W wyniku jej działania otrzymujemy tablicę podobną do:
$tablica = array (
\"Jan Kowalski jank haslo\",
\"Anna Kowalska annak haslo\"
);
Jeśli natomiast chcemy odczytać całą zawartość pliku do zmiennej jako napis, z pomocą przyjdzie funkcja {stala}file_get_contents{/stala}. Tak jak {stala}file(){/stala} przyjmuje ona w parametrze nazwę pliku, ale zwraca nie tablicę, lecz napis będący zawartością tego pliku:
$napis = file_get_content (\'uzytkownicy.txt\');
Obie te funkcje w przypadku niepowodzenia zwracają wartość false.
Funkcja {stala}file_get_contents{/stala} ma swoją odwrotność zapisującą napis w pliku o nazwie podanej w parametrze wywołania – {stala}file_put_contents{/stala}. Przyjmuje ona w pierwszym parametrze nazwę pliku, do którego ma być zapisana treść zawartą w drugim parametrze:
file_put_contents(\'uzytkownicy.txt\', \"Jan Malinowski janm haslo\n\");
Prosty system newsów
Nabyta wiedza jest już wystarczająca do zbudowania prostego systemu newsów opartego na plikach. Do dodawania newsów będzie służył formularz złożony z dwóch pól: tytuł newsa oraz jego treść. Oto kod strony dodaj.html:
Dodawanie newsa
Jak widać, zawartość formularza zostanie wysłana do skryptu dodaj.php. Oto on:
Dodano newsa:
Tytuł
Treść
I pozostaje wyświetlić newsy na stronie:
Newsy
Niektóre elementy skryptów wymagają wyjaśnienia bądź przypomnienia.
{stala}$_POST{/stala} jest tablicą asocjacyjną, w której zapisywane są wartości przysłane do skryptu metoda post – w naszym przypadku będą to pola opisujące nowego newsa, mające nazwę {stala}tytuł{/stala} oraz {stala}treść{/stala}.
Dość złożone są linijki:
$tytul = htmlentities($_POST [\'tytul\']);
$tresc = str_replace(\"\n\", \'\', nl2br(htmlentities($_POST [\'tresc\'])));
Funkcja {stala}htmlentities{/stala} powoduje zastąpienie niedozwolonych znaków HTML-a na odpowiednie wartości, tzw. entities. Np. znak < zamienia na {html}<{/html} znak > na {html}>{/html} itd. Dzięki temu po dodaniu tekstu zawierającego takie znaki będą one widoczne na wygenerowanej stronie.
Treść, poza przepuszczeniem przez funkcję {stala}htmlentities{/stala}, została także poddana działaniu funkcji {stala}nl2br{/stala}, zastępującej znak nowej linii znakiem {html}
{/html}. Niestety funkcja {stala}nl2br{/stala} nie usuwa znaku nowej linii z testu, dodając jedynie przed nim znak {html}
{/html}. Nam jednak zależy na wyeliminowaniu znaku nowej linii (\”\\n\”), tak aby cała treść newsa mieściła się w pojedynczej linii w pliku. Dlatego właśnie musimy posłużyć się funkcją {stala}str_replace{/stala}, która wyszukuje znaki nowej linii (\”\\n\”) i zastępuje je pustymi napisami (\’\’).
Na koniec
W artykule tym nie opisałem wszystkich oferowanych przez PHP funkcji pozwalających na operowanie plikami. Jednak te, które poznaliśmy, wystarczą do pisania nawet złożonych skryptów. Zastanówmy się na przykład, jak można ulepszyć nasz system newsów, tak aby wyeliminować jego wady, aby zrobić system newsów, który:
- pozwoli na wyświetlenie newsów partiami np. po 10 z przyciskiem \”dalej\” do wyświetlenia kolejnej partii (parametry podawane w adresie odczytuje się z tablicy {stala}$_GET{/stala}, np. dla adresu {stala}http://localhost/index.php?foo=bar{/stala}, istnieje {stala}$_GET[\’foo\’]{/stala} z zawartością \’bar\’),
- pozwoli na usuwanie (i jeśli się uda – edycję) newsów,
- pozwoli na komentowanie newsów przez użytkowników odwiedzających stronę.