Spis treści
Wszystkie funkcje obsługi plików (oprócz tej otwierającej plik) jako parametr pobierają tzw. „wskaźnik do pliku” (ang. „file pointer”). Jest to wartość zmiennej określająca otwarty plik. Jest to niezbędne, ponieważ skrypt może otworzyć jednocześnie wiele plików i na wszystkich jednocześnie pracować.
Wskaźnik to pliku jest zwracany przez funkcję fopen( nazwa pliku, string tryb )
. Drugi parametr określa tryb otwarcia pliku. Tryb trzeba dobrać odpowiednio do tego, co się chce z plikiem robić. Możliwe tryby to:
plik tylko do odczytu; wewnętrzny wskaźnik pliku umieszczany jest na początku pliku
plik do odczytu i zapisu; wewnętrzny wskaźnik pliku umieszczany jest na początku pliku
plik tylko do zapisu; wewnętrzny wskaźnik pliku umieszczany jest na końcu pliku; zawartość pliku jest niszczona (długość pliku jest zmieniana na zero); jeśli plik nie istnieje PHP próbuje go stworzyć
plik do odczytu i do zapisu; wewnętrzny wskaźnik pliku umieszczany jest na końcu pliku; zawartość pliku jest niszczona (długość pliku jest zmieniana na zero); jeśli plik nie istnieje
plik tylko do zapisu; wewnętrzny wskaźnik pliku umieszczany jest na końcu pliku; jeśli plik nie istnieje PHP próbuje go stworzyć
Pozycja „wewnętrznego wskaźnika pliku” określa, w którym miejscu zaczęłoby się pisać do pliku, gdyby zaczęło się pisać lub czytać z tego pliku zaraz po otwarciu pliku. Jeśli jest na końcu – to znaczy że dane dopisywane są na końcu.
Otwarcie pliku do odczytu
<?php $fp = fopen("dane.txt", "r"); ?>
Zmienna $fp
zawiera teraz wskaźnik do pliku dane.txt
. Wskaźnik ten trzeba podać przy każdej funkcji, która w jakiś sposób operuje na tym pliku.
Zamykanie pliku
Jeśli otworzyliśmy plik, to teraz trzeba go jakoś zamknąć.
Jeśli nie zamkniemy pliku ręcznie, to PHP zrobi to za nas po zakończeniu działania skryptu.
Nie sprawia to różnicy jeśli na stronie operujemy tylko jednym plikiem lub np. po zapisaniu danych do pliku już się nim nie interesujemy.
Inaczej sprawa wygląda jeśli po zapisaniu danych do pliku chcemy później go odczytać – dopiero po zamknięciu pliku zmiany w nim będą widoczne.
Do zamknięcia pliku służy funkcja fclose(int wskaźnik)
.
Odczyt danych z pliku "linia po linii"
Odczyt z pliku "linia po linii" odbywa się z zastosowaniem funkcji sprawdzającej osiągnięcie końca pliku feof($fp)
.
<?php $fp = fopen("tekst.txt", "r"); while(!feof($fp)) { $tekst = fgets($fp); echo "$tekst"; } ?>
Funkcją fopen
otwarty został plik do odczytu.
Następnie odczytywane są wiersze pliku (znacznik końca linii \n) do momentu osiągnięcia końca pliku.
Każdy wiersz podstawiany jest do zmiennej $tekst
.
Wczytywanie całości pliku
Cały plik można wczytać stosując funkcję file($nazwa_pliku)
zwracającą tablicę, w której każdy element jest osobnym wierszem z pliku,
którego nazwa jest podana w parametrze.
Samo wczytanie pliku do tablicy może być przydatne.
Traktując plik jako bazę danych, w której każdy wiersz to jeden rekord, wczytanie takiego pliku od razu do tablicy
oszczędza wiele pracy przy ręcznym rozbijaniu pliku na wiersze.
Dane do pliku można zapisać przy pomocy funkcji fputs( int wskaznik, string napis, int długość)
.
Jak przy większości funkcji operujących na plikach, niezbędne jest podanie wskaźnika pliku na którym chcemy operować.
Zapisać można albo całą zawartość zmiennej podanej jako drugi parametr, albo tylko do pewnej długości, którą to nalezy podać jako trzeci,
opcjonalny parametr (oczywiście przy pominięciu tego parametru zapisywana jest cała zmienna podana w drugim parametrze).
Zapis odbywa się w miejscu, na który wskazuje wewnętrzny wskaźnik pliku, nadpisując dane jeśli wskaźnik ten nie znajduje się na końcu pliku.
Nie ma żadnej możliwości bezpośredniego zapisania danych na początku lub w środku pliku.
Jedyna możliwość to wczytanie pliku do tymczasowej zmiennej, poprawienie tych danych i ponowny zapis tego pliku.
Przykład - "dopisanie" danych na początku pliku
<?php // wczytanie starych danych // otwarcie pliku do odczytu $fp = fopen("plik.txt", "r"); //odczytanie danych $stareDane = fread($fp, filesize("plik.txt")); // zamknięcie pliku fclose($fp) // stworzenie nowych danych $noweDane = "To, co chcesz, żeby było na początku\n"; $noweDane .= $stareDane; // zapisanie nowych danych // otwarcie pliku do zapisu $fp = fopen("plik.txt", "w"); // zapisanie danych fputs($fp, $noweDane); // zamknięcie pliku fclose($fp); ?>
Zapis na koniec pliku jest łatwy – wystarczy otworzyć plik w trybie „a” i od razu można dodować dane do pliku.
<?php $fp = fopen("plik.txt", "a"); // zapisanie danych fputs($fp, $Dane); // zamknięcie pliku fclose($fp); ?>
Blokowanie plików jest jednym z ważniejszych zagadanień przy używaniu plików do przechowywania danych.
W zastosowaniach internetowych może dojść do takiej sytuacji, że dwie lub więcej osób jednocześnie wejdzie na stronę czy po prostu uruchomi skrypt.
Jeśli będzie to na przykład licznik odwiedzin przechowujący ilość odwiedzin w pliku, to te kilka osób będzie chciało zapisać dane do tego pliku jednocześnie. Może to doprowadzić do utraty danych z tego licznika.
Wystarczy jednak zastosować mechanizm blokad aby zapobiec takiej sytuacji.
Funkcja flock( int wskaźnik, int operacja )
zakłada blokadę lub ją zdejmuje, zależnie od wartości drugiego parametru.
Są 2 typy blokad: blokada dzielona, używana jeśli plik ma być odczytywany (dzielona, ponieważ więcej niż jeden skrypt może utrzymywać taką blokade
na pliku), i blokada wyłączna, zakładana jeśli plik ma być zapisywany.
Drugi parametr funkcji może mieć takie wartości:
LOCK_SH
aby założyć blokadę dzieloną
LOCK_EX
aby założyć blokadę wyłączną
LOCK_UN
aby zdjąć blokadę, wszystko jedno jaką
Teraz wiadomo jak założyć i zdjąć blokadę. Ale jak wykorzystać to w praktyce? Otóż funkcja flock() zwraca wartość true lub false w zależności od tego, czy udało się założyć blokadę czy nie. Przykładowo, jeśli na pliku założona jest blokada dzielona, to można ten plik jeszcze raz zablokować do odczytu, ale do zapisu już nie. Jeśli natomiast założona jest blokada wyłączna, to żaden inny skrypt nie może już założyć blokady do czasu aż ta blokada zostanie zdjęta.
Zależnie od zastosowania, można różnie reagować na niemożność założenia blokady. Można albo przerwać skrypt albo czekać na zdjęcie blokady. W przypadku licznika jedne odwiedziny w tą czy tamtą nie mają znaczenia w obliczu konieczności czekania na zakończenie działania innego skryptu – pewnie będą to ułamki sekund, ale bywa z tym różnie (zwłaszcza, jeśli takich czekających jest więcej). Natomiast jeśli chodzi o blokadę bazy danych, to czekanie na zdjęcie blokady jest koniecznością.
Licznik odwiedzin z blokowaniem pliku
<?php // Konfiguracja // Nazwa pliku zawierającego licznik $df = "count.dat"; // Próba otwarcia pliku do odczytu if(!($fp=@fopen($df, "r"))){ // Jeśli plik jeszcze nie istnieje, to jest inicjowany wartością 0 $count=0; } else { // jeśli istnieje, to jego wartość jest odczytywana a plik zamykany $count = fgets($fp, 100); fclose($fp); } // Zwiększenie licznika o 1 $count+=1; // Otwarcie pliku do zapisu $fp = fopen($df, "w"); // Blokada - jeśli plik już jest zablokowany, to skrypt go zamyka i kończy działanie if(!flock($fp, LOCK_EX)){ fclose($fp); return; } else { // Jeśli nie jest zablokowany, to następuje blokada i zapis danych fputs($fp, $count); flock($fp, LOCK_UN); fclose($fp); } // Wyświetlenie stanu licznika echo $count; ?>
Ten plik możemy zapisać np. jako count.php i w odpowiednim miejscu na stronie, gdzie ma się pojawić licznik, należy wpisać
<?include "count.php"?>
.
Jeśli pojawią się błedy w stylu „Permission denied” oznacza to, że coś jest nie tak z prawami dostępu do pliku z danymi,
a jeśli jeszcze on nie istnieje, to z prawami do katalogu gdzie ma być ten licznik.
Serwer WWW powinien mieć prawa zapisu do tego pliku/katalogu.
Przy skryptach przeznaczonych do obsługi serwera czy na przykład analizujących system plików niezbędne jest uzyskanie informacji o konkretnym pliku. PHP oferuje cały zestaw funkcji zwracających dane o pliku o podanej ścieżce. Chyba najlepiej je wypunktować.
Funkcje informacyjne
filesize($nazwa_pliku)
zwraca wielkość pliku w bajtach
Funkcje logiczne (zwracają wartość true lub false)
is_dir($nazwa_pliku)
czy plik o podanej ścieżce jest katalogiem
is_file($nazwa_pliku)
czy plik jest normalnym plikiem
Do kopiowania plików służe funkcja o jakże zaskakującej nazwie copy()
.
Pierwszy parametr to plik źródłowy a drugi to plik lub katalog docelowy.
Funkcja ta zwraca wartość true
jeśli kopiowanie się powiodło lub false
w przeciwnym przypadku.
Dobrze jest sprawdzać czy kopiowanie się powiodło.
Sprawdzanie powodzenia kopiowania
<?php copy($source, $destination) or die("Błąd przy kopiowaniu"); ?>
Funkcja służaca do przenoszenia i zmiany nazwy pliku sugeruje tylko tą drugą funkcję, jednak bardzo dobrze spisuje się w obu zastosowaniach. Funkcja rename()
pobiera 2 argumenty: nazwę pliku źródłowego oraz nazwę pliku docelowego jeśli funkcja ma zmienić nazwę, nazwę katalogu jeśli plik ma być przeniesiony, lub ścieżkę wraz z nową nazwą, jeśli plik ma być przeniesiony ze zmianą nazwy.
Sposoby użycia funkcji rename
<?php rename("aaa", "bbb"); // zmiana nazwy pliku "aaa" na "bbb" rename("bbb", "test/");// przeniesienie pliku "bbb" do katalogu "test" rename("aaa", ".."); // przeniesienie pliku "aaa" do katalogu nadrzędnego rename("aaa", "test/bbb"); // przeniesienie pliku "aaa" do katalogu "test" ze zmianą nazwy na "bbb" ?>
Usuwanie plików jest rzeczą sprawiającą najwięcej trudności początkującym programistom PHP.
Dzieje się tak głównie dlatego, że funkcja usuwająca pliki nazywa się unlink($nazwa_pliku)
, co jest wynikiem tego,
że PHP powstało początkowo pod systemy Uniksowe.
Funkcja ta może nie działać pod systemami Windows – tam najoczywistszym rozwiązaniem jest del
z odpowiednim parametrem.
Do usuwania katalogów służy funkcja rmdir($nazwa)
.
UWAGA: - katalog musi być pusty.
Tworzeniem katalogów zajmuje się funkcja mkdir($nazwa_katalogu, $tryb)
.
Drugi parametr określa tryb utworzenia katalogu (prawa dostępu).
Nie ma w zasadzie co tu opisywać – funkcja zwraca wartość true
jeśli katalog został utworzony
lub false
.
Unix i pochodne, jako systemu przeznaczone dla wielu użytkowników, zawierają system zabezpieczania dostępu do plików przez niepowołane osoby. System ten opiera się o przydzielanie praw poszczególnym osobom (a konkretniej ich kontom systemowym) oraz grupom, do których użytkownicy należą.
Każdemu plikowi i katalogowi w systemie można przypisać 3 komplety praw. Każdy z tych kompletów to: prawo wykonywania (litera 'x’ lub liczba 1) – w przypadku katalogów oznacza to możliwość 'wejścia’ w niego, prawo zapisu (litera 'w’ lub liczba 2) i prawo odczytu (litera 'r’ lub liczba 4).
Pierwszy komplet tych praw dotyczy właściciela pliku, drugi grupy a trzeci użytkowników, którzy nie są właścicielami ani nie należą do grupy. Liczby przypisane konkretnym prawom służą do zapisu ósemkowego. Zapis taki to 3 cyfry, z których każda odpowiada kompletowi praw – sumie liczb oznaczających prawa. Przyładowo, liczba 7 to komplet praw wykonywania, zapisu i odczytu (1+2+4=7) a liczba 5 to prawo odczytu i wykonywania (1+4=5). Czyli aby nadać właścicielowi komplet praw, grupie odczyt i zapis a reszcie tylko odczyt, należy ustawić prawa „0764” (cyfra zero na początu służy do poinformowania PHP, że liczba zapisana jest w formacie ósemkowym).
Do ustawiania praw dostępu służy funkcja chmod($nazwa_pliku, $tryb)
, gdzie drugi parametr to prawa dostępu zapisane w formacie ósemkowym. Inne funkcje przydatne przy pracy z systemem zabezpieczeń to chown($nazwa_pliku, $user)
, zmieniająca właściciela pliku, i chgrp($nazwa_pliku, $grupa)
, zmieniająca grupę.
Ustawienie odpowiednich praw dostępu to sprawa bardzo ważna w przypadku skryptów zapisujących coś do plików. Błąd „Permission denied” zdarza się bardzo często i można go rozwiązać właśnie przez ustawienie odpowiednich praw. Aby skrypt mógł coś zapisać w pliku, niezbędne jest ustawienie prawa zapisu do tego pliku. PHP zazwyczaj pracuje jako użytkownik nobody lub hcodep – można to sprawdzić wywołując funkcję phpinfo()
.
PHP posiada wiele funkcji do analizy i przetwarzania zmiennych tekstowych, które zawierają ścieżkę do pliku.
Funkcja basename($path)
zwraca nazwę pliku a dirname($path)
wszystko oprócz nazwy pliku.
Z kolei funkcja pathinfo($path)
zwraca tablicę asocjacyjną zawierającą informację o ścieżce podanej jako parametr
– klucz „dirname” zawiera nazwę katalogu, „basename” nazwę pliku a „extension” – rozszerzenie pliku.
Przetwarzanie ścieżki
<?php $path = "/home/hcodepd/html/index.html"; echo dirname($path); // wyświetli "/home/hcodepd/html" echo basename($path); // wyświetli "index.html" $arr = pathinfo($path); echo $arr["dirname"]; // wyświetli "/home/hcodepd/html" echo $arr["basename"]; // wyświetli "index.html" echo $arr["extension"]; // wyświetli "html" ?>
Istnieją 2 mechanizmy przeglądania zawartości katalogów:
Pierwszy to mechanizm oparty na dwóch funkcjach: opendir($katalog)
i readdir($handle)
.
Funkcja opendir($katalog) otwiera katalog o podanej nazwie i zwraca uchwyt do niego.
Z kolei funkcja readdir($handle) za każdym wywołaniem zwraca nazwę kolejnego pliku/katalogu z otwartego katalogu.
Wyświetlenie wszystkich nazw plików z katalogu /tmp
<?php if ($dir = @opendir("/tmp")) { while($file = readdir($dir)) { echo "$file\n"; } closedir($dir); } ?>
Drugi oparty jest na mechanizmie pseudo-obiektowym.
Odczytywanie zawartości katalogu przy uzyciu funcji dir
<?php $d = dir("/etc"); echo "Handle: ".$d->handle."<br>\n"; echo "Path: ".$d->path."<br>\n"; while($entry=$d->read()) { echo $entry."<br>\n"; } $d->close(); ?>
Kilka słów omówienia. Obiekt zwraceny przez funkcję dir($katalog)
posiada 3 metody: metoda handle() zwraca zwykły uchwyt, który może być używany w funkcji readdir(), metoda path() zwraca string zawierający ścieżkę do katalogu a metoda read() zwraca kolejne nazwy plików/katalogów, podobnie jak funkcja readdir().
Dla funkcji readdir() i metody read() nie ma różnicy między plikiem lub katalogiem – zwracane są wszystkie nazwy z katalogu.
A czy to jest katalog programista musi sam sprawdzić za pomocą funkcji is_dir().